strong_csv 0.3.0 → 0.6.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: a367df39ccd195cbb7f62845c919f9718a4ef536de595294991fa7bc26693ca7
4
- data.tar.gz: 220e1d69c32123c78d5a31cb63d2c9c21686bbe76feb68e4f0aeb9762601e0e3
3
+ metadata.gz: 109d3d3c31812aedff005dc2003a8066d20a551513f0d287c0ba761a9469ac04
4
+ data.tar.gz: 11a3cd4890a60b27231276d09bcdc05fb29487c100fdbc6afff8cc7febf58694
5
5
  SHA512:
6
- metadata.gz: bfe0ecbb668a07be731f9f50143812fc42d213993b4e3591e04678a4c6323347f69f18a6241d4dbfa524faa37baada63c7ffc0ab394073c1f435c914910f5d95
7
- data.tar.gz: d345d0c12929b60d801c16e8d994fa1570f0e5fc2034f6d74de3f96bf188a7a0e85f7b9c2b1bbe61a1a0f4e4cdb4a41deddf7915cbeb540ace4b0c790c5056b0
6
+ metadata.gz: 6dd9abc5eb9a9b30f655ed13eae0e699c0471b09196ba5bc8ef265e06056ecf9bce526308f6a1edb28ff7d57ae9e5023e52b9e6bc827021cf186a8a62108026a
7
+ data.tar.gz: 3e9d411ab432f498639c8532b46be1d52e734188c48089b565a32398b690e8ceed1255b40adcc80ebc3d21256782f186576d7dd5cf8346f88b43fa6c2489ebfd
data/README.md CHANGED
@@ -2,8 +2,6 @@
2
2
 
3
3
  <a href="https://rubygems.org/gems/strong_csv"><img alt="strong_csv" src="https://img.shields.io/gem/v/strong_csv"></a>
4
4
 
