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 +4 -4
- data/README.md +49 -20
- data/lib/strong_csv/i18n.rb +6 -0
- data/lib/strong_csv/let.rb +40 -8
- data/lib/strong_csv/row.rb +3 -0
- data/lib/strong_csv/types/float.rb +15 -1
- data/lib/strong_csv/types/integer.rb +15 -1
- data/lib/strong_csv/types/literal.rb +14 -0
- data/lib/strong_csv/value_result.rb +4 -2
- data/lib/strong_csv/version.rb +1 -1
- data/lib/strong_csv.rb +7 -1
- data/sig/strong_csv.rbs +163 -0
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 109d3d3c31812aedff005dc2003a8066d20a551513f0d287c0ba761a9469ac04
|
4
|
+
data.tar.gz: 11a3cd4890a60b27231276d09bcdc05fb29487c100fdbc6afff8cc7febf58694
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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: ["
|
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
|
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}`の範囲外です"
|
data/lib/strong_csv/i18n.rb
CHANGED
@@ -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",
|
data/lib/strong_csv/let.rb
CHANGED
@@ -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
|
-
|
34
|
-
|
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
|
-
|
38
|
-
|
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
|
-
|
50
|
-
|
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
|
-
|
54
|
-
|
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
|
data/lib/strong_csv/row.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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?
|
data/lib/strong_csv/version.rb
CHANGED
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
|
-
#
|
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)
|
data/sig/strong_csv.rbs
ADDED
@@ -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.
|
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-
|
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
|