strong_csv 0.1.0 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/README.md +29 -10
- data/lib/strong_csv/let.rb +56 -6
- data/lib/strong_csv/row.rb +4 -3
- data/lib/strong_csv/types/boolean.rb +24 -0
- data/lib/strong_csv/types/float.rb +17 -0
- data/lib/strong_csv/types/integer.rb +3 -1
- data/lib/strong_csv/types/literal.rb +77 -0
- data/lib/strong_csv/types/optional.rb +22 -0
- data/lib/strong_csv/types/string.rb +29 -0
- data/lib/strong_csv/types/time.rb +26 -0
- data/lib/strong_csv/types/union.rb +27 -0
- data/lib/strong_csv/value_result.rb +5 -5
- data/lib/strong_csv/version.rb +1 -1
- data/lib/strong_csv.rb +12 -1
- metadata +10 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5c718425815c3296bf644f0206eb7f351a25565908eddf5ec9b5811c045a46ec
|
4
|
+
data.tar.gz: fb8912e844d913b06c026e46d0b78084f7f0b3e81738ef3cc48476d8e784d03d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cc0564f9036a150e8564ff3bd947d9e4d7197b25179d37ab1c0d9997c7517834c3c76d54b5e28a6c1c5631e5c64f2601989935052b21dd6d588fa7e521ec7c8d
|
7
|
+
data.tar.gz: 205d2e29e1512e1508328f724b63ec40cf1d138c8d3bf83e586f6f0d8d7730f2d8f5fae648a589650efaff1e6104c9cb56ae0b8771fb7098a34d34aa08c525ad
|
data/README.md
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
# strong_csv
|
2
2
|
|
3
|
+
<a href="https://rubygems.org/gems/strong_csv"><img alt="strong_csv" src="https://img.shields.io/gem/v/strong_csv"></a>
|
4
|
+
|
3
5
|
NOTE: This repository is still under development 🚧🚜🚧
|
4
6
|
|
5
7
|
Type checker for a CSV file inspired by [strong_json](https://github.com/soutaro/strong_json).
|
@@ -21,7 +23,7 @@ strong_json helps you to mitigate such a drudgery by letting you declare desired
|
|
21
23
|
Add this line to your application's Gemfile:
|
22
24
|
|
23
25
|
```ruby
|
24
|
-
gem
|
26
|
+
gem "strong_csv"
|
25
27
|
```
|
26
28
|
|
27
29
|
And then execute:
|
@@ -41,19 +43,21 @@ gem install strong_csv
|
|
41
43
|
TBD: This hasn't yet been implemented.
|
42
44
|
|
43
45
|
```ruby
|
46
|
+
require "strong_csv"
|
47
|
+
|
44
48
|
strong_csv = StrongCSV.new do
|
45
49
|
let :stock, integer
|
46
50
|
let :tax_rate, float
|
47
|
-
let :name, string(255)
|
48
|
-
let :description, string?(1000)
|
51
|
+
let :name, string(within: 1..255)
|
52
|
+
let :description, string?(within: 1..1000)
|
49
53
|
let :active, boolean
|
50
54
|
let :started_at, time?
|
51
55
|
let :data, any?
|
52
56
|
|
53
57
|
# Literal declaration
|
54
58
|
let :status, 0..6
|
55
|
-
let :priority, 10
|
56
|
-
let :size, "S"
|
59
|
+
let :priority, 10, 20, 30, 40, 50
|
60
|
+
let :size, "S", "M", "L" do |value|
|
57
61
|
case value
|
58
62
|
when "S"
|
59
63
|
1
|
@@ -75,7 +79,7 @@ strong_csv = StrongCSV.new do
|
|
75
79
|
pick :user_id, as: :user_ids do |ids|
|
76
80
|
User.where(id: ids).ids
|
77
81
|
end
|
78
|
-
let :user_id, integer {
|
82
|
+
let :user_id, integer(constraint: ->(i) { user_ids.include?(i) })
|
79
83
|
end
|
80
84
|
|
81
85
|
data = <<~CSV
|
@@ -89,7 +93,7 @@ strong_csv.parse(data, field_size_limit: 2048) do |row|
|
|
89
93
|
row[:active] # => true
|
90
94
|
# do something with row
|
91
95
|
else
|
92
|
-
row.errors # =>
|
96
|
+
row.errors # => { user_id: ["must be present", "must be an integer"] }
|
93
97
|
# do something with row.errors
|
94
98
|
end
|
95
99
|
end
|
@@ -97,9 +101,24 @@ end
|
|
97
101
|
|
98
102
|
## Available types
|
99
103
|
|
100
|
-
| Type
|
101
|
-
|
|
102
|
-
| integer
|
104
|
+
| Type | Arguments | Description | Example |
|
105
|
+
| ------------------------ | --------- | --------------------------------------------------------------------------------------- | ---------------------------------------------------------- |
|
106
|
+
| `integer` | | The value must be casted to Integer | `let :stock, integer` |
|
107
|
+
| `integer?` | | The value can be `nil`. If the value exists, it must satisfy `integer` constraint. | `let :stock, integer?` |
|
108
|
+
| `float` | | The value must be casted to Float | `let :rate, float` |
|
109
|
+
| `float?` | | The value can be `nil`. If the value exists, it must satisfy `float` constraint. | `let :rate, float?` |
|
110
|
+
| `boolean` | | The value must be casted to Boolean | `let :active, boolean` |
|
111
|
+
| `boolean?` | | The value can be `nil`. If the value exists, it must satisfy `boolean` constraint. | `let :active, boolean?` |
|
112
|
+
| `string` | `:within` | The value must be casted to String | `let :name, string(within: 1..255)` |
|
113
|
+
| `string?` | `:within` | The value can be `nil`. If the value exists, it must satisfy `string` constraint. | `let :name, string?(within: 1..255)` |
|
114
|
+
| `time` | `:format` | The value must be casted to Time | `let :started_at, time(format: "%Y-%m-%dT%%H:%M:%S")` |
|
115
|
+
| `time?` | `:format` | The value can be `nil`. If the value exists, it must satisfy `time` constraint. | `let :started_at, time?(format: "%Y-%m-%dT%%H:%M:%S")` |
|
116
|
+
| `optional` | `type` | The value can be `nil`. If the value exists, it must satisfy the given type constraint. | `let :foo, optional(123)` |
|
117
|
+
| `23` (Integer literal) | | The value must be casted to the specific Integer literal | `let :id, 3` |
|
118
|
+
| `15.12` (Float literal) | | The value must be casted to the specific Float literal | `let :id, 3.8` |
|
119
|
+
| `1..10` (Range literal) | | The value must be casted to the beginning of Range and be covered with it | `let :id, 10..30`, `let :id, 1.0..30`, `let :id, "a".."z"` |
|
120
|
+
| `"abc"` (String literal) | | The value must be casted to the specific String literal | `let :drink, "coffee"` |
|
121
|
+
| , (Union type) | | The value must satisfy one of the subtypes | `let :id, 1, 2, 3` |
|
103
122
|
|
104
123
|
## Contributing
|
105
124
|
|
data/lib/strong_csv/let.rb
CHANGED
@@ -3,7 +3,11 @@
|
|
3
3
|
class StrongCSV
|
4
4
|
# Let is a class that is used to define types for columns.
|
5
5
|
class Let
|
6
|
-
|
6
|
+
# @return [Hash{Symbol => [Types::Base, Proc]}]
|
7
|
+
attr_reader :types
|
8
|
+
|
9
|
+
# @return [Boolean]
|
10
|
+
attr_reader :headers
|
7
11
|
|
8
12
|
def initialize
|
9
13
|
@types = {}
|
@@ -11,12 +15,15 @@ class StrongCSV
|
|
11
15
|
end
|
12
16
|
|
13
17
|
# @param name [String, Symbol, Integer]
|
14
|
-
|
18
|
+
# @param type [StrongCSV::Type::Base]
|
19
|
+
# @param types [Array<StrongCSV::Type::Base>]
|
20
|
+
def let(name, type, *types, &block)
|
21
|
+
type = types.empty? ? type : Types::Union.new(type, *types)
|
15
22
|
case name
|
16
|
-
when Integer
|
17
|
-
@types[name] = type
|
18
|
-
when String, Symbol
|
19
|
-
@types[name.to_sym] = type
|
23
|
+
when ::Integer
|
24
|
+
@types[name] = [type, block]
|
25
|
+
when ::String, ::Symbol
|
26
|
+
@types[name.to_sym] = [type, block]
|
20
27
|
else
|
21
28
|
raise TypeError, "Invalid type specified for `name`. `name` must be String, Symbol, or Integer: #{name.inspect}"
|
22
29
|
end
|
@@ -27,6 +34,49 @@ class StrongCSV
|
|
27
34
|
Types::Integer.new
|
28
35
|
end
|
29
36
|
|
37
|
+
def integer?
|
38
|
+
optional(integer)
|
39
|
+
end
|
40
|
+
|
41
|
+
def boolean
|
42
|
+
Types::Boolean.new
|
43
|
+
end
|
44
|
+
|
45
|
+
def boolean?
|
46
|
+
optional(boolean)
|
47
|
+
end
|
48
|
+
|
49
|
+
def float
|
50
|
+
Types::Float.new
|
51
|
+
end
|
52
|
+
|
53
|
+
def float?
|
54
|
+
optional(float)
|
55
|
+
end
|
56
|
+
|
57
|
+
# @param options [Hash] See `Types::String#initialize` for more details.
|
58
|
+
def string(**options)
|
59
|
+
Types::String.new(**options)
|
60
|
+
end
|
61
|
+
|
62
|
+
def string?(**options)
|
63
|
+
optional(string(**options))
|
64
|
+
end
|
65
|
+
|
66
|
+
# @param options [Hash] See `Types::Time#initialize` for more details.
|
67
|
+
def time(**options)
|
68
|
+
Types::Time.new(**options)
|
69
|
+
end
|
70
|
+
|
71
|
+
def time?(**options)
|
72
|
+
optional(time(**options))
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param args [Array] See `Types::Optional#initialize` for more details.
|
76
|
+
def optional(*args)
|
77
|
+
Types::Optional.new(*args)
|
78
|
+
end
|
79
|
+
|
30
80
|
private
|
31
81
|
|
32
82
|
def validate_columns
|
data/lib/strong_csv/row.rb
CHANGED
@@ -4,6 +4,7 @@ class StrongCSV
|
|
4
4
|
# Row is a representation of a row in a CSV file, which has casted values with specified types.
|
5
5
|
class Row
|
6
6
|
extend Forwardable
|
7
|
+
using Types::Literal
|
7
8
|
|
8
9
|
def_delegators :@values, :[], :fetch
|
9
10
|
|
@@ -20,10 +21,10 @@ class StrongCSV
|
|
20
21
|
@values = {}
|
21
22
|
@errors = {}
|
22
23
|
@lineno = lineno
|
23
|
-
types.each do |key, type|
|
24
|
+
types.each do |key, (type, block)|
|
24
25
|
value_result = type.cast(row[key])
|
25
|
-
@values[key] = value_result.value
|
26
|
-
@errors[key] = value_result.
|
26
|
+
@values[key] = block && value_result.success? ? block.call(value_result.value) : value_result.value
|
27
|
+
@errors[key] = value_result.error_messages unless value_result.success?
|
27
28
|
end
|
28
29
|
end
|
29
30
|
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class StrongCSV
|
4
|
+
module Types
|
5
|
+
# Boolean type
|
6
|
+
class Boolean < Base
|
7
|
+
TRUE_VALUES = %w[true True TRUE].to_set.freeze
|
8
|
+
FALSE_VALUES = %w[false False FALSE].to_set.freeze
|
9
|
+
private_constant :TRUE_VALUES, :FALSE_VALUES
|
10
|
+
|
11
|
+
# @param value [Object] Value to be casted to Boolean
|
12
|
+
# @return [ValueResult]
|
13
|
+
def cast(value)
|
14
|
+
boolean = TRUE_VALUES.include?(value) ? true : nil
|
15
|
+
return ValueResult.new(value: boolean, original_value: value) unless boolean.nil?
|
16
|
+
|
17
|
+
boolean = FALSE_VALUES.include?(value) ? false : nil
|
18
|
+
return ValueResult.new(value: boolean, original_value: value) unless boolean.nil?
|
19
|
+
|
20
|
+
ValueResult.new(original_value: value, error_messages: ["`#{value.inspect}` can't be casted to Boolean"])
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class StrongCSV
|
4
|
+
module Types
|
5
|
+
# Float type
|
6
|
+
class Float < Base
|
7
|
+
# @todo Use :exception for Float after we drop the support of Ruby 2.5
|
8
|
+
# @param value [Object] Value to be casted to Float
|
9
|
+
# @return [ValueResult]
|
10
|
+
def cast(value)
|
11
|
+
ValueResult.new(value: Float(value), original_value: value)
|
12
|
+
rescue ArgumentError, TypeError
|
13
|
+
ValueResult.new(original_value: value, error_messages: ["`#{value.inspect}` can't be casted to Float"])
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -5,10 +5,12 @@ class StrongCSV
|
|
5
5
|
# Integer type
|
6
6
|
class Integer < Base
|
7
7
|
# @todo Use :exception for Integer after we drop the support of Ruby 2.5
|
8
|
+
# @param value [Object] Value to be casted to Integer
|
9
|
+
# @return [ValueResult]
|
8
10
|
def cast(value)
|
9
11
|
ValueResult.new(value: Integer(value), original_value: value)
|
10
12
|
rescue ArgumentError, TypeError
|
11
|
-
ValueResult.new(original_value: value,
|
13
|
+
ValueResult.new(original_value: value, error_messages: ["`#{value.inspect}` can't be casted to Integer"])
|
12
14
|
end
|
13
15
|
end
|
14
16
|
end
|
@@ -0,0 +1,77 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class StrongCSV
|
4
|
+
module Types
|
5
|
+
# Literal is a module to collect all the literal types for CSV parsing.
|
6
|
+
# This module is intended for refining built-in classes and provide `#cast` like `Base` class.
|
7
|
+
module Literal
|
8
|
+
refine ::Integer do
|
9
|
+
# @param value [Object] Value to be casted to Integer
|
10
|
+
# @return [ValueResult]
|
11
|
+
def cast(value)
|
12
|
+
int = Integer(value)
|
13
|
+
if int == self
|
14
|
+
ValueResult.new(value: int, original_value: value)
|
15
|
+
else
|
16
|
+
ValueResult.new(original_value: value, error_messages: ["`#{inspect}` is expected, but `#{int}` was given"])
|
17
|
+
end
|
18
|
+
rescue ArgumentError, TypeError
|
19
|
+
ValueResult.new(original_value: value, error_messages: ["`#{value.inspect}` can't be casted to #{self.class.name}"])
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
refine ::Float do
|
24
|
+
# @param value [Object] Value to be casted to Float
|
25
|
+
# @return [ValueResult]
|
26
|
+
def cast(value)
|
27
|
+
float = Float(value)
|
28
|
+
if float == self
|
29
|
+
ValueResult.new(value: float, original_value: value)
|
30
|
+
else
|
31
|
+
ValueResult.new(original_value: value, error_messages: ["`#{inspect}` is expected, but `#{float}` was given"])
|
32
|
+
end
|
33
|
+
rescue ArgumentError, TypeError
|
34
|
+
ValueResult.new(original_value: value, error_messages: ["`#{value.inspect}` can't be casted to #{self.class.name}"])
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
refine ::Range do
|
39
|
+
# @param value [Object] Value to be casted to Range
|
40
|
+
# @return [ValueResult]
|
41
|
+
def cast(value)
|
42
|
+
return ValueResult.new(original_value: value, error_messages: ["`nil` can't be casted to the beginning of `#{inspect}`"]) if value.nil?
|
43
|
+
|
44
|
+
casted = case self.begin
|
45
|
+
when ::Float
|
46
|
+
Float(value)
|
47
|
+
when ::Integer
|
48
|
+
Integer(value)
|
49
|
+
when ::String
|
50
|
+
value
|
51
|
+
else
|
52
|
+
raise TypeError, "#{self.begin.class} is not supported"
|
53
|
+
end
|
54
|
+
if cover?(casted)
|
55
|
+
ValueResult.new(value: casted, original_value: value)
|
56
|
+
else
|
57
|
+
ValueResult.new(original_value: value, error_messages: ["`#{casted.inspect}` is not within `#{inspect}`"])
|
58
|
+
end
|
59
|
+
rescue ArgumentError
|
60
|
+
ValueResult.new(original_value: value, error_messages: ["`#{value.inspect}` can't be casted to the beginning of `#{inspect}`"])
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
refine ::String do
|
65
|
+
# @param value [Object] Value to be casted to String
|
66
|
+
# @return [ValueResult]
|
67
|
+
def cast(value)
|
68
|
+
if self == value
|
69
|
+
ValueResult.new(value: self, original_value: value)
|
70
|
+
else
|
71
|
+
ValueResult.new(original_value: value, error_messages: ["`#{inspect}` is expected, but `#{value.inspect}` was given"])
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class StrongCSV
|
4
|
+
module Types
|
5
|
+
# Optional type
|
6
|
+
class Optional < Base
|
7
|
+
using Types::Literal
|
8
|
+
|
9
|
+
# @param type [Base]
|
10
|
+
def initialize(type)
|
11
|
+
super()
|
12
|
+
@type = type
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param value [Object]
|
16
|
+
# @return [ValueResult]
|
17
|
+
def cast(value)
|
18
|
+
value.nil? ? ValueResult.new(value: nil, original_value: value) : @type.cast(value)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class StrongCSV
|
4
|
+
module Types
|
5
|
+
# String type
|
6
|
+
class String < Base
|
7
|
+
# @param within [Range, nil]
|
8
|
+
def initialize(within: nil)
|
9
|
+
raise ArgumentError, "`within` must be a Range" unless within.nil? || within.is_a?(Range)
|
10
|
+
|
11
|
+
super()
|
12
|
+
@within = within
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param value [Object] Value to be casted to Boolean
|
16
|
+
# @return [ValueResult]
|
17
|
+
def cast(value)
|
18
|
+
return ValueResult.new(original_value: value, error_messages: ["`#{value.inspect}` can't be casted to String"]) if value.nil?
|
19
|
+
|
20
|
+
casted = String(value)
|
21
|
+
if @within && !@within.cover?(casted.size)
|
22
|
+
ValueResult.new(original_value: value, error_messages: ["`#{casted.inspect}` is out of range `#{@within.inspect}`"])
|
23
|
+
else
|
24
|
+
ValueResult.new(value: casted, original_value: value)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class StrongCSV
|
4
|
+
module Types
|
5
|
+
# Time type
|
6
|
+
class Time < Base
|
7
|
+
# @param format [String] The format of #strptime
|
8
|
+
def initialize(format: "%Y-%m-%d")
|
9
|
+
raise ArgumentError, "`format` must be a String" unless format.is_a?(::String)
|
10
|
+
|
11
|
+
super()
|
12
|
+
@format = format
|
13
|
+
end
|
14
|
+
|
15
|
+
# @param value [Object] Value to be casted to Time
|
16
|
+
# @return [ValueResult]
|
17
|
+
def cast(value)
|
18
|
+
return ValueResult.new(original_value: value, error_messages: ["`#{value.inspect}` can't be casted to Time"]) if value.nil?
|
19
|
+
|
20
|
+
ValueResult.new(value: ::Time.strptime(value.to_s, @format), original_value: value)
|
21
|
+
rescue ArgumentError
|
22
|
+
ValueResult.new(original_value: value, error_messages: ["`#{value.inspect}` can't be casted to Time with the format `#{@format}`"])
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class StrongCSV
|
4
|
+
module Types
|
5
|
+
# Union type is a type that combine multiple types.
|
6
|
+
class Union < Base
|
7
|
+
using Types::Literal
|
8
|
+
|
9
|
+
# @param type [Base]
|
10
|
+
# @param types [Array<Base>]
|
11
|
+
def initialize(type, *types)
|
12
|
+
super()
|
13
|
+
@types = [type, *types]
|
14
|
+
end
|
15
|
+
|
16
|
+
# @param value [Object] Value to be casted to Integer
|
17
|
+
# @return [ValueResult]
|
18
|
+
def cast(value)
|
19
|
+
results = @types.map { |type| type.cast(value) }
|
20
|
+
results.find(&:success?) || results.reduce do |memo, result|
|
21
|
+
memo.error_messages.concat(result.error_messages).uniq!
|
22
|
+
memo
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -6,13 +6,13 @@ class StrongCSV
|
|
6
6
|
DEFAULT_VALUE = Object.new
|
7
7
|
private_constant :DEFAULT_VALUE
|
8
8
|
|
9
|
-
# @return [String
|
10
|
-
attr_reader :
|
9
|
+
# @return [Array<String>, nil] The error messages for the field.
|
10
|
+
attr_reader :error_messages
|
11
11
|
|
12
|
-
def initialize(original_value:, value: DEFAULT_VALUE,
|
12
|
+
def initialize(original_value:, value: DEFAULT_VALUE, error_messages: nil)
|
13
13
|
@value = value
|
14
14
|
@original_value = original_value
|
15
|
-
@
|
15
|
+
@error_messages = error_messages
|
16
16
|
end
|
17
17
|
|
18
18
|
# @return [Object] The casted value if it's valid. Otherwise, returns the original value.
|
@@ -22,7 +22,7 @@ class StrongCSV
|
|
22
22
|
|
23
23
|
# @return [Boolean]
|
24
24
|
def success?
|
25
|
-
@
|
25
|
+
@error_messages.nil?
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
data/lib/strong_csv/version.rb
CHANGED
data/lib/strong_csv.rb
CHANGED
@@ -2,12 +2,21 @@
|
|
2
2
|
|
3
3
|
require "csv"
|
4
4
|
require "forwardable"
|
5
|
+
require "set"
|
6
|
+
require "time"
|
5
7
|
|
6
8
|
require_relative "strong_csv/version"
|
7
|
-
require_relative "strong_csv/let"
|
8
9
|
require_relative "strong_csv/value_result"
|
9
10
|
require_relative "strong_csv/types/base"
|
11
|
+
require_relative "strong_csv/types/boolean"
|
12
|
+
require_relative "strong_csv/types/float"
|
10
13
|
require_relative "strong_csv/types/integer"
|
14
|
+
require_relative "strong_csv/types/literal"
|
15
|
+
require_relative "strong_csv/types/optional"
|
16
|
+
require_relative "strong_csv/types/string"
|
17
|
+
require_relative "strong_csv/types/time"
|
18
|
+
require_relative "strong_csv/types/union"
|
19
|
+
require_relative "strong_csv/let"
|
11
20
|
require_relative "strong_csv/row"
|
12
21
|
|
13
22
|
# The top-level namespace for the strong_csv gem.
|
@@ -22,6 +31,8 @@ class StrongCSV
|
|
22
31
|
# @param csv [String, IO]
|
23
32
|
# @param options [Hash] CSV options for parsing.
|
24
33
|
def parse(csv, **options)
|
34
|
+
# NOTE: Some options are overridden here to ensure that StrongCSV can handle parsed values correctly.
|
35
|
+
options.delete(:nil_value)
|
25
36
|
options = options.merge(headers: @let.headers, header_converters: :symbol)
|
26
37
|
csv = CSV.new(csv, **options)
|
27
38
|
if block_given?
|
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.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Yutaka Kamei
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2022-04-
|
11
|
+
date: 2022-04-29 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: strong_csv is a type checker for a CSV file. It lets developers declare
|
14
14
|
types for each column to ensure all cells are satisfied with desired types.
|
@@ -24,7 +24,14 @@ files:
|
|
24
24
|
- lib/strong_csv/let.rb
|
25
25
|
- lib/strong_csv/row.rb
|
26
26
|
- lib/strong_csv/types/base.rb
|
27
|
+
- lib/strong_csv/types/boolean.rb
|
28
|
+
- lib/strong_csv/types/float.rb
|
27
29
|
- lib/strong_csv/types/integer.rb
|
30
|
+
- lib/strong_csv/types/literal.rb
|
31
|
+
- lib/strong_csv/types/optional.rb
|
32
|
+
- lib/strong_csv/types/string.rb
|
33
|
+
- lib/strong_csv/types/time.rb
|
34
|
+
- lib/strong_csv/types/union.rb
|
28
35
|
- lib/strong_csv/value_result.rb
|
29
36
|
- lib/strong_csv/version.rb
|
30
37
|
homepage: https://github.com/yykamei/strong_csv
|