strong_csv 0.3.0 → 0.6.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: 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