philiprehberger-csv_builder 0.4.0 → 0.5.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: a303ee02020135b1ce25af2882b415847487b5ee10f5b3f7fc929586150c750d
4
- data.tar.gz: b7cde53364fd15eacca588c5df3ca2056a3abc40d552ba650b7fad1681f804f2
3
+ metadata.gz: 921263332f87142c3cfcdc4483e601a9ab51ce54cd53481988cc75b3b46eb7ed
4
+ data.tar.gz: fa347e0127c702eca2a1c4104ca1b5eada5c43458660884c9b6f9028753a8f80
5
5
  SHA512:
6
- metadata.gz: 98272067c382b0d1a89d4400886e4162913efa72fbc6677dc3445599bcc9bdba85177894b5a89fdc9a28bd44c440279afa8a254917b208d766e3aa703e4bdc6e
7
- data.tar.gz: ee0c236b83966c409af73445c43bb626330c7227964cba73054ac475b2cb3db7218f95ef4167901e9086f64c1af348f4adbc998abfdbbcc770472937fb80fa9f
6
+ metadata.gz: a9fcc0e96415c5b1d5b55e257963c48881ec879809b652ae7604138d524df50264577650e60bb239cf6b838b5fbae2bd03e6b76d5d6cdb0b3225528dd6a5d9a6
7
+ data.tar.gz: c7a65b052c99facd38baee4f16f2cca0bbd36ae5906ce6ca1302da36797c6b1b585066e096b1aec8432dacbd8d1167ab23f9cb522123e4253c3088b8643c14b4
data/CHANGELOG.md CHANGED
@@ -7,6 +7,16 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.5.0] - 2026-04-11
11
+
12
+ ### Added
13
+ - UTF-8 BOM support via `bom: true` option for Excel-compatible output
14
+ - Custom output encoding via `encoding:` option
15
+
16
+ ### Fixed
17
+ - Bug report template now requires Ruby version and gem version fields
18
+ - Feature request template now includes "Alternatives considered" field
19
+
10
20
  ## [0.4.0] - 2026-04-10
11
21
 
12
22
  ### Added
@@ -65,6 +75,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
65
75
  - Support for hash records with symbol and string keys
66
76
  - Proper CSV escaping for values with commas and quotes
67
77
 
78
+ [0.5.0]: https://github.com/philiprehberger/rb-csv-builder/releases/tag/v0.5.0
68
79
  [0.4.0]: https://github.com/philiprehberger/rb-csv-builder/releases/tag/v0.4.0
69
80
  [0.3.0]: https://github.com/philiprehberger/rb-csv-builder/releases/tag/v0.3.0
70
81
  [0.2.0]: https://github.com/philiprehberger/rb-csv-builder/releases/tag/v0.2.0
data/README.md CHANGED
@@ -197,6 +197,27 @@ end
197
197
 
198
198
  `offset` and `limit` are applied after filtering and sorting.
199
199
 
200
+ ### Excel-Compatible Output (BOM)
201
+
202
+ Prepend a UTF-8 BOM so Excel opens the CSV with correct encoding:
203
+
204
+ ```ruby
205
+ builder = Philiprehberger::CsvBuilder.build(records, bom: true) do
206
+ column :name
207
+ column :email
208
+ end
209
+
210
+ builder.to_file('export.csv')
211
+ ```
212
+
213
+ ### Custom Encoding
214
+
215
+ ```ruby
216
+ builder = Philiprehberger::CsvBuilder.build(records, encoding: 'ISO-8859-1') do
217
+ column :name
218
+ end
219
+ ```
220
+
200
221
  ### Streaming
201
222
 
202
223
  ```ruby
@@ -232,7 +253,7 @@ builder.headers # => ["name", "email"]
232
253
 
233
254
  | Method | Description |
234
255
  |--------|-------------|
235
- | `CsvBuilder.build(records, delimiter:, quote_char:, &block)` | Build a CSV using the column DSL |
256
+ | `CsvBuilder.build(records, delimiter:, quote_char:, bom:, encoding:, &block)` | Build a CSV using the column DSL |
236
257
  | `Builder#column(name, header:, &block)` | Define a column with optional alias and transform |
237
258
  | `Builder#filter(&block)` | Filter records (block returns true to include) |
238
259
  | `Builder#sort_by(direction:, &block)` | Sort records by block key (`:asc` or `:desc`) |
@@ -16,7 +16,9 @@ module Philiprehberger
16
16
  # @param records [Array] the source records
17
17
  # @param delimiter [String] the column separator (default: ",")
18
18
  # @param quote_char [String] the quote character (default: '"')
