honey_format 0.19.0 → 0.23.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: 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: []