honey_format 0.19.0 → 0.20.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: cf04ea671d5bdbe82a40c6c755e0f975a4326e3f803cc380cbfb513bcb7587d1
4
- data.tar.gz: 14893ca65e6ecb420fac9e4a4783d2ab2267d2b63b492a70c297ca51e66bf711
3
+ metadata.gz: 7c0136727f1cf1e473ac8a517105158a9c927c5988f525b1640ffced32c5a2fd
4
+ data.tar.gz: d26d86cd08edbc33f2f9b8399ef2f7c5aade4b35ca4ea7617dc6a21415b72c9a
5
5
  SHA512:
6
- metadata.gz: c9a89819d1d40b99af14bbc1c0b5df765d5c91ab7c1146f39f8b6dd04796d718251280b6b31b3dc61e14ba413f46d9edb9e7e8a1aa1c308531d20cfdb0c5bb9b
7
- data.tar.gz: b23f67d3df21ca3a01d03c7054502aa0a60d2819717f770db3a30edccdf597b9a9943b7107515e1e454156625c5d1aa3efcd918772b0dcbc5b9b85298ed71fe8
6
+ metadata.gz: 6ec8850e11d3ae4039b748738641bacd19ea3ffe9f437e2d93eb407e9d7e7cb2dcc669116f04f2a175855fbc121bad62228dc6dbd022163d033a52e89af6e8e7
7
+ data.tar.gz: 9ec609fe969d26348c1be54458a79ea24a199085381e82c3bc79d58b5054bb02300759946f520c7b29eb0273e56db290375c791ee4fb2c5fecf10da65954e54b
@@ -9,11 +9,11 @@ matrix:
9
9
  include:
10
10
  - rvm: 2.3.0
11
11
  install:
12
- - gem install bundler --no-ri --no-rdoc
12
+ - gem install bundler --no-document
13
13
  - bundle install
14
14
  - rvm: 2.5.1
15
15
  install:
16
- - gem install bundler --no-ri --no-rdoc
16
+ - gem install bundler --no-document
17
17
  - bundle install
18
18
  - rvm: 2.6.0-preview3
19
19
  install:
@@ -21,7 +21,7 @@ matrix:
21
21
  - name: TruffleRuby
22
22
  rvm: system
23
23
  install:
24
- - export TRUFFLERUBY_VERSION=1.0.0-rc3
24
+ - export TRUFFLERUBY_VERSION=1.0.0-rc10
25
25
  - curl -L https://github.com/oracle/truffleruby/releases/download/vm-$TRUFFLERUBY_VERSION/truffleruby-$TRUFFLERUBY_VERSION-linux-amd64.tar.gz | tar xz
26
26
  - export PATH="$PWD/truffleruby-$TRUFFLERUBY_VERSION-linux-amd64/bin:$PATH"
27
27
  - gem install bundler -v 1.16.6 --no-ri --no-rdoc
@@ -1,5 +1,17 @@
1
1
  # HEAD
2
2
 
3
+ ## v0.20.0
4
+
5
+ * Support additional header variant, pass hash with `String => Symbol` and/or `String => #call` (callable object). Unmapped keys are converted using the default header converter.
6
+ ```ruby
7
+ converter = {
8
+ 'First name' => :first_name,
9
+ 'Last name' => -> { :surname }
10
+ }
11
+ csv = HoneyFormat::CSV.new(csv_string, header_converter: converter)
12
+ ```
13
+ * Add `encoding` option to `CSV`
14
+
3
15
  ## v0.19.0
4
16
 
5
17
  * Add `method_name` as alias for `header_column` converter
data/README.md CHANGED
@@ -27,8 +27,8 @@ Id,Username,Email
27
27
  2,jacob,jacob@example.com
28
28
  CSV
29
29
  csv = HoneyFormat::CSV.new(csv_string, type_map: { id: :integer })
30
- csv.columns # => [:id, :username]
31
- csv.rows # => [#<Row id=1, username="buren">]
30
+ csv.columns # => [:id, :username, :email]
31
+ csv.rows # => [#<Row id=1, username="buren", email="buren@example.com">, #<Row id=2, username="jacob", email="jacob@example.com">]
32
32
  user = csv.rows.first
33
33
  user.id # => 1
34
34
  user.username # => "buren"
@@ -248,21 +248,36 @@ csv.columns # => [:ID, :USERNAME]
248
248
 
249
249
  Pass your own header converter