5
- NOTE: This repository is still under development 🚧🚜🚧
6
-
7
5
  Type checker for a CSV file inspired by [strong_json](https://github.com/soutaro/strong_json).
8
6
 
9
7
  ## Motivation
@@ -78,8 +76,6 @@ strong_csv = StrongCSV.new do
78
76
  end
79
77
  end
80
78
 
81
- # TODO: The followings are not implemented so far.
82
-
83
79
  # Regular expressions
84
80
  let :url, %r{\Ahttps://}
85
81
 
@@ -105,12 +101,30 @@ strong_csv.parse(data, field_size_limit: 2048) do |row|
105
101
  row[:active] # => true
106
102
  # do something with row
107
103
  else
108
- row.errors # => { user_id: ["must be present", "must be an integer"] }
104
+ row.errors # => { user_id: ["`nil` can't be casted to Integer"] }
109
105
  # do something with row.errors
110
106
  end
111
107
  end
112
108
  ```
113
109
 
110
+ You can also define types without CSV headers by specifying column numbers.
111
+ Note the numbers must start from `0` (zero-based index).
112
+
113
+ ```ruby
114
+ StrongCSV.new do
115
+ let 0, integer
116
+ let 1, string
117
+ let 2, 1..10
118
+ end
119
+ ```
120
+
121
+ This declaration expects a CSV has the contents like this:
122
+
123
+ ```csv
124
+ 123,abc,3
125
+ 830,mno,10
126
+ ```
127
+
114
128
  ## Available types
115
129
 
116
130
  <table>
@@ -158,6 +172,10 @@ end
158
172
  <td><a href="#literal"><code>"abc"</code> (String literal)</a></td>
159
173
  <td>The value must be casted to the specific <code>String</code> literal.</td>
160
174
  </tr>
175
+ <tr>
176
+ <td><a href="#literal"><code>%r{\Ahttps://}</code> (Regexp literal)</a></td>
177
+ <td>The value must be casted to a <code>String</code> that matches the specified Regexp.</td>
178
+ </tr>
161
179
  <tr>
162
180
  <td><a href="#union"><code>,</code> (Union type)</a></td>
163
181
  <td>The value must satisfy one of the subtypes.</td>
@@ -167,7 +185,7 @@ end
167
185
  ### `integer` and `integer?`
168
186
 
169
187
  The value must be casted to Integer. `integer?` allows the value to be `nil`, so you can declare optional integer type
170
- for columns.
188
+ for columns. It also lets you allow values that satisfy the specified limitation through `:constraint`.
171
189
 
172
190
  _Example_
173
191
 
@@ -175,25 +193,30 @@ _Example_
175
193
  strong_csv = StrongCSV.new do
176
194
  let :stock, integer
177
195
  let :state, integer?
196
+ let :user_id, integer(constraint: ->(v) { user_ids.include?(v)})
197
+ pick :user_id, as: :user_ids do |values|
198
+ User.where(id: values).ids
199
+ end
178
200
  end
179
201
 
180
202
  result = strong_csv.parse(<<~CSV)
181
- stock,state
182
- 12,0
183
- 20,
184
- non-integer,1
203
+ stock,state,user_id
204
+ 12,0,1
205
+ 20,,2
206
+ non-integer,1,4
185
207
  CSV
186
208
 
187
209
  result.map(&:valid?) # => [true, true, false]
188
- result[0].slice(:stock, :state) # => {:stock=>12, :state=>0}
189
- result[1].slice(:stock, :state) # => {:stock=>20, :state=>nil}
190
- result[2].slice(:stock, :state) # => {:stock=>"non-integer", :state=>1} ("non-integer" cannot be casted to Integer)
210
+ result[0].slice(:stock, :state, :user_id) # => {:stock=>12, :state=>0, :user_id=>1}
211
+ result[1].slice(:stock, :state, :user_id) # => {:stock=>20, :state=>nil, :user_id=>2}
212
+ result[2].slice(:stock, :state, :user_id) # => {:stock=>"non-integer", :state=>1, :user_id=>"4"}
213
+ result[2].errors.slice(:stock, :user_id) # => {:stock=>["`\"non-integer\"` can't be casted to Integer"], :user_id=>["`\"4\"` does not satisfy the specified constraint"]}
191
214
  ```
192
215
 
193
216
  ### `float` and `float?`
194
217
 
195
218
  The value must be casted to Float. `float?` allows the value to be `nil`, so you can declare optional float type for
196
- columns.
219
+ columns. It also lets you allow values that satisfy the specified limitation through `:constraint`.
197
220
 
198
221
  _Example_
199
222
 
@@ -338,18 +361,19 @@ strong_csv = StrongCSV.new do
338
361
  let 1, "test"
339
362
  let 2, 2.5
340
363
  let 3, 1..10
364
+ let 4, /[a-z]+/
341
365
  end
342
366
 
343
367
  result = strong_csv.parse(<<~CSV)
344
- 123,test,2.5,9
345
- 123,test,2.5,0
346
- 123,Hey,2.5,10
368
+ 123,test,2.5,9,abc
369
+ 123,test,2.5,0,xyz
370
+ 123,Hey,2.5,10,!
347
371
  CSV
348
372
 
349
373
  result.map(&:valid?) # => [true, false, false]
350
- result[0].slice(0, 1, 2, 3) # => {0=>123, 1=>"test", 2=>2.5, 3=>9}
351
- result[1].slice(0, 1, 2, 3) # => {0=>123, 1=>"test", 2=>2.5, 3=>"0"} (0 is out of 1..10)
352
- result[2].slice(0, 1, 2, 3) # => {0=>123, 1=>"Hey", 2=>2.5, 3=>10} ("Hey" is not equal to "test")
374
+ result[0].slice(0, 1, 2, 3, 4) # => {0=>123, 1=>"test", 2=>2.5, 3=>9, 4=>"abc"}
375
+ result[1].slice(0, 1, 2, 3, 4) # => {0=>123, 1=>"test", 2=>2.5, 3=>"0", 4=>"xyz"} (0 is out of 1..10)
376
+ result[2].slice(0, 1, 2, 3, 4) # => {0=>123, 1=>"Hey", 2=>2.5, 3=>10, 4=>"!"} ("Hey" is not equal to "test", and "!" does not match /[a-z]+/)
353
377
  ```
354
378
 
355
379
  ### Union
@@ -391,8 +415,10 @@ ja:
391
415
  cant_be_casted: "`%{value}`はBooleanに変換できません"
392
416
  float:
393
417
  cant_be_casted: "`%{value}`はFloatに変換できません"
418
+ constraint_error: "`%{value}`は指定された成約を満たしていません",
394
419
  integer:
395
420
  cant_be_casted: "`%{value}`はIntegerに変換できません"
