Class: LiquidXlsx::Tags::SheetTag
- Inherits:
-
Liquid::Tag
- Object
- Liquid::Tag
- LiquidXlsx::Tags::SheetTag
- Defined in:
- lib/liquid_xlsx/tags/sheet_tag.rb
Overview
Handles the sheet % Liquid tag for dynamic sheet creation.
Syntax:
{% sheet name: <expr> template: "SheetName" data: <expr> as: "var_name" %}
name: Liquid expression for the new sheet name (e.g. "invoice.number")template: quoted string — name of existing template sheet to clonedata: Liquid expression — object to pass as local variableas: (optional) quoted string — local variable name, defaults to "item"
Constant Summary collapse
- SYNTAX =
/\A\s*(.+)\s*\z/- KNOWN_KEYS =
Known argument keys. Values are split on the positions of these keys (not on whitespace) so that a name/data value may itself contain spaces and Liquid filters, e.g.: name: inv.number | append: " - "
%w[name template data as].freeze
- KEY_PATTERN =
/\b(?:name|template|data|as)\s*:/
Instance Attribute Summary collapse
-
#as_name ⇒ Object
readonly
Exposed for Validator: it has to know which sheet the tag clones, to check that the sheet exists, without rendering the tag.
-
#template_name ⇒ Object
readonly
Exposed for Validator: it has to know which sheet the tag clones, to check that the sheet exists, without rendering the tag.
Instance Method Summary collapse
-
#initialize(tag_name, markup, options) ⇒ SheetTag
constructor
A new instance of SheetTag.
-
#parse_args(markup) ⇒ Object
Split "name: EXPR template: "X" data: EXPR as: VAR" into a hash of { key => { value:, quoted: } }.
- #render(context) ⇒ Object
Constructor Details
#initialize(tag_name, markup, options) ⇒ SheetTag
Returns a new instance of SheetTag.
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 |
# File 'lib/liquid_xlsx/tags/sheet_tag.rb', line 26 def initialize(tag_name, markup, ) super match = markup.match(SYNTAX) unless match raise_template_err( "Invalid sheet tag syntax. Expected: {% sheet name: ... template: ... data: ... %}" ) end @args = parse_args(match[1]) raise_template_err("Missing required argument: name") unless @args["name"] raise_template_err("Missing required argument: template") unless @args["template"] raise_template_err("Missing required argument: data") unless @args["data"] @name_arg = @args["name"] @template_name = @args["template"][:value] @data_arg = @args["data"] # Validate that template and as are literals raise_template_err("template must be a quoted string literal") unless @args["template"][:quoted] raise_template_err("as must be a valid identifier") \ if @args["as"] && !@args["as"][:value].match?(/\A[A-Za-z_]\w*\z/) @as_name = @args["as"] ? @args["as"][:value] : "item" end |
Instance Attribute Details
#as_name ⇒ Object (readonly)
Exposed for Validator: it has to know which sheet the tag clones, to check that the sheet exists, without rendering the tag.
17 18 19 |
# File 'lib/liquid_xlsx/tags/sheet_tag.rb', line 17 def as_name @as_name end |
#template_name ⇒ Object (readonly)
Exposed for Validator: it has to know which sheet the tag clones, to check that the sheet exists, without rendering the tag.
17 18 19 |
# File 'lib/liquid_xlsx/tags/sheet_tag.rb', line 17 def template_name @template_name end |
Instance Method Details
#parse_args(markup) ⇒ Object
Split "name: EXPR template: "X" data: EXPR as: VAR" into a hash of { key => { value:, quoted: } }. Values may contain spaces and Liquid filters because we cut the markup at the positions of the known keys.
Keys are matched only OUTSIDE double-quoted strings, so a quoted value
like name: "data: foo" is not mistaken for the data: key.
60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/liquid_xlsx/tags/sheet_tag.rb', line 60 def parse_args(markup) occurrences = [] in_quote = false i = 0 while i < markup.length ch = markup[i] if ch == '"' in_quote = !in_quote i += 1 next end if !in_quote && (m = markup.match(KEY_PATTERN, i)) && m.begin(0) == i key = m[0][/\A\w+/] occurrences << { key: key, key_start: i, val_start: m.end(0) } i = m.end(0) next end i += 1 end occurrences.sort_by! { |o| o[:key_start] } args = {} occurrences.each_with_index do |occ, i| next_start = i + 1 < occurrences.length ? occurrences[i + 1][:key_start] : markup.length raw = markup[occ[:val_start], next_start - occ[:val_start]] raw = raw.strip.sub(/,\s*\z/, "").strip raise_template_err("Empty value for argument: #{occ[:key].inspect}") if raw.empty? raise_template_err("Duplicate argument: #{occ[:key].inspect}") if args.key?(occ[:key]) quoted = raw.start_with?('"') && raw.end_with?('"') && raw.length >= 2 val = quoted ? raw[1..-2] : raw args[occ[:key]] = { value: val, quoted: quoted } end args end |
#render(context) ⇒ Object
98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 |
# File 'lib/liquid_xlsx/tags/sheet_tag.rb', line 98 def render(context) # Raise error if dynamic_sheets is disabled unless context.registers[:liquid_xlsx_dynamic_sheets] raise RenderError.new( "Dynamic sheet creation is disabled. Pass dynamic_sheets: true.", sheet: context.registers[:liquid_xlsx_sheet_name], row: context.registers[:liquid_xlsx_source_row], cell: context.registers[:liquid_xlsx_source_cell], template: "{% sheet ... %}" ) end # Resolve name expression through Liquid context (supports filters) name_val = evaluate_arg(@name_arg, context, expression: true) name_val = name_val.to_s.strip # Resolve data expression through Liquid context (preserves raw object) data_val = evaluate_arg(@data_arg, context) # Build operation with source context for error reporting op = { op: :create_sheet, template_sheet: @template_name, name: name_val, data: data_val, as: @as_name, source_sheet: context.registers[:liquid_xlsx_sheet_name], source_row: context.registers[:liquid_xlsx_source_row], source_cell: context.registers[:liquid_xlsx_source_cell] } # Store operation in registers ops = context.registers[:liquid_xlsx_workbook_ops] ||= [] ops << op "" end |