250
250
  ```ruby
251
- map = { 'First^Name' => :first_name }
252
- converter = ->(column) { map.fetch(column, column.downcase) }
251
+ # unmapped keys use the default header converter,
252
+ # mix simple key => value mapping with key => proc
253
+ converter = {
254
+ 'First^Name' => :first_name,
255
+ 'Username' => -> { :handle }
256
+ }
253
257
 
254
- csv_string = "ID,First^Name\n1,Jacob"
258
+ csv_string = "ID,Username,First^Name\n1,buren,Jacob"
255
259
  user = HoneyFormat::CSV.new(csv_string, header_converter: converter).rows.first
256
260
  user.first_name # => "Jacob"
261
+ user.handle # => "buren"
257
262
  user.id # => "1"
263
+
264
+ # you can also pass a proc or any callable object
265
+ converter = Class.new do
266
+ define_singleton_method(:call) { |value, index| "#{value}#{index}" }
267
+ end
268
+ # or
269
+ converter = ->(value, index) { "#{value}#{index}" }
270
+ user = HoneyFormat::CSV.new(csv_string, header_converter: converter)
258
271
  ```
259
272
 
260
- Missing header values
273
+ Missing header values are automatically set and deduplicated
261
274
  ```ruby
262
- csv_string = "first,,third\nval0,val1,val2"
275
+ csv_string = "first,,third,third\nval0,val1,val2,val3"
263
276
  csv = HoneyFormat::CSV.new(csv_string)
264
277
  user = csv.rows.first
265
278
  user.column1 # => "val1"
279
+ user.third # => "val2"
280
+ user.third1 # => "val3"
266
281
  ```
267
282
 
268
283
  Duplicated header values
@@ -302,7 +317,7 @@ user['first^name'] # => "Jacob"
302
317
 
303
318
  __Errors__
304
319
 
305
- > When you need that some extra safety.
320
+ > When you need to be extra safe.
306
321
 
307
322
  If you want to there are some errors you can rescue
