honey_format 0.10.0 → 0.11.0

Sign up to get free protection for your applications and to get access to all the features.
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