honey_format 0.19.0 → 0.23.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: 54b1c442221f4921f054ba328b55f066166d27b27bd56aa8499bf195bac65847
4
+ data.tar.gz: 58977315c3fb95a54a0cbc7ab930a8ade04016865c589f4e6374ce113a080c1c
5
5
  SHA512:
6
- metadata.gz: c9a89819d1d40b99af14bbc1c0b5df765d5c91ab7c1146f39f8b6dd04796d718251280b6b31b3dc61e14ba413f46d9edb9e7e8a1aa1c308531d20cfdb0c5bb9b
7
- data.tar.gz: b23f67d3df21ca3a01d03c7054502aa0a60d2819717f770db3a30edccdf597b9a9943b7107515e1e454156625c5d1aa3efcd918772b0dcbc5b9b85298ed71fe8
6
+ metadata.gz: 1cbbc61c953422dea9de989ebe429b05325140868c54b2861dbaafac222425706d8af481d86c0a0f3a75bbc19bcabfb5d580d0261c0a3ba928c3a2d45390abf7
7
+ data.tar.gz: d07789988d8ad6f335fa517bc07954f0f9800c3dce3ab10a9295c6edae81994fd78ca9ed2e44c6ac6b8759593e0807edeb78457bbb54ce4e348db116f953374a
@@ -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,33 @@
1
1
  # HEAD
2
2
 