308
323
  ```ruby
@@ -23,7 +23,7 @@ Gem::Specification.new do |spec|
23
23
  spec.required_ruby_version = '>= 2.3.0'
24
24
 
25
25
  spec.add_development_dependency 'benchmark-ips'
26
- spec.add_development_dependency 'bundler', '~> 1.10'
26
+ spec.add_development_dependency 'bundler', '> 1.10', '< 3'
27
27
  spec.add_development_dependency 'byebug'
28
28
  spec.add_development_dependency 'rake', '~> 10.0'
29
29
  spec.add_development_dependency 'rspec'
@@ -19,6 +19,7 @@ module HoneyFormat
19
19
  # @param header_deduplicator [#call] deduplicates header columns.
20
20
  # @param row_builder [#call] will be called for each parsed row.
21
21
  # @param type_map [Hash] map of column_name => type conversion to perform.
22
+ # @param encoding [String] CSV encoding (for example "BOM|UTF-16LE:UTF-8").
22
23
  # @param skip_lines [Regexp, String]
23
24
  # Regexp for determining wheter a line is a comment. See CSV skip_lines option.
24
25
  # @raise [HeaderError] super class of errors raised when there is a CSV header error.
@@ -46,6 +47,8 @@ module HoneyFormat
46
47
  # @example Skip lines all lines starting with '#'
47
48
  # csv = HoneyFormat::CSV.new("name,id\n# some comment\njacob,1", skip_lines: '#')
48
49
  # csv.rows.length # => 1
50
+ # @example CSV encoding
51
+ # csv = HoneyFormat::CSV.new(csv_string, encoding: "BOM|UTF-16LE:UTF-8")
49
52
  # @see Matrix#new
50
53
  def initialize(
51
54
  csv,
@@ -56,6 +59,7 @@ module HoneyFormat
56
59
  header_converter: HoneyFormat.header_converter,
57
60
  header_deduplicator: HoneyFormat.config.header_deduplicator,
58
61
  row_builder: nil,
62
+ encoding: nil,
59
63
  type_map: {},
60
64
  skip_lines: HoneyFormat.config.skip_lines
61
65
  )
@@ -65,7 +69,8 @@ module HoneyFormat
65
69
  row_sep: row_delimiter,
66
70
  quote_char: quote_character,
67
71
  skip_blanks: true,
68
- skip_lines: skip_lines
72
+ skip_lines: skip_lines,
73
+ encoding: encoding
69
74
  )
70
75
  super(
71
76
  csv,
@@ -10,10 +10,11 @@ module HoneyFormat
10
10
  # Instantiate a Header
11
11
  # @return [Header] a new instance of Header.
12
12
  # @param [Array<String>] header array of strings.
13
- # @param converter [#call, Symbol]
13
+ # @param converter [#call, Symbol, Hash]
14
14
  # header converter that implements a #call method
15
15
  # that takes one column (string) argument OR symbol for a registered
16
- # converter registry.
16
+ # converter registry OR a hash mapped to a symbol or something that responds
17
+ # to #call.
17
18
  # @param deduplicator [#call, Symbol]
18
19
  # header deduplicator that implements a #call method
19
20
  # that takes columns Array<String> argument OR symbol for a registered
@@ -93,7 +94,7 @@ module HoneyFormat
93
94
  private
94
95
 
95
96
  # Set the header converter
96
- # @param [Symbol, #call] symbol to known converter or object that responds to #call
97
+ # @param [Hash, Symbol, #call] symbol to known converter, object that responds to #call or Hash
97
98
  # @return [nil]
98
99
  def converter=(object)
99
100
  if object.is_a?(Symbol)
@@ -101,6 +102,11 @@ module HoneyFormat
101
102
  return
102
103
  end
103
104
 
105
+ if object.is_a?(Hash)
106
+ @converter = hash_converter(object)
107
+ return
108
+ end
109
+
104
110
  @converter = object
105
111
  end
106
112
 
@@ -134,21 +140,17 @@ module HoneyFormat
134
140
  # @param [Integer] index the CSV header column index
135
141
  # @return [Symbol] the converted column
136
142
  def convert_column(column, index)
137
- value = if converter_arity == 1
138
- @converter.call(column)
139
- else
140
- @converter.call(column, index)
141
- end
142
- value.to_sym
143
+ call_column_builder(@converter, column, index)&.to_sym
143
144
  end
144
145
 
145
- # Returns the converter#call method arity
146
- # @return [Integer] the converter#call method arity
147
- def converter_arity
146
+ # Returns the callable object method arity
147
+ # @param [#arity, #call] callable thing that responds to #call and maybe #arity
148
+ # @return [Integer] the method arity
149
+ def callable_arity(callable)
148
150
  # procs and lambdas respond to #arity
149
- return @converter.arity if @converter.respond_to?(:arity)
151
+ return callable.arity if callable.respond_to?(:arity)
150
152
 
151
- @converter.method(:call).arity
153
+ callable.method(:call).arity
152
154
  end
153
155
 
154
156
  # Raises an error if header column is missing/empty
@@ -164,5 +166,29 @@ module HoneyFormat
164
166
  ]
165
167
  raise(Errors::MissingHeaderColumnError, parts.join(' '))
166
168
  end
169
+
170
+ def hash_converter(hash)
171
+ lambda { |value, index|
172
+ # support strings and symbol keys interchangeably
173
+ column = hash.fetch(value) do
174
+ key = value.respond_to?(:to_sym) ? value.to_sym : value
175
+ # if column is unmapped use the default header converter
176
+ hash.fetch(key) { HoneyFormat.header_converter.call(value, index) }
177
+ end
178
+
179
+ # The hash can contain mixed values, Symbol and procs
180
+ if column.respond_to?(:call)
181
+ column = call_column_builder(column, value, index)
182
+ end
183
+
184
+ column&.to_sym
185
+ }
186
+ end
187
+
188
+ def call_column_builder(callable, value, index)
189
+ return callable.call if callable_arity(callable).zero?
190
+ return callable.call(value) if callable_arity(callable) == 1
191
+ callable.call(value, index)
192
+ end
167
193
  end
168
194
  end
@@ -4,7 +4,7 @@ module HoneyFormat
4
4
  # Default row builder
5
5
  class Row < Struct
6
6
  # Create a row
7
- # @return [Struct] returns an instantiated Struct representing a row
7
+ # @return [Row] returns an instantiated Row
8
8
  # @example
9
9
  # row_klass = Row.new(:id, :username)
10
10
  # row = row_klass.call('1', 'buren')
@@ -4,7 +4,7 @@ module HoneyFormat
4
4
  # Gem version
5
5
  VERSION = [
6
6
  MAJOR_VERSION = 0,
7
- MINOR_VERSION = 19,
7
+ MINOR_VERSION = 20,
8
8
  PATCH_VERSION = 0,
9
9
  ].join('.')
10
10
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: honey_format
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.19.0
4
+ version: 0.20.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Burenstam
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-08 00:00:00.000000000 Z
11
+ date: 2019-12-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: benchmark-ips
@@ -28,16 +28,22 @@ dependencies:
28
28
  name: bundler
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">"
32
32
  - !ruby/object:Gem::Version
33
33
  version: '1.10'
34
+ - - "<"
35
+ - !ruby/object:Gem::Version
36
+ version: '3'
34
37
  type: :development
35
38
  prerelease: false
36
39
  version_requirements: !ruby/object:Gem::Requirement
37
40
  requirements:
38
- - - "~>"
41
+ - - ">"
39
42
  - !ruby/object:Gem::Version
40
43
  version: '1.10'
44
+ - - "<"
45
+ - !ruby/object:Gem::Version
46
+ version: '3'
41
47
  - !ruby/object:Gem::Dependency
42
48
  name: byebug
43
49
  requirement: !ruby/object:Gem::Requirement
@@ -162,8 +168,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
162
168
  - !ruby/object:Gem::Version
163
169
  version: '0'
164
170
  requirements: []
165
- rubyforge_project:
166
- rubygems_version: 2.7.6
171
+ rubygems_version: 3.0.3
167
172
  signing_key:
168
173
  specification_version: 4
169
174
  summary: Makes working with CSVs as smooth as honey.