honey_format 0.10.0 → 0.11.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: e5d7c212e16b346e58a80617576ff6c39b1938885b8a66ad7a91c1b7654897cf
4
- data.tar.gz: e70bee6d2a69eb8624a35d69300c0e0f02772e88f0237a94f64fa6a13d0c409f
3
+ metadata.gz: 220883bb92b35808697b428d346d1f171d2d06d47d4a9ed79404b824a7e65781
4
+ data.tar.gz: 9260762827e1fa50c1669696edfbc625a9e94ffc04773db81f27d6c8d9e7dd88
5
5
  SHA512:
6
- metadata.gz: 7a0af309f8e7f31f725cd9b3d391ee27c4ebf858d31498f39dad99e78cf55ebcd8541a347fb2937fa51a52a0b43405b611943f52dda8040a9124ada30f3a2b54
7
- data.tar.gz: 9c6a89db6da810c78dfe2c74b0fb1ea175b4e62392226bd84e92c53ce5529049190e5c99cdb518dc358f5a98d4385fc3554add7407d2f5c809e3966631ffc467
6
+ metadata.gz: 245594826d582fdbbe02fb303a004197849f9ae17b184ecd4d57e833f98b220e28543bd3aaa1706221b9196b548d7adbd2b73f38ce96e24685f361645d86e307
7
+ data.tar.gz: 3c907de2398718ae946aeb3984991b9273b359e667004c331e86a359e513c38e2d09836c52fba230a2a98e47cdb3e93a5e964b7583c48e6d43ce739018806bd8
data/CHANGELOG.md CHANGED
@@ -1,3 +1,8 @@
1
+ # v0.11.0
2
+
3
+ * Add CLI: `honey_format` executable
4
+ * Swap `RowBuilder` <> `Row` class names [[#PR12](https://github.com/buren/honey_format/pull/12)]
5
+
1
6
  # v0.10.0
2
7
 
3
8
  * Add support for filtering what rows are included in `#to_csv` [[#PR11](https://github.com/buren/honey_format/pull/11)]
data/README.md CHANGED
@@ -1,8 +1,19 @@
1
- # HoneyFormat [![Build Status](https://travis-ci.org/buren/honey_format.svg)](https://travis-ci.org/buren/honey_format) [![Code Climate](https://codeclimate.com/github/buren/honey_format/badges/gpa.svg)](https://codeclimate.com/github/buren/honey_format) ![Docs badge](https://inch-ci.org/github/buren/honey_format.svg?branch=master)
1
+ # HoneyFormat [![Build Status](https://travis-ci.org/buren/honey_format.svg)](https://travis-ci.org/buren/honey_format) [![Code Climate](https://codeclimate.com/github/buren/honey_format/badges/gpa.svg)](https://codeclimate.com/github/buren/honey_format) [![Inline docs](http://inch-ci.org/github/buren/honey_format.svg)](http://inch-ci.org/github/buren/honey_format)
2
2
 
3
3
  Convert CSV to an array of objects with with ease.
4
4
 
5
- Perfect for small files of test data or small import scripts.
5
+ ## Features
6
+
7
+ - Proper objects for CSV header and rows
8
+ - Convert column values with custom row builder
9
+ - Convert header column names
10
+ - Customize what columns and rows are included in CSV output
11
+ - Has no dependencies other than Ruby stdlib
12
+ - Supports Ruby >= 2.3
13
+
14
+ ## Examples
15
+
16
+ See [examples/](https://github.com/buren/honey_format/tree/master/examples) for more examples.
6
17
 
7
18
  ```ruby
8
19
  csv_string = "Id,Username\n1,buren"
@@ -13,8 +24,6 @@ user.id # => "1"
13
24
  user.username # => "buren"
14
25
  ```
15
26
 
16
- :information_source: Supports Ruby >= 2.3, has no dependencies other than Ruby stdlib.
17
-
18
27
  ## Installation
19
28
 
20
29
  Add this line to your application's Gemfile:
@@ -164,10 +173,10 @@ Errors
164
173
  begin
165
174
  HoneyFormat::CSV.new(csv_string)
166
175
  rescue HoneyFormat::HeaderError => e
167
- puts 'there is a problem with the header'
176
+ puts 'there was a problem with the header'
168
177
  raise(e)
169
178
  rescue HoneyFormat::RowError => e
170
- puts 'there is a problem with a row'
179
+ puts 'there was a problem with a row'
171
180
  raise(e)
172
181
  end
173
182
  ```
@@ -193,13 +202,14 @@ $ bin/benchmark file.csv
193
202
  HoneyFormat::CSV: 49.6 i/s - 1.05x slower
194
203
  ```
195
204
 
196
- 19MB (100k lines)
205
+ 2MB (10k lines)
197
206
 
198
207
  ```
199
- stdlib CSV: 0.4 i/s
200
- HoneyFormat::CSV: 0.4 i/s - 1.11x slower
208
+ stdlib CSV: 4.6 i/s
209
+ HoneyFormat::CSV: 4.2 i/s - 1.08x slower
201
210
  ```
202
211
 
212
+
203
213
  ## Development
204
214
 
205
215
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
data/exe/honey_format ADDED
@@ -0,0 +1,58 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # for dev purposes
4
+ require 'bundler/setup' if ENV['HONEY_FORMAT_GEM_DEV']
5
+ require 'honey_format'
6
+
7
+ input_path = ARGV.first
8
+
9
+ columns = nil
10
+ output_path = nil
11
+ delimiter = ','
12
+
13
+ require 'optparse'
14
+
15
+ OptionParser.new do |parser|
16
+ parser.banner = "Usage: honey_format [file.csv] [options]"
17
+ parser.default_argv = ARGV
18
+
19
+ parser.on("--csv=input.csv", String, "CSV file") do |value|
20
+ input_path = value
21
+ end
22
+
23
+ parser.on("--columns=id,name", Array, "Select columns.") do |value|
24
+ columns = value
25
+ end
26
+
27
+ parser.on("--output=output.csv", String, "CSV output (STDOUT otherwise)") do |value|
28
+ output_path = value
29
+ end
30
+
31
+ parser.on("--delimiter=,", String, "CSV delimiter (default: ,)") do |value|
32
+ delimiter = value
33
+ end
34
+
35
+ parser.on("-h", "--help", "How to use") do
36
+ puts parser
37
+ exit
38
+ end
39
+
40
+ parser.on_tail('--version', 'Show version') do
41
+ puts "HoneyFormat version #{HoneyFormat::VERSION}"
42
+ exit
43
+ end
44
+
45
+ # No argument, shows at tail. This will print an options summary.
46
+ parser.on_tail("-h", "--help", "Show this message") do
47
+ puts parser
48
+ exit
49
+ end
50
+ end.parse!
51
+
52
+ csv = HoneyFormat::CSV.new(File.read(input_path), delimiter: delimiter)
53
+ csv_string = csv.to_csv(columns: columns.map(&:to_sym))
54
+ if output_path
55
+ File.write(output_path, csv_string)
56
+ else
57
+ puts csv_string
58
+ end
data/honey_format.gemspec CHANGED
@@ -14,7 +14,8 @@ Gem::Specification.new do |spec|
14
14
  spec.homepage = 'https://github.com/buren/honey_format'
15
15
  spec.license = 'MIT'
16
16
 
17
- spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features|examples)/}) }
18
+ spec.bindir = 'exe'
18
19
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
19
20
  spec.require_paths = ['lib']
20
21
 
@@ -87,17 +87,27 @@ module HoneyFormat
87
87
  end
88
88
  end
89
89
 
90
+ # Convert the column value
91
+ # @param [Object] column the CSV header column value
92
+ # @param [Integer] index the CSV header column index
93
+ # @return [Object] the converted object
90
94
  def convert_column(column, index)
91
95
  return @converter.call(column) if converter_arity == 1
92
96
  @converter.call(column, index)
93
97
  end
94
98
 
99
+ # Returns the converter#call method arity
100
+ # @return [Integer] the converter#call method arity
95
101
  def converter_arity
96
102
  # procs and lambdas respond to #arity
97
103
  return @converter.arity if @converter.respond_to?(:arity)
98
104
  @converter.method(:call).arity
99
105
  end
100
106
 
107
+ # Raises an error if header column is unknown
108
+ # @param [Object] column the CSV header column
109
+ # @param [Array<Symbol, String>] valid CSV columns
110
+ # @raise [Errors::UnknownHeaderColumnError]
101
111
  def maybe_raise_unknown_column!(column, valid)
102
112
  return if valid.empty?
103
113
  return if valid.include?(column)
@@ -106,6 +116,9 @@ module HoneyFormat
106
116
  raise(Errors::UnknownHeaderColumnError, err_msg)
107
117
  end
108
118
 
119
+ # Raises an error if header column is missing/empty
120
+ # @param [Object] column the CSV header column
121
+ # @raise [Errors::MissingHeaderColumnError]
109
122
  def maybe_raise_missing_column!(column)
110
123
  return if column && !column.empty?
111
124
 
@@ -1,49 +1,39 @@
1
- require 'honey_format/row_builder'
2
-
3
1
  module HoneyFormat
4
- # Holds data for a single row.
5
- class Row
6
- # Returns a new instance of Row.
7
- # @return [Row] a new instance of Row.
8
- # @param [Array] columns an array of symbols.
9
- # @param builder [#call, #to_csv] optional row builder
10
- # @raise [RowError] super class of errors raised when there is a row error.
11
- # @raise [EmptyRowColumnsError] raised when there are no columns.
12
- # @raise [InvalidRowLengthError] raised when row has more columns than header columns.
13
- # @example Create new row
14
- # Row.new!([:id])
15
- def initialize(columns, builder: nil)
16
- if columns.empty?
17
- err_msg = 'Expected array with at least one element, but was empty.'
18
- raise(Errors::EmptyRowColumnsError, err_msg)
19
- end
2
+ # Default row builder
3
+ class Row < Struct
4
+ # Create a row
5
+ # @return [Struct] returns an instantiated Struct representing a row
6
+ # @example
7
+ # row_klass = Row.new(:id, :username)
8
+ # row = row_klass.call('1', 'buren')
9
+ # # => #<struct id="1", username="buren">
10
+ def self.call(row)
11
+ new(*row)
12
+ end
13
+
14
+ # Represent row as CSV
15
+ # @param columns [Array<Symbol>, Set<Symbol>, NilClass] the columns to output, nil means all columns (default: nil)
16
+ # @return [String] CSV-string representation.
17
+ def to_csv(columns: nil)
18
+ attributes = members
19
+ attributes = columns & attributes if columns
20
20
 
21
- @row_builder = RowBuilder.new(*columns)
22
- @builder = builder
23
- @columns = columns
21
+ row = attributes.map { |column| to_csv_value(column) }
22
+
23
+ ::CSV.generate_line(row)
24
24
  end
25
25
 
26
- # Returns a Struct.
27
- # @return [Object] a new instance of built row.
28
- # @param row [Array] the row array.
29
- # @raise [InvalidRowLengthError] raised when there are more row elements longer than columns
30
- # @example Build new row
31
- # r = Row.new([:id])
32
- # r.build(['1']).id #=> '1'
33
- def build(row)
34
- built_row = @row_builder.call(row)
35
- return built_row unless @builder
36
- @builder.call(built_row)
37
- rescue ArgumentError => e
38
- raise unless e.message == 'struct size differs'
26
+ private
27
+
28
+ # Returns the column in CSV format
29
+ # @param [Symbol] column name
30
+ # @return [String] column value as CSV string
31
+ def to_csv_value(column)
32
+ value = public_send(column)
33
+ return if value.nil?
34
+ return value.to_csv if value.respond_to?(:to_csv)
39
35
 
40
- err_msg = [
41
- "Row length #{row.length}",
42
- "column length #{@columns.length}",
43
- "row: #{row.inspect}",
44
- "orignal message: '#{e.message}'"
45
- ].join(', ')
46
- raise(Errors::InvalidRowLengthError, err_msg)
36
+ value.to_s
47
37
  end
48
38
  end
49
39
  end
@@ -1,28 +1,58 @@
1
+ require 'honey_format/row'
2
+
1
3
  module HoneyFormat
2
- # Default row builder
3
- class RowBuilder < Struct
4
- # Create a row
5
- # @return [Struct] returns an instantiated Struct representing a row
6
- def self.call(row)
7
- new(*row)
8
- end
4
+ # Holds data for a single row.
5
+ class RowBuilder
6
+ # Returns a new instance of RowBuilder.
7
+ # @return [RowBuilder] a new instance of RowBuilder.
8
+ # @param [Array<Symbol>] columns an array of symbols.
9
+ # @param builder [#call, #to_csv] optional row builder
10
+ # @raise [RowError] super class of errors raised when there is a row error.
11
+ # @raise [EmptyRowColumnsError] raised when there are no columns.
12
+ # @raise [InvalidRowLengthError] raised when row has more columns than header columns.
13
+ # @example Create new row
14
+ # RowBuilder.new!([:id])
15
+ def initialize(columns, builder: nil)
16
+ if columns.empty?
17
+ err_msg = 'Expected array with at least one element, but was empty.'
18
+ raise(Errors::EmptyRowColumnsError, err_msg)
19
+ end
9
20
 
10
- # Represent row as CSV
11
- # @param columns [Array<Symbol>, Set<Symbol>, NilClass] the columns to output, nil means all columns (default: nil)
12
- # @return [String] CSV-string representation.
13
- def to_csv(columns: nil)
14
- attributes = members
15
- attributes = columns & attributes if columns
21
+ @row_klass = Row.new(*columns)
22
+ @builder = builder
23
+ @columns = columns
24
+ end
16
25
 
17
- row = attributes.map do |column_name|
18
- column = public_send(column_name)
19
- next column.to_csv if column.respond_to?(:to_csv)
20
- next if column.nil?
26
+ # Returns a Struct.
27
+ # @return [Row, Object] a new instance of built row.
28
+ # @param row [Array] the row array.
29
+ # @raise [InvalidRowLengthError] raised when there are more row elements longer than columns
30
+ # @example Build new row
31
+ # r = RowBuilder.new([:id])
32
+ # r.build(['1']).id #=> '1'
33
+ def build(row)
34
+ row = @row_klass.call(row)
35
+ return row unless @builder
36
+ @builder.call(row)
37
+ rescue ArgumentError => e
38
+ raise unless e.message == 'struct size differs'
39
+ raise_invalid_row_length!(e, row)
40
+ end
21
41
 
22
- column.to_s
23
- end
42
+ private
24
43
 
25
- ::CSV.generate_line(row)
44
+ # Raises invalid row length error
45
+ # @param [StandardError] e the raised error
46
+ # @param [Object] row
47
+ # @raise [Errors::InvalidRowLengthError]
48
+ def raise_invalid_row_length!(e, row)
49
+ err_msg = [
50
+ "Row length #{row.length}",
51
+ "column length #{@columns.length}",
52
+ "row: #{row.inspect}",
53
+ "orignal message: '#{e.message}'"
54
+ ].join(', ')
55
+ raise(Errors::InvalidRowLengthError, err_msg)
26
56
  end
27
57
  end
28
58
  end
@@ -1,5 +1,5 @@
1
1
  require 'set'
2
- require 'honey_format/row'
2
+ require 'honey_format/row_builder'
3
3
 
4
4
  module HoneyFormat
5
5
  # Represents rows.
@@ -14,7 +14,8 @@ module HoneyFormat
14
14
  # @raise [EmptyRowColumnsError] raised when there are no columns.
15
15
  # @raise [InvalidRowLengthError] raised when row has more columns than header columns.
16
16
  def initialize(rows, columns, builder: nil)
17
- @rows = prepare_rows(Row.new(columns, builder: builder), rows)
17
+ builder = RowBuilder.new(columns, builder: builder)
18
+ @rows = prepare_rows(builder, rows)
18
19
  end
19
20
 
20
21
  # @yield [row] The given block will be passed for every row.
@@ -1,4 +1,4 @@
1
1
  module HoneyFormat
2
2
  # Gem version
3
- VERSION = '0.10.0'
3
+ VERSION = '0.11.0'
4
4
  end
metadata CHANGED
@@ -1,12 +1,12 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honey_format
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.10.0
4
+ version: 0.11.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Burenstam
8
8
  autorequire:
9
- bindir: bin
9
+ bindir: exe
10
10
  cert_chain: []
11
11
  date: 2018-06-09 00:00:00.000000000 Z
12
12
  dependencies:
@@ -99,7 +99,8 @@ description: Convert CSV to an array of objects with with ease. Create objects f
99
99
  stdlib.
100
100
  email:
101
101
  - burenstam@gmail.com
102
- executables: []
102
+ executables:
103
+ - honey_format
103
104
  extensions: []
104
105
  extra_rdoc_files: []
105
106
  files:
@@ -115,6 +116,7 @@ files:
115
116
  - bin/benchmark
116
117
  - bin/console
117
118
  - bin/setup
119
+ - exe/honey_format
118
120
  - honey_format.gemspec
119
121
  - lib/honey_format.rb
120
122
  - lib/honey_format/convert_header_value.rb