19
- def initialize(records, delimiter: ',', quote_char: '"')
19
+ # @param bom [Boolean] prepend UTF-8 BOM (default: false)
20
+ # @param encoding [String] output encoding name (default: "UTF-8")
21
+ def initialize(records, delimiter: ',', quote_char: '"', bom: false, encoding: 'UTF-8')
20
22
  @records = records
21
23
  @columns = []
22
24
  @filters = []
@@ -28,6 +30,8 @@ module Philiprehberger
28
30
  @limit_count = nil
29
31
  @offset_count = nil
30
32
  @footer_block = nil
33
+ @bom = bom
34
+ @encoding = encoding
31
35
  end
32
36
 
33
37
  # Sort records before CSV output
@@ -39,7 +43,8 @@ module Philiprehberger
39
43
  # @raise [Error] if direction is not :asc or :desc
40
44
  def sort_by(direction: :asc, &block)
41
45
  raise Error, 'A block is required for sort_by' unless block
42
- raise Error, "direction must be :asc or :desc (got #{direction.inspect})" unless %i[asc desc].include?(direction)
46
+ raise Error, "direction must be :asc or :desc (got #{direction.inspect})" unless %i[asc
47
+ desc].include?(direction)
43
48
 
44
49
  @sort_by = block
45
50
  @sort_direction = direction
@@ -135,13 +140,15 @@ module Philiprehberger
135
140
  # @return [String]
136
141
  def to_csv
137
142
  recs = filtered_records
138
- CSV.generate(**csv_options) do |csv|
143
+ csv_string = CSV.generate(**csv_options) do |csv|
139
144
  csv << headers
140
145
  recs.each_with_index do |record, index|
141
146
  csv << build_row(record, index)
142
147
  end
143
148
  csv << @footer_block.call(recs) if @footer_block
144
149
  end
150
+ csv_string = csv_string.encode(@encoding) unless @encoding == 'UTF-8'
151
+ @bom ? "\xEF\xBB\xBF#{csv_string}" : csv_string
145
152
  end
146
153
 
147
154
  # Write the CSV to a file
@@ -149,7 +156,7 @@ module Philiprehberger
149
156
  # @param path [String] the output file path
150
157
  # @return [void]
151
158
  def to_file(path)
152
- File.write(path, to_csv)
159
+ File.binwrite(path, to_csv)
153
160
  end
154
161
 
155
162
  # Stream CSV to any IO object
@@ -157,6 +164,7 @@ module Philiprehberger
157
164
  # @param io [IO, StringIO] the IO object to write to
158
165
  # @return [void]
159
166
  def to_io(io)
167
+ io.write("\xEF\xBB\xBF") if @bom
160
168
  recs = filtered_records
161
169
  csv = CSV.new(io, **csv_options)
162
170
  csv << headers
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Philiprehberger
4
4
  module CsvBuilder
5
- VERSION = '0.4.0'
5
+ VERSION = '0.5.0'
6
6
  end
7
7
  end
@@ -13,14 +13,20 @@ module Philiprehberger
13
13
  # @param records [Array] the source records
14
14
  # @param delimiter [String] the column separator (default: ",")
15
15
  # @param quote_char [String] the quote character (default: '"')
16
+ # @param bom [Boolean] prepend UTF-8 BOM for Excel compatibility (default: false)
17
+ # @param encoding [String] output encoding name (default: "UTF-8")
16
18
  # @yield [builder] the builder instance for defining columns
17
19
  # @yieldparam builder [Builder]
18
20
  # @return [Builder] the configured builder
19
21
  # @raise [Error] if no block is given
20
- def self.build(records, delimiter: ',', quote_char: '"', &block)
22
+ def self.build(records, delimiter: ',', quote_char: '"', bom: false, encoding: 'UTF-8', &block)
21
23
  raise Error, 'A block is required' unless block
22
24
 
23
- builder = Builder.new(records, delimiter: delimiter, quote_char: quote_char)
25
+ builder = Builder.new(
26
+ records,
27
+ delimiter: delimiter, quote_char: quote_char,
28
+ bom: bom, encoding: encoding
29
+ )
24
30
  builder.instance_eval(&block)
25
31
  builder
26
32
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: philiprehberger-csv_builder
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Philip Rehberger
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2026-04-10 00:00:00.000000000 Z
11
+ date: 2026-04-11 00:00:00.000000000 Z
12
12
  dependencies: []
13
13
  description: Build CSV files from record collections using a declarative DSL with
14
14
  column definitions, custom transforms, filtering, sorting, pagination via limit/offset,