philiprehberger-csv_builder 0.1.3 → 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 +4 -4
- data/CHANGELOG.md +20 -0
- data/README.md +128 -4
- data/lib/philiprehberger/csv_builder/builder.rb +74 -7
- data/lib/philiprehberger/csv_builder/column.rb +4 -2
- data/lib/philiprehberger/csv_builder/version.rb +1 -1
- data/lib/philiprehberger/csv_builder.rb +4 -2
- metadata +4 -4
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 2f0d8a20ab069951c75151dcb5d3892a7c86ba7ed94b8ab09a9ef03baf2bf0a2
|
|
4
|
+
data.tar.gz: 7b1efd9f802aa42886292127d3a4417f0aabaf5d5673049819c915cd3b4e5b76
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8f99b2921a5871ac26e0d516f773cf644a310c6f3a72f2b999acb2f18fa4c2f3b049723421bd0247344736e65d7b0c186d9091bafdbde955fea80d3fdaf52b50
|
|
7
|
+
data.tar.gz: 791c95f2680441487f40314c5e265c6b4f7294c2260ec21e67670572f25b817ee958f7cd506794482b30447fc64b069facfc6f47a612b567f3828a4a1fdda1d5
|
data/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,26 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.2.0] - 2026-04-03
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- Custom CSV delimiters via `delimiter:` option
|
|
14
|
+
- Custom quote character via `quote_char:` option
|
|
15
|
+
- Column header aliasing via `header:` option on columns
|
|
16
|
+
- Record filtering via `filter`
|
|
17
|
+
- Auto-incrementing row numbers via `row_number`
|
|
18
|
+
- Streaming output via `to_io`
|
|
19
|
+
|
|
20
|
+
## [0.1.5] - 2026-03-31
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Add GitHub issue templates, dependabot config, and PR template
|
|
24
|
+
|
|
25
|
+
## [0.1.4] - 2026-03-31
|
|
26
|
+
|
|
27
|
+
### Changed
|
|
28
|
+
- Standardize README badges, support section, and license format
|
|
29
|
+
|
|
10
30
|
## [0.1.3] - 2026-03-24
|
|
11
31
|
|
|
12
32
|
### Fixed
|
data/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://github.com/philiprehberger/rb-csv-builder/actions/workflows/ci.yml)
|
|
4
4
|
[](https://rubygems.org/gems/philiprehberger-csv_builder)
|
|
5
|
-
[](https://github.com/philiprehberger/rb-csv-builder/commits/main)
|
|
6
6
|
|
|
7
7
|
Declarative CSV builder with column mapping and streaming output
|
|
8
8
|
|
|
@@ -71,6 +71,109 @@ end
|
|
|
71
71
|
builder.to_file('output.csv')
|
|
72
72
|
```
|
|
73
73
|
|
|
74
|
+
### Custom Delimiters
|
|
75
|
+
|
|
76
|
+
```ruby
|
|
77
|
+
builder = Philiprehberger::CsvBuilder.build(records, delimiter: "\t") do
|
|
78
|
+
column :name
|
|
79
|
+
column :email
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
puts builder.to_csv
|
|
83
|
+
# name email
|
|
84
|
+
# Alice alice@example.com
|
|
85
|
+
# Bob bob@example.com
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
You can also set a custom quote character:
|
|
89
|
+
|
|
90
|
+
```ruby
|
|
91
|
+
builder = Philiprehberger::CsvBuilder.build(records, quote_char: "'") do
|
|
92
|
+
column :name
|
|
93
|
+
column :email
|
|
94
|
+
end
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### Column Aliases
|
|
98
|
+
|
|
99
|
+
```ruby
|
|
100
|
+
builder = Philiprehberger::CsvBuilder.build(records) do
|
|
101
|
+
column :name, header: 'Full Name'
|
|
102
|
+
column :email, header: 'Email Address'
|
|
103
|
+
column(:status, header: 'Active?') { |r| r[:active] ? 'Yes' : 'No' }
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
puts builder.to_csv
|
|
107
|
+
# Full Name,Email Address,Active?
|
|
108
|
+
# Alice,alice@example.com,Yes
|
|
109
|
+
# Bob,bob@example.com,No
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
### Filtering
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
builder = Philiprehberger::CsvBuilder.build(records) do
|
|
116
|
+
column :name
|
|
117
|
+
column :email
|
|
118
|
+
filter { |r| r[:active] }
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
puts builder.to_csv
|
|
122
|
+
# name,email
|
|
123
|
+
# Alice,alice@example.com
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
Multiple filters are combined with AND logic:
|
|
127
|
+
|
|
128
|
+
```ruby
|
|
129
|
+
builder = Philiprehberger::CsvBuilder.build(records) do
|
|
130
|
+
column :name
|
|
131
|
+
filter { |r| r[:active] }
|
|
132
|
+
filter { |r| r[:name].start_with?('A') }
|
|
133
|
+
end
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### Row Numbers
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
builder = Philiprehberger::CsvBuilder.build(records) do
|
|
140
|
+
column :name
|
|
141
|
+
column :email
|
|
142
|
+
row_number
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
puts builder.to_csv
|
|
146
|
+
# #,name,email
|
|
147
|
+
# 1,Alice,alice@example.com
|
|
148
|
+
# 2,Bob,bob@example.com
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
Customize the header label:
|
|
152
|
+
|
|
153
|
+
```ruby
|
|
154
|
+
row_number(header: 'Row')
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Streaming
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
File.open('output.csv', 'w') do |file|
|
|
161
|
+
builder = Philiprehberger::CsvBuilder.build(records) do
|
|
162
|
+
column :name
|
|
163
|
+
column :email
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
builder.to_io(file)
|
|
167
|
+
end
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Works with any IO object, including `StringIO`:
|
|
171
|
+
|
|
172
|
+
```ruby
|
|
173
|
+
io = StringIO.new
|
|
174
|
+
builder.to_io(io)
|
|
175
|
+
```
|
|
176
|
+
|
|
74
177
|
### Headers
|
|
75
178
|
|
|
76
179
|
```ruby
|
|
@@ -86,10 +189,13 @@ builder.headers # => ["name", "email"]
|
|
|
86
189
|
|
|
87
190
|
| Method | Description |
|
|
88
191
|
|--------|-------------|
|
|
89
|
-
| `CsvBuilder.build(records, &block)` | Build a CSV using the column DSL |
|
|
90
|
-
| `Builder#column(name, &block)` | Define a column with optional transform |
|
|
192
|
+
| `CsvBuilder.build(records, delimiter:, quote_char:, &block)` | Build a CSV using the column DSL |
|
|
193
|
+
| `Builder#column(name, header:, &block)` | Define a column with optional alias and transform |
|
|
194
|
+
| `Builder#filter(&block)` | Filter records (block returns true to include) |
|
|
195
|
+
| `Builder#row_number(header:)` | Add auto-incrementing row number column |
|
|
91
196
|
| `Builder#to_csv` | Generate CSV as a string |
|
|
92
197
|
| `Builder#to_file(path)` | Write CSV to a file |
|
|
198
|
+
| `Builder#to_io(io)` | Stream CSV to any IO object |
|
|
93
199
|
| `Builder#headers` | Return column header names |
|
|
94
200
|
|
|
95
201
|
## Development
|
|
@@ -100,6 +206,24 @@ bundle exec rspec
|
|
|
100
206
|
bundle exec rubocop
|
|
101
207
|
```
|
|
102
208
|
|
|
209
|
+
## Support
|
|
210
|
+
|
|
211
|
+
If you find this project useful:
|
|
212
|
+
|
|
213
|
+
⭐ [Star the repo](https://github.com/philiprehberger/rb-csv-builder)
|
|
214
|
+
|
|
215
|
+
🐛 [Report issues](https://github.com/philiprehberger/rb-csv-builder/issues?q=is%3Aissue+is%3Aopen+label%3Abug)
|
|
216
|
+
|
|
217
|
+
💡 [Suggest features](https://github.com/philiprehberger/rb-csv-builder/issues?q=is%3Aissue+is%3Aopen+label%3Aenhancement)
|
|
218
|
+
|
|
219
|
+
❤️ [Sponsor development](https://github.com/sponsors/philiprehberger)
|
|
220
|
+
|
|
221
|
+
🌐 [All Open Source Projects](https://philiprehberger.com/open-source-packages)
|
|
222
|
+
|
|
223
|
+
💻 [GitHub Profile](https://github.com/philiprehberger)
|
|
224
|
+
|
|
225
|
+
🔗 [LinkedIn Profile](https://www.linkedin.com/in/philiprehberger)
|
|
226
|
+
|
|
103
227
|
## License
|
|
104
228
|
|
|
105
|
-
MIT
|
|
229
|
+
[MIT](LICENSE)
|
|
@@ -14,19 +14,45 @@ module Philiprehberger
|
|
|
14
14
|
attr_reader :records
|
|
15
15
|
|
|
16
16
|
# @param records [Array] the source records
|
|
17
|
-
|
|
17
|
+
# @param delimiter [String] the column separator (default: ",")
|
|
18
|
+
# @param quote_char [String] the quote character (default: '"')
|
|
19
|
+
def initialize(records, delimiter: ',', quote_char: '"')
|
|
18
20
|
@records = records
|
|
19
21
|
@columns = []
|
|
22
|
+
@filters = []
|
|
23
|
+
@row_number_header = nil
|
|
24
|
+
@delimiter = delimiter
|
|
25
|
+
@quote_char = quote_char
|
|
20
26
|
end
|
|
21
27
|
|
|
22
28
|
# Define a column
|
|
23
29
|
#
|
|
24
30
|
# @param name [Symbol, String] the column name
|
|
31
|
+
# @param header [String, nil] optional custom header label
|
|
25
32
|
# @yield [record] optional block to transform the value
|
|
26
33
|
# @yieldparam record [Object] the source record
|
|
27
34
|
# @return [self]
|
|
28
|
-
def column(name, &block)
|
|
29
|
-
@columns << Column.new(name, &block)
|
|
35
|
+
def column(name, header: nil, &block)
|
|
36
|
+
@columns << Column.new(name, header: header, &block)
|
|
37
|
+
self
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Add a filter to exclude records
|
|
41
|
+
#
|
|
42
|
+
# @yield [record] block that returns true to include the record
|
|
43
|
+
# @yieldparam record [Object] the source record
|
|
44
|
+
# @return [self]
|
|
45
|
+
def filter(&block)
|
|
46
|
+
@filters << block
|
|
47
|
+
self
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Add an auto-incrementing row number as the first column
|
|
51
|
+
#
|
|
52
|
+
# @param header [String] the header label for the row number column
|
|
53
|
+
# @return [self]
|
|
54
|
+
def row_number(header: '#')
|
|
55
|
+
@row_number_header = header
|
|
30
56
|
self
|
|
31
57
|
end
|
|
32
58
|
|
|
@@ -34,17 +60,29 @@ module Philiprehberger
|
|
|
34
60
|
#
|
|
35
61
|
# @return [Array<String>]
|
|
36
62
|
def headers
|
|
37
|
-
@columns.map(&:header)
|
|
63
|
+
base = @columns.map(&:header)
|
|
64
|
+
@row_number_header ? [@row_number_header] + base : base
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
# Return the filtered records
|
|
68
|
+
#
|
|
69
|
+
# @return [Array]
|
|
70
|
+
def filtered_records
|
|
71
|
+
result = @records
|
|
72
|
+
@filters.each do |f|
|
|
73
|
+
result = result.select(&f)
|
|
74
|
+
end
|
|
75
|
+
result
|
|
38
76
|
end
|
|
39
77
|
|
|
40
78
|
# Generate the CSV as a string
|
|
41
79
|
#
|
|
42
80
|
# @return [String]
|
|
43
81
|
def to_csv
|
|
44
|
-
CSV.generate do |csv|
|
|
82
|
+
CSV.generate(**csv_options) do |csv|
|
|
45
83
|
csv << headers
|
|
46
|
-
|
|
47
|
-
csv <<
|
|
84
|
+
filtered_records.each_with_index do |record, index|
|
|
85
|
+
csv << build_row(record, index)
|
|
48
86
|
end
|
|
49
87
|
end
|
|
50
88
|
end
|
|
@@ -56,6 +94,35 @@ module Philiprehberger
|
|
|
56
94
|
def to_file(path)
|
|
57
95
|
File.write(path, to_csv)
|
|
58
96
|
end
|
|
97
|
+
|
|
98
|
+
# Stream CSV to any IO object
|
|
99
|
+
#
|
|
100
|
+
# @param io [IO, StringIO] the IO object to write to
|
|
101
|
+
# @return [void]
|
|
102
|
+
def to_io(io)
|
|
103
|
+
csv = CSV.new(io, **csv_options)
|
|
104
|
+
csv << headers
|
|
105
|
+
filtered_records.each_with_index do |record, index|
|
|
106
|
+
csv << build_row(record, index)
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
private
|
|
111
|
+
|
|
112
|
+
# @return [Hash] CSV library options
|
|
113
|
+
def csv_options
|
|
114
|
+
{ col_sep: @delimiter, quote_char: @quote_char }
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
# Build a single row array for the given record
|
|
118
|
+
#
|
|
119
|
+
# @param record [Object] the source record
|
|
120
|
+
# @param index [Integer] zero-based row index
|
|
121
|
+
# @return [Array]
|
|
122
|
+
def build_row(record, index)
|
|
123
|
+
row = @columns.map { |col| col.extract(record) }
|
|
124
|
+
@row_number_header ? [index + 1] + row : row
|
|
125
|
+
end
|
|
59
126
|
end
|
|
60
127
|
end
|
|
61
128
|
end
|
|
@@ -11,9 +11,11 @@ module Philiprehberger
|
|
|
11
11
|
attr_reader :transform
|
|
12
12
|
|
|
13
13
|
# @param name [Symbol, String] the column name (also used as the record key)
|
|
14
|
+
# @param header [String, nil] optional custom header label
|
|
14
15
|
# @param transform [Proc, nil] optional block to transform the value
|
|
15
|
-
def initialize(name, &transform)
|
|
16
|
+
def initialize(name, header: nil, &transform)
|
|
16
17
|
@name = name.to_sym
|
|
18
|
+
@custom_header = header
|
|
17
19
|
@transform = block_given? ? transform : nil
|
|
18
20
|
end
|
|
19
21
|
|
|
@@ -37,7 +39,7 @@ module Philiprehberger
|
|
|
37
39
|
#
|
|
38
40
|
# @return [String]
|
|
39
41
|
def header
|
|
40
|
-
@name.to_s
|
|
42
|
+
@custom_header || @name.to_s
|
|
41
43
|
end
|
|
42
44
|
end
|
|
43
45
|
end
|
|
@@ -11,14 +11,16 @@ module Philiprehberger
|
|
|
11
11
|
# Build a CSV from records using a declarative DSL
|
|
12
12
|
#
|
|
13
13
|
# @param records [Array] the source records
|
|
14
|
+
# @param delimiter [String] the column separator (default: ",")
|
|
15
|
+
# @param quote_char [String] the quote character (default: '"')
|
|
14
16
|
# @yield [builder] the builder instance for defining columns
|
|
15
17
|
# @yieldparam builder [Builder]
|
|
16
18
|
# @return [Builder] the configured builder
|
|
17
19
|
# @raise [Error] if no block is given
|
|
18
|
-
def self.build(records, &block)
|
|
20
|
+
def self.build(records, delimiter: ',', quote_char: '"', &block)
|
|
19
21
|
raise Error, 'A block is required' unless block
|
|
20
22
|
|
|
21
|
-
builder = Builder.new(records)
|
|
23
|
+
builder = Builder.new(records, delimiter: delimiter, quote_char: quote_char)
|
|
22
24
|
builder.instance_eval(&block)
|
|
23
25
|
builder
|
|
24
26
|
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
|
+
version: 0.2.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-03
|
|
11
|
+
date: 2026-04-03 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, and file output support.
|
|
@@ -25,11 +25,11 @@ files:
|
|
|
25
25
|
- lib/philiprehberger/csv_builder/builder.rb
|
|
26
26
|
- lib/philiprehberger/csv_builder/column.rb
|
|
27
27
|
- lib/philiprehberger/csv_builder/version.rb
|
|
28
|
-
homepage: https://
|
|
28
|
+
homepage: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-csv_builder
|
|
29
29
|
licenses:
|
|
30
30
|
- MIT
|
|
31
31
|
metadata:
|
|
32
|
-
homepage_uri: https://
|
|
32
|
+
homepage_uri: https://philiprehberger.com/open-source-packages/ruby/philiprehberger-csv_builder
|
|
33
33
|
source_code_uri: https://github.com/philiprehberger/rb-csv-builder
|
|
34
34
|
changelog_uri: https://github.com/philiprehberger/rb-csv-builder/blob/main/CHANGELOG.md
|
|
35
35
|
bug_tracker_uri: https://github.com/philiprehberger/rb-csv-builder/issues
|