421
+ constraint_error: "`%{value}`は指定された成約を満たしていません",
396
422
  literal:
397
423
  integer:
398
424
  unexpected: "`%{expected}`ではなく`%{value}`が入力されています"
@@ -405,6 +431,9 @@ ja:
405
431
  range:
406
432
  cant_be_casted: "`%{value}`は`%{expected}`の始端に変換できません"
407
433
  out_of_range: "`%{value}`は`%{range}`の範囲外です"
434
+ regexp:
435
+ cant_be_casted: "`%{value}`はStringに変換できません"
436
+ unexpected: "`%{value}`は`%{expected}`とマッチしませんでした"
408
437
  string:
409
438
  cant_be_casted: "`%{value}`はStringに変換できません"
410
439
  out_of_range: "`%{value}`の文字数は`%{range}`の範囲外です"
@@ -8,9 +8,11 @@ I18n.backend.store_translations(
8
8
  },
9
9
  float: {
10
10
  cant_be_casted: "`%{value}` can't be casted to Float",
11
+ constraint_error: "`%{value}` does not satisfy the specified constraint",
11
12
  },
12
13
  integer: {
13
14
  cant_be_casted: "`%{value}` can't be casted to Integer",
15
+ constraint_error: "`%{value}` does not satisfy the specified constraint",
14
16
  },
