liquid_xlsx 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cdc976c8086895374758eae13fd1006379ffd15f37e3cad03cff012be6986065
4
- data.tar.gz: de8e167af7057eb7dfad00991610a3171a30a13b67ed2eda1fa43b1f00bd9358
3
+ metadata.gz: e4a2f29e44a564a50870b6f4c7d066dbb2d648a5ce1031a31115c3087f968710
4
+ data.tar.gz: d1c8cbddb4f24c831cd7ada7cf5089814b615a72fa1f788828cddefce67a3dcf
5
5
  SHA512:
6
- metadata.gz: c36433b43abf4b17f5a0fdbdcab009f6fb2a2d6d18c5c1c517c99a02f8a0207be1b3d3c2ba37187f57d96e1279083450666a3036d9281e5e7f9c3fa0f734ab34
7
- data.tar.gz: 7c8f204d3284aba334ed3f942b0ee34ac3dde5953ad413d8a9847c5d6b588f1aaf754a82f0ada0cf15af0958b15bb3330f4ad49c917cb20644da4cbc3ff75b1b
6
+ metadata.gz: 503a440465922dbf924b017ecbd7f7a532ac695a861cffe9cf64fc3ae97603e73a961f645eb20737ac95f93901b62970222d24e20cfc004dc6ec18eeed2ea97e
7
+ data.tar.gz: 6f4f7399389a693dcd85af3fe4380e706783db5c4034133a4182a1445de4d3979ac535533e36b422aeeb720168cbe6dc0b85768be54a7557459037471c8c8ab5
data/CHANGELOG.md CHANGED
@@ -1,5 +1,23 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0 (unreleased)
4
+
5
+ ### Fixed
6
+
7
+ - Empty rows omitted from `sheetData` no longer collapse. Spreadsheet
8
+ applications skip blank rows when writing a sheet, so row numbers arrive with
9
+ gaps; the renderer used to renumber output rows contiguously and silently
10
+ dropped that vertical space. Templates that use blank rows as spacers — print
11
+ forms in particular — drifted upwards, and multi-page documents repaginated.
12
+ The gaps are now materialized as empty placeholder rows.
13
+
14
+ ### Added
15
+
16
+ - `filters:` render option — pass a Liquid filter module (or an array of them)
17
+ to make host-application filters available in every cell. The modules are
18
+ added to the render context only, so nothing is registered on
19
+ `Liquid::Environment` and the host's global Liquid config stays untouched.
20
+
3
21
  ## 0.1.0 (2026-08-04)
4
22
 
5
23
  First public release.
data/README.md CHANGED
@@ -689,7 +689,8 @@ LiquidXlsx.render(
689
689
  hide_control_sheets: true, # скрывать контрольные листы
690
690
  hide_template_sheets: false, # скрывать листы-шаблоны после клонирования
691
691
  images: nil, # { loader:, default_dpi: } для {% image_tag %}
692
- liquid_resource_limits: nil # { render_length_limit:, render_score_limit:, ... }
692
+ liquid_resource_limits: nil, # { render_length_limit:, render_score_limit:, ... }
693
+ filters: nil # модуль (или массив модулей) со своими Liquid-фильтрами
693
694
  )
694
695
  ```
695
696
 
@@ -703,6 +704,28 @@ LiquidXlsx.render(
703
704
  | `hide_template_sheets` | `false` | Скрывать листы-шаблоны |
704
705
  | `images` | `nil` | `{ loader:, default_dpi: }` — загрузчик путей и DPI для `width/height` |
705
706
  | `liquid_resource_limits` | `nil` | Лимиты Liquid (`render_length_limit`, `render_score_limit`, `assign_score_limit`) |
707
+ | `filters` | `nil` | Модуль или массив модулей со своими фильтрами — см. [Свои фильтры](#свои-фильтры) |
708
+
709
+ ### Свои фильтры
710
+
711
+ Фильтры приложения подключаются опцией `filters:` и доступны в любой ячейке книги:
712
+
713
+ ```ruby
714
+ module MyFilters
715
+ def money2(value) = format("%.2f", value.to_f)
716
+ end
717
+
718
+ LiquidXlsx.render(template: "invoice.xlsx", output: "out.xlsx",
719
+ data: data, filters: MyFilters) # или [MyFilters, DateFilters]
720
+ ```
721
+
722
+ ```
723
+ {{ invoice.sum | money2 }}
724
+ ```
725
+
726
+ Модули добавляются только в контекст рендера (`Liquid::Context#add_filters`),
727
+ на `Liquid::Environment` ничего не регистрируется — глобальная конфигурация
728
+ Liquid в приложении остаётся нетронутой.
706
729
 
