Class: Philiprehberger::CsvBuilder::Builder
- Inherits:
-
Object
- Object
- Philiprehberger::CsvBuilder::Builder
- Defined in:
- lib/philiprehberger/csv_builder/builder.rb
Overview
DSL builder for constructing CSV output from records
Instance Attribute Summary collapse
-
#columns ⇒ Array<Column>
readonly
The defined columns.
-
#records ⇒ Array
readonly
The source records.
Instance Method Summary collapse
-
#append_to(path) ⇒ void
Append data rows (no header, no BOM) to an existing CSV file.
-
#column(name, header: nil) {|record| ... } ⇒ self
Define a column.
-
#filter {|record| ... } ⇒ self
Add a filter to exclude records.
-
#filtered_records ⇒ Array
Return the filtered records.
-
#footer {|Array| ... } ⇒ self
Append a computed footer row after all data rows.
-
#headers ⇒ Array<String>
Return the header row.
-
#initialize(records, delimiter: ',', quote_char: '"', row_sep: "\n", bom: false, encoding: 'UTF-8', empty_value: '') ⇒ Builder
constructor
A new instance of Builder.
-
#limit(n) ⇒ self
Limit the number of output rows.
-
#offset(n) ⇒ self
Skip the first N filtered/sorted records.
-
#row_count ⇒ Integer
Number of data rows the builder will emit (headers and footer excluded).
-
#row_number(header: '#') ⇒ self
Add an auto-incrementing row number as the first column.
-
#sort_by(direction: :asc) {|record| ... } ⇒ self
Sort records before CSV output.
-
#to_a ⇒ Array<Array>
Return the CSV as an array of row arrays (headers + data + footer).
-
#to_csv ⇒ String
Generate the CSV as a string.
-
#to_file(path) ⇒ void
Write the CSV to a file.
-
#to_io(io) ⇒ void
Stream CSV to any IO object.
-
#to_s ⇒ String
Alias for #to_csv so instances behave nicely with string interpolation.
-
#total(column_name) {|values| ... } ⇒ self
Shorthand for adding a footer row with a computed total for the named column.
-
#transform_header {|name| ... } ⇒ self
Register a proc applied to all column headers during rendering.
-
#validate {|row| ... } ⇒ self
Register a validation block for rows.
-
#write_to(path, mode: 'wb') ⇒ void
Write the CSV to a file with an explicit mode.
Constructor Details
#initialize(records, delimiter: ',', quote_char: '"', row_sep: "\n", bom: false, encoding: 'UTF-8', empty_value: '') ⇒ Builder
Returns a new instance of Builder.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 23 def initialize(records, delimiter: ',', quote_char: '"', row_sep: "\n", bom: false, encoding: 'UTF-8', empty_value: '') @records = records @columns = [] @filters = [] @validations = [] @row_number_header = nil @delimiter = delimiter @quote_char = quote_char @row_sep = row_sep @sort_by = nil @sort_direction = :asc @limit_count = nil @offset_count = nil @footer_block = nil @header_transform = nil @bom = bom @encoding = encoding @empty_value = empty_value end |
Instance Attribute Details
#columns ⇒ Array<Column> (readonly)
Returns the defined columns.
11 12 13 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 11 def columns @columns end |
#records ⇒ Array (readonly)
Returns the source records.
14 15 16 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 14 def records @records end |
Instance Method Details
#append_to(path) ⇒ void
This method returns an undefined value.
Append data rows (no header, no BOM) to an existing CSV file.
249 250 251 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 249 def append_to(path) write_to(path, mode: 'ab') end |
#column(name, header: nil) {|record| ... } ⇒ self
Define a column
136 137 138 139 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 136 def column(name, header: nil, &block) @columns << Column.new(name, header: header, &block) self end |
#filter {|record| ... } ⇒ self
Add a filter to exclude records
146 147 148 149 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 146 def filter(&block) @filters << block self end |
#filtered_records ⇒ Array
Return the filtered records
180 181 182 183 184 185 186 187 188 189 190 191 192 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 180 def filtered_records result = @records @filters.each do |f| result = result.select(&f) end if @sort_by result = result.sort_by(&@sort_by) result = result.reverse if @sort_direction == :desc end result = result.drop(@offset_count) if @offset_count result = result.first(@limit_count) if @limit_count result end |
#footer {|Array| ... } ⇒ self
Append a computed footer row after all data rows
84 85 86 87 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 84 def (&block) @footer_block = block self end |
#headers ⇒ Array<String>
Return the header row
163 164 165 166 167 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 163 def headers base = @columns.map(&:header) base = base.map { |h| @header_transform.call(h) } if @header_transform @row_number_header ? [@row_number_header] + base : base end |
#limit(n) ⇒ self
Limit the number of output rows
65 66 67 68 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 65 def limit(n) @limit_count = n self end |
#offset(n) ⇒ self
Skip the first N filtered/sorted records
74 75 76 77 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 74 def offset(n) @offset_count = n self end |
#row_count ⇒ Integer
Number of data rows the builder will emit (headers and footer excluded). Applies all configured filters, sorts, offsets, and limits.
173 174 175 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 173 def row_count filtered_records.size end |
#row_number(header: '#') ⇒ self
Add an auto-incrementing row number as the first column
155 156 157 158 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 155 def row_number(header: '#') @row_number_header = header self end |
#sort_by(direction: :asc) {|record| ... } ⇒ self
Sort records before CSV output
51 52 53 54 55 56 57 58 59 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 51 def sort_by(direction: :asc, &block) raise Error, 'A block is required for sort_by' unless block raise Error, "direction must be :asc or :desc (got #{direction.inspect})" unless %i[asc desc].include?(direction) @sort_by = block @sort_direction = direction self end |
#to_a ⇒ Array<Array>
Return the CSV as an array of row arrays (headers + data + footer).
274 275 276 277 278 279 280 281 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 274 def to_a recs = filtered_records validate_rows!(recs) unless @validations.empty? rows = [headers] recs.each_with_index { |record, index| rows << build_row(record, index) } rows << @footer_block.call(recs) if @footer_block rows end |
#to_csv ⇒ String
Generate the CSV as a string
198 199 200 201 202 203 204 205 206 207 208 209 210 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 198 def to_csv recs = filtered_records validate_rows!(recs) unless @validations.empty? csv_string = CSV.generate(**) do |csv| csv << headers recs.each_with_index do |record, index| csv << build_row(record, index) end csv << @footer_block.call(recs) if @footer_block end csv_string = csv_string.encode(@encoding) unless @encoding == 'UTF-8' @bom ? "\xEF\xBB\xBF#{csv_string}" : csv_string end |
#to_file(path) ⇒ void
This method returns an undefined value.
Write the CSV to a file
223 224 225 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 223 def to_file(path) File.binwrite(path, to_csv) end |
#to_io(io) ⇒ void
This method returns an undefined value.
Stream CSV to any IO object
258 259 260 261 262 263 264 265 266 267 268 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 258 def to_io(io) io.write("\xEF\xBB\xBF") if @bom recs = filtered_records validate_rows!(recs) unless @validations.empty? csv = CSV.new(io, **) csv << headers recs.each_with_index do |record, index| csv << build_row(record, index) end csv << @footer_block.call(recs) if @footer_block end |
#to_s ⇒ String
Alias for #to_csv so instances behave nicely with string interpolation.
215 216 217 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 215 def to_s to_csv end |
#total(column_name) {|values| ... } ⇒ self
Shorthand for adding a footer row with a computed total for the named column
114 115 116 117 118 119 120 121 122 123 124 125 126 127 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 114 def total(column_name, &block) col_name = column_name.to_sym @footer_block = lambda do |recs| columns.map do |col| if col.name == col_name values = recs.map { |r| col.extract(r, empty_value: @empty_value).to_f } block ? block.call(values) : values.sum else '' end end end self end |
#transform_header {|name| ... } ⇒ self
Register a proc applied to all column headers during rendering
104 105 106 107 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 104 def transform_header(&block) @header_transform = block self end |
#validate {|row| ... } ⇒ self
Register a validation block for rows
94 95 96 97 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 94 def validate(&block) @validations << block self end |
#write_to(path, mode: 'wb') ⇒ void
This method returns an undefined value.
Write the CSV to a file with an explicit mode. Useful for appending to existing files or combining multiple builders into one file.
When appending (‘mode: ’ab’‘ / `’a’‘), the header row and BOM from subsequent writes are suppressed so the file keeps a single header.
236 237 238 239 240 241 242 243 |
# File 'lib/philiprehberger/csv_builder/builder.rb', line 236 def write_to(path, mode: 'wb') appending = mode.start_with?('a') if appending File.open(path, mode) { |f| write_body_rows(f) } else to_file(path) end end |