3
+ ## v0.23.0
4
+
5
+ * Add `Rows#columnns` method that returns header columns
6
+
7
+ ## v0.22.0
8
+
9
+ * Add `Matrix#type_map` and ignore non-existing column in the type map. [PR#64](https://github.com/buren/honey_format/pull/64). Thank you [@prem-prakash](https://github.com/prem-prakash).
10
+
11
+ ## v0.21.1
12
+
13
+ * Closes [issue #58](https://github.com/buren/honey_format/issues/58). [PR #62](https://github.com/buren/honey_format/pull/62)
14
+
15
+ ## v0.21.0
16
+
17
+ * Add `Rows#[]` method
18
+
19
+ ## v0.20.0
20
+
21
+ * Support additional header variant, pass hash with `String => Symbol` and/or `String => #call` (callable object). Unmapped keys are converted using the default header converter.
22
+ ```ruby
23
+ converter = {
24
+ 'First name' => :first_name,
25
+ 'Last name' => -> { :surname }
26
+ }
27
+ csv = HoneyFormat::CSV.new(csv_string, header_converter: converter)
28
+ ```
29
+ * Add `encoding` option to `CSV`
30
+
3
31
  ## v0.19.0
4
32
 
5
33
  * 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"
@@ -91,7 +91,7 @@ __Type converters__
91
91
 
92
92
  > Type converters are great if you want to convert column values, like numbers and dates.
93
93
 
94
- There are a few default type converters
94
+ There are a bunch of [default type converters](https://github.com/buren/honey_format/blob/master/lib/honey_format/converters/converters.rb)
95
95
  ```ruby
96
96
  csv_string = "Id,Username\n1,buren"
97
97
  type_map = { id: :integer }
@@ -139,7 +139,7 @@ Default converter names
139
139
  HoneyFormat.config.default_converters.keys
140
140
  ```
141
141
 
142
- See [`Configuration#default_converters`](https://github.com/buren/honey_format/blob/master/lib/honey_format/configuration.rb#L99) for a complete list of the default ones.
142
+ See [`Converters::DEFAULT`](https://github.com/buren/honey_format/blob/master/lib/honey_format/converters.rb) for a complete list of the default converter names.
143
143
 
144
144
  __Row builder__
145
145
 
@@ -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,9 +23,9 @@ 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
- spec.add_development_dependency 'rake', '~> 10.0'
28
+ spec.add_development_dependency 'rake', '~> 12.3'
29
29
  spec.add_development_dependency 'rspec'
30
30
  spec.add_development_dependency 'simplecov'
31
31
  end
@@ -97,34 +97,7 @@ module HoneyFormat
97
97
  # Default converter registry
98
98
  # @return [Hash] hash with default converters
99
99
  def default_converters
100
- @default_converters ||= {
101
- # strict variants
102
- decimal!: StrictConvertDecimal,
103
- integer!: StrictConvertInteger,
104
- date!: StrictConvertDate,
105
- datetime!: StrictConvertDatetime,
106
- symbol!: StrictConvertSymbol,
107
- downcase!: StrictConvertDowncase,
108
- upcase!: StrictConvertUpcase,
109
- boolean!: StrictConvertBoolean,
110
- # safe variants
111
- decimal: ConvertDecimal,
112
- decimal_or_zero: ConvertDecimalOrZero,
113
- integer: ConvertInteger,
114
- integer_or_zero: ConvertIntegerOrZero,
115
- date: ConvertDate,
116
- datetime: ConvertDatetime,
117
- symbol: ConvertSymbol,
118
- downcase: ConvertDowncase,
119
- upcase: ConvertUpcase,
120
- boolean: ConvertBoolean,
121
- md5: ConvertMD5,
122
- hex: ConvertHex,
123
- nil: ConvertNil,
124
- blank: ConvertBlank,
125
- header_column: ConvertHeaderColumn,
126
- method_name: ConvertHeaderColumn,
127
- }.freeze
100
+ @default_converters ||= Converters::DEFAULT
128
101
  end
129
102
  end
130
103
  end
@@ -4,9 +4,9 @@ require 'set'
4
4
 
5
5
  module HoneyFormat
6
6
  # String values considered truthy
7
- TRUTHY = Set.new(%w[t T 1 y Y true TRUE]).freeze
7
+ TRUTHY = Set.new(%w[t T 1 y Y true TRUE] + [true]).freeze
8
8
  # String values considered falsy
9
- FALSY = Set.new(%w[f F 0 n N false FALSE]).freeze
9
+ FALSY = Set.new(%w[f F 0 n N false FALSE] + [false]).freeze
10
10
 
11
11
  # Tries to convert value boolean to, returns nil if it can't convert
12
12
  ConvertBoolean = proc do |v|
@@ -23,8 +23,8 @@ module HoneyFormat
23
23
  end
24
24
 
25
25
  # Convert to date or raise error
26
- StrictConvertDate = proc { |v| Date.parse(v) }
26
+ StrictConvertDate = proc { |v| v.is_a?(Date) ? v : Date.parse(v) }
27
27
 
28
28
  # Convert to datetime or raise error
29
- StrictConvertDatetime = proc { |v| Time.parse(v) }
29
+ StrictConvertDatetime = proc { |v| v.is_a?(Time) ? v : Time.parse(v) }
30
30
  end
@@ -9,4 +9,35 @@ require 'honey_format/converters/convert_string'
9
9
  module HoneyFormat
10
10
  # Convert to nil
11
11
  ConvertNil = proc {}
12
+
13
+ module Converters
14
+ DEFAULT = {
15
+ # strict variants
16
+ decimal!: StrictConvertDecimal,
17
+ integer!: StrictConvertInteger,
18
+ date!: StrictConvertDate,
19
+ datetime!: StrictConvertDatetime,
20
+ symbol!: StrictConvertSymbol,
21
+ downcase!: StrictConvertDowncase,
22
+ upcase!: StrictConvertUpcase,
23
+ boolean!: StrictConvertBoolean,
24
+ # safe variants
25
+ decimal: ConvertDecimal,
26
+ decimal_or_zero: ConvertDecimalOrZero,
27
+ integer: ConvertInteger,
28
+ integer_or_zero: ConvertIntegerOrZero,
29
+ date: ConvertDate,
30
+ datetime: ConvertDatetime,
31
+ symbol: ConvertSymbol,
32
+ downcase: ConvertDowncase,
33
+ upcase: ConvertUpcase,
34
+ boolean: ConvertBoolean,
35
+ md5: ConvertMD5,
36
+ hex: ConvertHex,
37
+ nil: ConvertNil,
38
+ blank: ConvertBlank,
39
+ header_column: ConvertHeaderColumn,
40
+ method_name: ConvertHeaderColumn,
41
+ }.freeze
42
+ end
12
43
  end
@@ -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
@@ -51,7 +51,8 @@ module HoneyFormat
51
51
  converter: header_converter,
52
52
  deduplicator: header_deduplicator
53
53
  )
54
- @rows = Rows.new(matrix, columns, builder: row_builder, type_map: type_map)
54
+ @type_map = type_map.select { |key, _v| @header.columns.include?(key) }.to_h
55
+ @rows = Rows.new(matrix, columns, builder: row_builder, type_map: @type_map)
55
56
  end
56
57
 
57
58
  # Original matrix header
@@ -66,6 +67,12 @@ module HoneyFormat
66
67
  @header.to_a
67
68
  end
68
69
 
70
+ # Matrix type map used
71
+ # @return [Hash<Symbol, Symbol>] the type map used.
72
+ def type_map
73
+ @type_map
74
+ end
75
+
69
76
  # Return rows
70
77
  # @return [Rows] rows.
71
78
  def rows
@@ -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')
@@ -16,10 +16,17 @@ module HoneyFormat
16
16
  # @raise [EmptyRowColumnsError] raised when there are no columns.
17
17
  # @raise [InvalidRowLengthError] raised when row has more columns than header columns.
18
18
  def initialize(rows, columns, builder: nil, type_map: {})
19
- builder = RowBuilder.new(columns, builder: builder, type_map: type_map)
19
+ @columns = columns
20
+ builder = RowBuilder.new(@columns, builder: builder, type_map: type_map)
20
21
  @rows = prepare_rows(builder, rows)
21
22
  end
22
23
 
24
+ # Row columns
25
+ # @return [Array<Symbol>] of column identifiers.
26
+ def columns
27
+ @columns
28
+ end
29
+
23
30
  # Returns true if rows contains no elements.
24
31
  # @return [true, false] true if rows contains no elements.
25
32
  def empty?
@@ -40,6 +47,12 @@ module HoneyFormat
40
47
  @rows
41
48
  end
42
49
 
50
+ # Return element at given position.
51
+ # @return [Row] of rows.
52
+ def [](index)
53
+ @rows[index]
54
+ end
55
+
43
56
  # Return the number of rows
44
57
  # @return [Integer] the number of rows
45
58
  def length
@@ -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 = 23,
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.23.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jacob Burenstam
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-12-08 00:00:00.000000000 Z
11
+ date: 2020-11-29 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
@@ -58,14 +64,14 @@ dependencies:
58
64
  requirements:
59
65
  - - "~>"
60
66
  - !ruby/object:Gem::Version
61
- version: '10.0'
67
+ version: '12.3'
62
68
  type: :development
63
69
  prerelease: false
64
70
  version_requirements: !ruby/object:Gem::Requirement
65
71
  requirements:
66
72
  - - "~>"
67
73
  - !ruby/object:Gem::Version
68
- version: '10.0'
74
+ version: '12.3'
69
75
  - !ruby/object:Gem::Dependency
70
76
  name: rspec
71
77
  requirement: !ruby/object:Gem::Requirement
@@ -147,7 +153,7 @@ homepage: https://github.com/buren/honey_format
147
153
  licenses:
148
154
  - MIT
149
155
  metadata: {}
150
- post_install_message:
156
+ post_install_message:
151
157
  rdoc_options: []
152
158
  require_paths:
153
159
  - lib
@@ -162,9 +168,8 @@ 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
167
- signing_key:
171
+ rubygems_version: 3.0.3
172
+ signing_key:
168
173
  specification_version: 4
169
174
  summary: Makes working with CSVs as smooth as honey.
170
175
  test_files: []