strong_csv 0.1.0 → 0.2.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 +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
|