707
730
  ## Обработка ошибок
708
731
 
@@ -97,6 +97,11 @@ module LiquidXlsx
97
97
  end
98
98
  liquid_context.strict_variables = strict_vars
99
99
  liquid_context.strict_filters = strict_filts
100
+ # Host-application filters, if any. Scoped to this context — nothing is
101
+ # registered on Liquid::Environment, so the host's global config is
102
+ # neither required nor mutated.
103
+ extra_filters = @options[:filters]
104
+ liquid_context.add_filters(extra_filters) if extra_filters
100
105
  apply_resource_limits(liquid_context)
101
106
 
102
107
  # Get original merge cells
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module LiquidXlsx
4
- VERSION = "0.1.0"
4
+ VERSION = "0.2.0"
5
5
  end
@@ -56,6 +56,14 @@ module LiquidXlsx
56
56
  end
57
57
 
58
58
  # Parse rows from the worksheet.
59
+ #
60
+ # Spreadsheet applications omit empty rows from sheetData, so row numbers
61
+ # come with gaps. Those gaps are not nothing: an empty row still takes up
62
+ # vertical space on the sheet, and templates use them as spacers. Since the
63
+ # renderer renumbers output rows contiguously, the gaps are materialized
64
+ # here as empty placeholder rows — otherwise every spacer collapses and the
65
+ # whole document creeps upwards.
66
+ #
59
67
  # @return [Array<Hash>] rows with :row_number, :row_xml, :cells, :row_attrs
60
68
  def parse_rows
61
69
  rows = []
@@ -68,6 +76,9 @@ module LiquidXlsx
68
76
  @doc.xpath("//xmlns:sheetData/xmlns:row", "xmlns" => ns.href).each do |row_elem|
69
77
  row_num = row_elem["r"]&.to_i
70
78
  row_num = last_row_num + 1 if row_num.nil? || row_num < 1
79
+
80
+ ((last_row_num + 1)...row_num).each { |missing| rows << placeholder_row(missing) }
81
+
71
82
  last_row_num = row_num
72
83
  cells = parse_cells(row_elem, row_num)
73
84
  rows << {
@@ -80,6 +91,17 @@ module LiquidXlsx
80
91
  rows
81
92
  end
82
93
 
94
+ # An empty row omitted from sheetData. No attributes — the sheet's
95
+ # defaultRowHeight applies, exactly as the spreadsheet application shows it.
96
+ def placeholder_row(row_num)
97
+ {
98
+ row_number: row_num,
99
+ row_xml: nil,
100
+ cells: [],
101
+ row_attrs: {}
102
+ }
103
+ end
104
+
83
105
  # Get merge cells from the worksheet.
84
106
  # @return [Array<Hash>] each: {ref: "A3:B3", start_row:, end_row:}
85
107
  def merge_cells
data/lib/liquid_xlsx.rb CHANGED
@@ -64,13 +64,16 @@ module LiquidXlsx
64
64
  # @param images [Hash] image rendering options: { loader:, default_dpi: }
65
65
  # @param liquid_resource_limits [Hash, nil] optional Liquid resource limits:
66
66
  # { render_length_limit:, render_score_limit:, assign_score_limit: }
67
+ # @param filters [Module, Array<Module>, nil] extra Liquid filter modules made
68
+ # available to every cell of the workbook. Added to the render context only,
69
+ # so nothing is registered globally on Liquid::Environment.
67
70
  # rubocop:disable Metrics/ParameterLists
68
71
  def render(template:, output:, data:,
69
72
  strict_variables: false, strict_filters: false,
70
73
  recalculate_formulas: false, remove_template_comments: false,
71
74
  dynamic_sheets: false, hide_control_sheets: true,
72
75
  hide_template_sheets: false, images: nil,
73
- liquid_resource_limits: nil)
76
+ liquid_resource_limits: nil, filters: nil)
74
77
  tpl = Template.new(template, {
75
78
  strict_variables: strict_variables,
76
79
  strict_filters: strict_filters,
@@ -80,7 +83,8 @@ module LiquidXlsx
80
83
  hide_control_sheets: hide_control_sheets,
81
84
  hide_template_sheets: hide_template_sheets,
82
85
  images: images || {},
83
- liquid_resource_limits: liquid_resource_limits
86
+ liquid_resource_limits: liquid_resource_limits,
87
+ filters: filters
84
88
  })
85
89
  # rubocop:enable Metrics/ParameterLists
86
90
  tpl.render_to_file(data, output)
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: liquid_xlsx
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ivan Khlipitkin