15
17
  literal: {
16
18
  integer: {
@@ -28,6 +30,10 @@ I18n.backend.store_translations(
28
30
  cant_be_casted: "`%{value}` can't be casted to the beginning of `%{expected}`",
29
31
  out_of_range: "`%{value}` is not within `%{range}`",
30
32
  },
33
+ regexp: {
34
+ cant_be_casted: "`%{value}` can't be casted to String",
35
+ unexpected: "`%{value}` did not match `%{expected}`",
36
+ },
31
37
  },
32
38
  string: {
33
39
  cant_be_casted: "`%{value}` can't be casted to String",
@@ -9,9 +9,14 @@ class StrongCSV
9
9
  # @return [Boolean]
10
10
  attr_reader :headers
11
11
 
12
+ # @return [Hash]
13
+ attr_reader :pickers
14
+
12
15
  def initialize
13
16
  @types = {}
14
17
  @headers = false
18
+ @pickers = {}
19
+ @picked = {}
15
20
  end
16
21
 
17
22
  # @param name [String, Symbol, Integer]
@@ -30,12 +35,35 @@ class StrongCSV
30
35
  validate_columns
31
36
  end
32
37
 
33
- def integer
34
- Types::Integer.new
38
+ # #pick is intended for defining a singleton method with `:as`.
39
+ # This might be useful for the case where you want to receive IDs that are stored in a database,
40
+ # and you want to make sure the IDs actually exist.
41
+ #
42
+ # @example
43
+ # pick :user_id, as: :user_ids do |user_ids|
44
+ # User.where(id: user_ids).ids
45
+ # end
46
+ #
47
+ # @param column [Symbol, Integer]
48
+ # @param as [Symbol]
49
+ # @yieldparam values [Array<String>] The values for the column. NOTE: This is an array of String, not casted values.
50
+ def pick(column, as:, &block)
51
+ define_singleton_method(as) do
52
+ @picked[as]
53
+ end
54
+ @pickers[as] = lambda do |csv|
55
+ @picked[as] = block.call(csv.map { |row| row[column] })
56
+ end
35
57
  end
36
58
 
37
- def integer?
38
- optional(integer)
59
+ # @param options [Hash] See `Types::Integer#initialize` for more details.
60
+ def integer(**options)
61
+ Types::Integer.new(**options)
62
+ end
63
+
64
+ # @param options [Hash] See `Types::Integer#initialize` for more details.
65
+ def integer?(**options)
66
+ optional(integer(**options))
39
67
  end
40
68
 
41
69
  def boolean
@@ -46,12 +74,14 @@ class StrongCSV
46
74
  optional(boolean)
47
75
  end
48
76
 
49
- def float
50
- Types::Float.new
77
+ # @param options [Hash] See `Types::Float#initialize` for more details.
78
+ def float(**options)
79
+ Types::Float.new(**options)
51
80
  end
52
81
 
53
- def float?
54
- optional(float)
82
+ # @param options [Hash] See `Types::Float#initialize` for more details.
83
+ def float?(**options)
84
+ optional(float(**options))
55
85
  end
56
86
 
57
87
  # @param options [Hash] See `Types::String#initialize` for more details.
@@ -59,6 +89,7 @@ class StrongCSV
59
89
  Types::String.new(**options)
60
90
  end
61
91
 
92
+ # @param options [Hash] See `Types::String#initialize` for more details.
62
93
  def string?(**options)
63
94
  optional(string(**options))
64
95
  end
@@ -68,6 +99,7 @@ class StrongCSV
68
99
  Types::Time.new(**options)
69
100
  end
70
101
 
102
+ # @param options [Hash] See `Types::Time#initialize` for more details.
71
103
  def time?(**options)
72
104
  optional(time(**options))
73
105
  end
@@ -28,6 +28,9 @@ class StrongCSV
28
28
  end
29
29
  end
30
30
 
31
+ # It returns true if the row has no errors.
32
+ #
33
+ # @return [Boolean]
31
34
  def valid?
32
35
  @errors.empty?
33
36
  end
@@ -4,11 +4,25 @@ class StrongCSV
4
4
  module Types
5
5
  # Float type
6
6
  class Float < Base
7
+ DEFAULT_CONSTRAINT = ->(_) { true }
8
+ private_constant :DEFAULT_CONSTRAINT
9
+
10
+ # @param constraint [Proc]
11
+ def initialize(constraint: DEFAULT_CONSTRAINT)
12
+ super()
13
+ @constraint = constraint
14
+ end
15
+
7
16
  # @todo Use :exception for Float after we drop the support of Ruby 2.5
8
17
  # @param value [Object] Value to be casted to Float
9
18
  # @return [ValueResult]
10
19
  def cast(value)
11
- ValueResult.new(value: Float(value), original_value: value)
20
+ float = Float(value)
21
+ if @constraint.call(float)
22
+ ValueResult.new(value: float, original_value: value)
23
+ else
24
+ ValueResult.new(original_value: value, error_messages: [I18n.t("strong_csv.float.constraint_error", value: value.inspect, default: :"_strong_csv.float.constraint_error")])
25
+ end
12
26
  rescue ArgumentError, TypeError
13
27
  ValueResult.new(original_value: value, error_messages: [I18n.t("strong_csv.float.cant_be_casted", value: value.inspect, default: :"_strong_csv.float.cant_be_casted")])
14
28
  end
@@ -4,11 +4,25 @@ class StrongCSV
4
4
  module Types
5
5
  # Integer type
6
6
  class Integer < Base
7
+ DEFAULT_CONSTRAINT = ->(_) { true }
8
+ private_constant :DEFAULT_CONSTRAINT
9
+
10
+ # @param constraint [Proc]
11
+ def initialize(constraint: DEFAULT_CONSTRAINT)
12
+ super()
13
+ @constraint = constraint
14
+ end
15
+
7
16
  # @todo Use :exception for Integer after we drop the support of Ruby 2.5
8
17
  # @param value [Object] Value to be casted to Integer
9
18
  # @return [ValueResult]
10
19
  def cast(value)
11
- ValueResult.new(value: Integer(value), original_value: value)
20
+ int = Integer(value)
21
+ if @constraint.call(int)
22
+ ValueResult.new(value: int, original_value: value)
23
+ else
24
+ ValueResult.new(original_value: value, error_messages: [I18n.t("strong_csv.integer.constraint_error", value: value.inspect, default: :"_strong_csv.integer.constraint_error")])
25
+ end
12
26
  rescue ArgumentError, TypeError
13
27
  ValueResult.new(original_value: value, error_messages: [I18n.t("strong_csv.integer.cant_be_casted", value: value.inspect, default: :"_strong_csv.integer.cant_be_casted")])
14
28
  end
@@ -72,6 +72,20 @@ class StrongCSV
72
72
  end
73
73
  end
74
74
  end
75
+
76
+ refine ::Regexp do
77
+ # @param value [Object] Value to be casted to String
78
+ # @return [ValueResult]
79
+ def cast(value)
80
+ return ValueResult.new(original_value: value, error_messages: [I18n.t("strong_csv.literal.regexp.cant_be_casted", value: value.inspect, default: :"_strong_csv.literal.regexp.cant_be_casted")]) if value.nil?
81
+
82
+ if self =~ value
83
+ ValueResult.new(value: value, original_value: value)
84
+ else
85
+ ValueResult.new(original_value: value, error_messages: [I18n.t("strong_csv.literal.regexp.unexpected", value: value.inspect, expected: inspect, default: :"_strong_csv.literal.regexp.unexpected")])
86
+ end
87
+ end
88
+ end
75
89
  end
76
90
  end
77
91
  end
@@ -1,7 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class StrongCSV
4
- # ValueResult represents a CSV field is valid or not and contains its casted value if it's valid.
4
+ # ValueResult represents whether a CSV field is valid or not, and contains its casted value if it's valid.
5
5
  class ValueResult
6
6
  DEFAULT_VALUE = Object.new
7
7
  private_constant :DEFAULT_VALUE
@@ -15,11 +15,13 @@ class StrongCSV
15
15
  @error_messages = error_messages
16
16
  end
17
17
 
18
- # @return [Object] The casted value if it's valid. Otherwise, returns the original value.
18
+ # @return [::Float, ::Integer, ::Object, ::String, ::Range, Boolean, ::Time, nil] The casted value if it's valid. Otherwise, returns the original value.
19
19
  def value
20
20
  success? ? @value : @original_value
21
21
  end
22
22
 
23
+ # It returns true if the value is successfully casted.
24
+ #
23
25
  # @return [Boolean]
24
26
  def success?
25
27
  @error_messages.nil?
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  class StrongCSV
4
- VERSION = "0.3.0"
4
+ VERSION = "0.6.0"
5
5
  end
data/lib/strong_csv.rb CHANGED
@@ -21,7 +21,7 @@ require_relative "strong_csv/types/union"
21
21
  require_relative "strong_csv/let"
22
22
  require_relative "strong_csv/row"
23
23
 
24
- # The top-level namespace for the strong_csv gem.
24
+ # StrongCSV is a library for parsing CSV contents with type checking.
25
25
  class StrongCSV
26
26
  class Error < StandardError; end
27
27
 
@@ -37,6 +37,12 @@ class StrongCSV
37
37
  options.delete(:nil_value)
38
38
  options = options.merge(headers: @let.headers, header_converters: :symbol)
39
39
  csv = CSV.new(csv, **options)
40
+
41
+ @let.pickers.each_value do |picker|
42
+ picker.call(csv)
43
+ csv.rewind
44
+ end
45
+
40
46
  if block_given?
41
47
  csv.each do |row|
42
48
  yield Row.new(row: row, types: @let.types, lineno: csv.lineno)
@@ -0,0 +1,163 @@
1
+ # TypeProf 0.21.2
2
+
3
+ type literals = ::Integer | ::Float | ::Range[untyped] | ::String | ::Regexp
4
+ type declarable = StrongCSV::Types::Base | literals
5
+ type casted = bool | ::Float | ::Integer | ::Range[untyped] | ::String | ::Regexp | ::Time
6
+ type column = ::Integer | ::Symbol
7
+
8
+ # Classes
9
+ class StrongCSV
10
+ VERSION: String
11
+
12
+ @let: Let
13
+
14
+ def initialize: -> void
15
+
16
+ def parse: (String | IO, **untyped) -> Array[Row]
17
+ | (String | IO, **untyped) { (Row) -> void } -> untyped
18
+
19
+ class ValueResult
20
+ DEFAULT_VALUE: Object
21
+
22
+ @value: (casted)?
23
+ @original_value: untyped
24
+
25
+ attr_reader error_messages: [::String]?
26
+
27
+ def initialize: (original_value: ::String | nil, ?value: (casted | ::Object)?, ?error_messages: [::String]?) -> void
28
+
29
+ def value: -> ((casted | ::Object | nil)?)
30
+
31
+ def success?: -> bool
32
+ end
33
+
34
+ module Types
35
+ class Base
36
+ def cast: (untyped _value) -> bot
37
+ end
38
+
39
+ class Boolean < Base
40
+ TRUE_VALUES: Set[::String]
41
+ FALSE_VALUES: Set[::String]
42
+
43
+ def cast: (::String | nil) -> ValueResult
44
+ end
45
+
46
+ class Float < Base
47
+ DEFAULT_CONSTRAINT: ^(::Float) -> true
48
+
49
+ @constraint: ^(::Float) -> boolish
50
+
51
+ def initialize: (?constraint: ^(::Float) -> boolish) -> void
52
+
53
+ def cast: (::String | nil) -> ValueResult
54
+ end
55
+
56
+ class Integer < Base
57
+ DEFAULT_CONSTRAINT: ^(::Integer) -> true
58
+
59
+ @constraint: ^(::Integer) -> boolish
60
+
61
+ def initialize: (?constraint: ^(::Integer) -> boolish) -> void
62
+
63
+ def cast: (::String | nil) -> ValueResult
64
+ end
65
+
66
+ module Literal
67
+ def cast: (untyped value) -> ValueResult
68
+ | (untyped value) -> ValueResult
69
+ | (untyped value) -> ValueResult
70
+ | (untyped value) -> ValueResult
71
+ | (untyped value) -> ValueResult
72
+ end
73
+
74
+ class Optional < Base
75
+ @type: Boolean | Float | Integer | String | Time | Literal
76
+
77
+ def initialize: (declarable) -> void
78
+
79
+ def cast: (::String | nil) -> ValueResult
80
+ end
81
+
82
+ class String < Base
83
+ @within: Range[untyped] | nil
84
+
85
+ def initialize: (?within: Range[untyped] | nil) -> void
86
+
87
+ def cast: (::String | nil) -> ValueResult
88
+ end
89
+
90
+ class Time < Base
91
+ @format: ::String
92
+
93
+ def initialize: (?format: ::String) -> void
94
+
95
+ def cast: (::String | nil) -> ValueResult
96
+ end
97
+
98
+ class Union < Base
99
+ @types: Array[untyped]
100
+
101
+ def initialize: (untyped `type`, *untyped types) -> void
102
+
103
+ def cast: (::String | nil) -> untyped
104
+ end
105
+ end
106
+
107
+ class Let
108
+ @picked: Hash[untyped, untyped]
109
+
110
+ attr_reader types: Hash[column, [declarable, ^(casted) -> untyped | nil]]
111
+ attr_reader headers: bool
112
+ attr_reader pickers: Hash[untyped, Proc]
113
+
114
+ def initialize: -> void
115
+
116
+ def let: (::String | column, declarable, *declarable) -> void
117
+ | (::String | column, declarable, *declarable) { (casted) -> untyped } -> void
118
+
119
+ def pick: (column, as: ::Symbol) { (Array[::String]) -> untyped } -> void
120
+
121
+ def integer: (**untyped) -> Types::Integer
122
+
123
+ def integer?: (**untyped) -> Types::Optional
124
+
125
+ def boolean: -> Types::Boolean
126
+
127
+ def boolean?: -> Types::Optional
128
+
129
+ def float: (**untyped) -> Types::Float
130
+
131
+ def float?: (**untyped) -> Types::Optional
132
+
133
+ def string: (**untyped) -> Types::String
134
+
135
+ def string?: (**untyped) -> Types::Optional
136
+
137
+ def time: (**untyped) -> Types::Time
138
+
139
+ def time?: (**untyped) -> Types::Optional
140
+
141
+ def optional: (*declarable) -> Types::Optional
142
+
143
+ private
144
+
145
+ def validate_columns: -> bool
146
+ end
147
+
148
+ class Row
149
+ extend Forwardable
150
+
151
+ @values: Hash[column, casted | ::Object | nil]
152
+
153
+ attr_reader errors: Hash[column, Array[::String]]
154
+ attr_reader lineno: ::Integer
155
+
156
+ def initialize: (row: Array[::String] | CSV::Row, types: Hash[column, [declarable, (^(casted | ::Object | nil) -> untyped | nil)]], lineno: ::Integer) -> void
157
+
158
+ def valid?: -> bool
159
+ end
160
+
161
+ class Error < StandardError
162
+ end
163
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: strong_csv
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yutaka Kamei
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2022-05-04 00:00:00.000000000 Z
11
+ date: 2022-07-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: i18n
@@ -55,6 +55,7 @@ files:
55
55
  - lib/strong_csv/types/union.rb
56
56
  - lib/strong_csv/value_result.rb
57
57
  - lib/strong_csv/version.rb
58
+ - sig/strong_csv.rbs
58
59
  homepage: https://github.com/yykamei/strong_csv
59
60
  licenses:
60
61
  - MIT