foraneus 0.0.12 → 0.0.13
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 +48 -16
- data/lib/foraneus.rb +39 -19
- data/lib/foraneus/converters/date.rb +4 -1
- data/lib/foraneus/converters/float.rb +2 -0
- data/lib/foraneus/converters/integer.rb +2 -0
- data/spec/lib/foraneus/converters/boolean_converter_spec.rb +10 -8
- data/spec/lib/foraneus/converters/date_converter_spec.rb +14 -18
- data/spec/lib/foraneus/converters/decimal_converter_spec.rb +16 -16
- data/spec/lib/foraneus/converters/float_converter_spec.rb +24 -22
- data/spec/lib/foraneus/converters/integer_converter_spec.rb +32 -29
- data/spec/lib/foraneus/converters/noop_converter_spec.rb +4 -2
- data/spec/lib/foraneus/converters/string_converter_spec.rb +6 -4
- data/spec/lib/foraneus_spec.rb +232 -141
- data/spec/runner.rb +16 -0
- data/spec/spec_helper.rb +4 -1
- data/spec/spec_helper_its.rb +42 -0
- metadata +38 -49
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: f3f668a304ebd112cf2651006dc871679cac85db
|
|
4
|
+
data.tar.gz: 607492a6bd176a001eca2f2f0971e2f6f6dca73f
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 1954c53b5f18b2099d55a2e4c0d92dc09cb6cfca491e0db191b220a997cb8abedd580392edcb70c8ab3d80363d97b938fee4d6e99531d2fe13436b0074eb1f8c
|
|
7
|
+
data.tar.gz: 8689562a31c5c06aade9b290f663539f0e8f042bddb1a71638b92a70f24944f853ee48fa3458b7310cfac8dc304b0d09db019023bded19bb9d6fd694b8d415f3
|
data/README.md
CHANGED
|
@@ -62,16 +62,16 @@ Declare source classes by inheriting from `Foraneus` base class.
|
|
|
62
62
|
end
|
|
63
63
|
```
|
|
64
64
|
|
|
65
|
-
Fields are declared
|
|
65
|
+
Fields are declared by two ways:
|
|
66
66
|
|
|
67
67
|
- calling `.field`
|
|
68
68
|
- calling a shortcut method, like `.float`
|
|
69
69
|
|
|
70
70
|
|
|
71
|
-
There are
|
|
71
|
+
There are shortcut methods for any of the built-in converters: boolean, date, decimal, float, integer,
|
|
72
72
|
noop, and string.
|
|
73
73
|
|
|
74
|
-
When no converter is passed to `.field`, Foraneus::Converters::Noop is assigned to the declared
|
|
74
|
+
When no converter is passed to `.field`, `Foraneus::Converters::Noop` is assigned to the declared
|
|
75
75
|
field.
|
|
76
76
|
|
|
77
77
|
## Instantiation
|
|
@@ -91,10 +91,10 @@ Converters have two interrelated responsibilities:
|
|
|
91
91
|
- Parse data, like the string `"3,000"`, into an object, `like 3_000`.
|
|
92
92
|
- Serialize data, like integer `3_000`, into string `"3,000"`
|
|
93
93
|
|
|
94
|
-
A converter is
|
|
94
|
+
A converter is an object that responds to `#parse(s)`, `#raw(v)`, and `#opts` methods.
|
|
95
95
|
|
|
96
96
|
When `#parse(s)` raises a StandardError exception, or any of its descendants, the exception is
|
|
97
|
-
rescued and a Foraneus::Error instance is added to `Foraneus#errors` map.
|
|
97
|
+
rescued and a `Foraneus::Error` instance is added to `Foraneus#errors` map.
|
|
98
98
|
|
|
99
99
|
`#opts` should return the opts hash used to instantiate the converter.
|
|
100
100
|
|
|
@@ -119,7 +119,7 @@ Valid instance:
|
|
|
119
119
|
|
|
120
120
|
``` ruby
|
|
121
121
|
form.valid? # => true
|
|
122
|
-
form
|
|
122
|
+
form.errors # => {}
|
|
123
123
|
```
|
|
124
124
|
|
|
125
125
|
Invalid one:
|
|
@@ -129,15 +129,15 @@ Invalid one:
|
|
|
129
129
|
|
|
130
130
|
form.valid? # => false
|
|
131
131
|
|
|
132
|
-
form
|
|
133
|
-
form
|
|
132
|
+
form.errors[:delay].key # => 'ArgumentError'
|
|
133
|
+
form.errors[:delay].message # => 'invalid value for Integer(): "INVALID"'
|
|
134
134
|
```
|
|
135
135
|
|
|
136
136
|
`#errors` is a map in which keys correspond to field names, and values are instances of
|
|
137
137
|
`Foraneus::Error`.
|
|
138
138
|
|
|
139
139
|
The name of the exception raised by `#parse` is the error's `key` attribute, and the exception's
|
|
140
|
-
message is
|
|
140
|
+
message is set to the error's `message` attribute.
|
|
141
141
|
|
|
142
142
|
Data coming from the inside is assumed to be valid, so `.raw` won't return an instance having
|
|
143
143
|
errors neither being invalid.
|
|
@@ -152,15 +152,15 @@ Fields can be declared as required.
|
|
|
152
152
|
end
|
|
153
153
|
```
|
|
154
154
|
|
|
155
|
-
If an external value is not fed into a required field, an error with key
|
|
155
|
+
If an external value is not fed into a required field, an error with key `KeyError` will be assigned.
|
|
156
156
|
|
|
157
157
|
``` ruby
|
|
158
158
|
form = MyForm.parse
|
|
159
159
|
|
|
160
160
|
form.valid? # => false
|
|
161
161
|
|
|
162
|
-
form
|
|
163
|
-
form
|
|
162
|
+
form.errors[:delay].key # => 'KeyError'
|
|
163
|
+
form.errors[:delay].message # => 'required parameter not found: "delay"'
|
|
164
164
|
```
|
|
165
165
|
|
|
166
166
|
## Blank values
|
|
@@ -197,7 +197,8 @@ Parse data from the ouside:
|
|
|
197
197
|
form = MyForm.parse
|
|
198
198
|
|
|
199
199
|
form.name # => 'Alice'
|
|
200
|
-
form[:name] # => nil, data from the outside
|
|
200
|
+
form[:name] # => nil, because data from the outside
|
|
201
|
+
# don't include any value
|
|
201
202
|
```
|
|
202
203
|
|
|
203
204
|
Convert values back from the inside:
|
|
@@ -206,7 +207,32 @@ Convert values back from the inside:
|
|
|
206
207
|
form = MyForm.raw
|
|
207
208
|
|
|
208
209
|
form[:name] # => 'Alice'
|
|
209
|
-
form.name # => nil, data from the inside
|
|
210
|
+
form.name # => nil, because data from the inside
|
|
211
|
+
# don't include any value
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
## Prevent name clashes
|
|
215
|
+
|
|
216
|
+
It is possible to rename methods `#errors` and `#data` so it will not conflict with defined fields.
|
|
217
|
+
|
|
218
|
+
``` ruby
|
|
219
|
+
MyForm = Class.new(Foraneus) {
|
|
220
|
+
field :errors
|
|
221
|
+
field :data
|
|
222
|
+
|
|
223
|
+
accessors[:errors] = :non_clashing_errors
|
|
224
|
+
accessors[:data] = :non_clashing_data
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
``` ruby
|
|
229
|
+
form = MyForm.parse(:errors => 'some errors', :data => 'some data')
|
|
230
|
+
|
|
231
|
+
form.errors # => 'some errors'
|
|
232
|
+
form.data # => 'some data'
|
|
233
|
+
|
|
234
|
+
form.non_clashing_errors # []
|
|
235
|
+
form.non_clashing_data # { :errors => 'some errors', :data => 'some data' }
|
|
210
236
|
```
|
|
211
237
|
|
|
212
238
|
## Installation
|
|
@@ -219,10 +245,16 @@ Convert values back from the inside:
|
|
|
219
245
|
|
|
220
246
|
## Running tests
|
|
221
247
|
|
|
222
|
-
Tests are written in
|
|
248
|
+
Tests are written in MiniTest. To run them all just execute the following from your command line:
|
|
249
|
+
|
|
250
|
+
``` shell
|
|
251
|
+
ruby spec/runner.rb
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
To run a specific test case:
|
|
223
255
|
|
|
224
256
|
``` shell
|
|
225
|
-
|
|
257
|
+
ruby -Ispec -Ilib spec/lib/foraneus_spec.rb
|
|
226
258
|
```
|
|
227
259
|
|
|
228
260
|
## Code documentation
|
data/lib/foraneus.rb
CHANGED
|
@@ -11,15 +11,9 @@ require_relative 'foraneus/errors'
|
|
|
11
11
|
class Foraneus
|
|
12
12
|
|
|
13
13
|
|
|
14
|
-
# @return [Hash] Parsed data.
|
|
15
|
-
attr_accessor :data
|
|
16
|
-
|
|
17
14
|
# @api private
|
|
18
15
|
def initialize
|
|
19
|
-
@
|
|
20
|
-
@raw_data = {}
|
|
21
|
-
|
|
22
|
-
@errors = {}
|
|
16
|
+
@_ = {}
|
|
23
17
|
end
|
|
24
18
|
|
|
25
19
|
# Declares a boolean field.
|
|
@@ -102,13 +96,38 @@ class Foraneus
|
|
|
102
96
|
@fields ||= {}
|
|
103
97
|
end
|
|
104
98
|
|
|
99
|
+
def self.accessors
|
|
100
|
+
@accessors ||= {
|
|
101
|
+
:data => :data,
|
|
102
|
+
:errors => :errors
|
|
103
|
+
}
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def self.create_instance
|
|
107
|
+
instance = self.new
|
|
108
|
+
spec = self
|
|
109
|
+
|
|
110
|
+
instance.singleton_class.send(:attr_reader, self.accessors[:data])
|
|
111
|
+
|
|
112
|
+
instance.instance_exec do
|
|
113
|
+
instance.instance_variable_set(:"@#{spec.accessors[:data]}", {})
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
instance.singleton_class.send(:attr_reader, self.accessors[:errors])
|
|
117
|
+
instance.instance_exec do
|
|
118
|
+
instance.instance_variable_set(:"@#{spec.accessors[:errors]}", {})
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
instance
|
|
122
|
+
end
|
|
123
|
+
|
|
105
124
|
# Parses data coming from an external source.
|
|
106
125
|
#
|
|
107
126
|
# @param [Hash<Symbol, String>] data External data.
|
|
108
127
|
#
|
|
109
128
|
# @return [Foraneus] An instance of a form.
|
|
110
129
|
def self.parse(data = {})
|
|
111
|
-
instance = self.
|
|
130
|
+
instance = self.create_instance
|
|
112
131
|
|
|
113
132
|
parsed_keys = []
|
|
114
133
|
|
|
@@ -138,7 +157,7 @@ class Foraneus
|
|
|
138
157
|
#
|
|
139
158
|
# @return [Foraneus] An instance of a form.
|
|
140
159
|
def self.raw(data = {})
|
|
141
|
-
instance = self.
|
|
160
|
+
instance = self.create_instance
|
|
142
161
|
|
|
143
162
|
fields.each do |field, converter|
|
|
144
163
|
given_key = field
|
|
@@ -161,7 +180,7 @@ class Foraneus
|
|
|
161
180
|
end
|
|
162
181
|
|
|
163
182
|
instance[given_key] = s
|
|
164
|
-
instance.data[given_key] = v
|
|
183
|
+
instance.send(self.accessors[:data])[given_key] = v
|
|
165
184
|
end
|
|
166
185
|
|
|
167
186
|
instance
|
|
@@ -171,13 +190,11 @@ class Foraneus
|
|
|
171
190
|
# @return [Array<Error>] errors when m == :errors.
|
|
172
191
|
# @return [String] raw data value for the field m.
|
|
173
192
|
def [](m = nil)
|
|
174
|
-
if m
|
|
175
|
-
@
|
|
176
|
-
elsif m.nil?
|
|
177
|
-
@raw_data
|
|
193
|
+
if m.nil?
|
|
194
|
+
@_
|
|
178
195
|
else
|
|
179
|
-
@
|
|
180
|
-
@
|
|
196
|
+
@_.fetch(m) do
|
|
197
|
+
@_[m.to_s]
|
|
181
198
|
end
|
|
182
199
|
end
|
|
183
200
|
end
|
|
@@ -189,7 +206,10 @@ class Foraneus
|
|
|
189
206
|
# @param [Symbol] k Field name.
|
|
190
207
|
# @param [String] v Raw value.
|
|
191
208
|
def []=(k, v)
|
|
192
|
-
|
|
209
|
+
#raw_data = @_
|
|
210
|
+
|
|
211
|
+
#raw_data[k] = v
|
|
212
|
+
@_[k] = v
|
|
193
213
|
end
|
|
194
214
|
|
|
195
215
|
# Returns true if no conversion errors occurred. false otherwise.
|
|
@@ -225,11 +245,11 @@ class Foraneus
|
|
|
225
245
|
end
|
|
226
246
|
|
|
227
247
|
foraneus.send("#{field}=", v)
|
|
228
|
-
foraneus.data[k] = v
|
|
248
|
+
foraneus.send(self.accessors[:data])[k] = v
|
|
229
249
|
|
|
230
250
|
rescue
|
|
231
251
|
error = Foraneus::Error.new($!.class.name, $!.message)
|
|
232
|
-
foraneus.
|
|
252
|
+
foraneus.send(self.accessors[:errors])[k] = error
|
|
233
253
|
end
|
|
234
254
|
private_class_method :__parse_raw_datum
|
|
235
255
|
|
|
@@ -2,35 +2,37 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe Foraneus::Converters::Boolean do
|
|
4
4
|
|
|
5
|
+
let(:converter) { Foraneus::Converters::Boolean.new }
|
|
6
|
+
|
|
5
7
|
describe '#parse' do
|
|
6
8
|
it 'returns true with true' do
|
|
7
|
-
parsed =
|
|
9
|
+
parsed = converter.parse('true')
|
|
8
10
|
|
|
9
|
-
parsed
|
|
11
|
+
assert_equal true, parsed
|
|
10
12
|
end
|
|
11
13
|
|
|
12
14
|
it 'returns false with sth else' do
|
|
13
|
-
parsed =
|
|
15
|
+
parsed = converter.parse('false')
|
|
14
16
|
|
|
15
|
-
parsed
|
|
17
|
+
assert_equal false, parsed
|
|
16
18
|
end
|
|
17
19
|
end
|
|
18
20
|
|
|
19
21
|
describe '#raw' do
|
|
20
22
|
it 'returns "true" with true' do
|
|
21
|
-
|
|
23
|
+
assert_equal 'true', converter.raw(true)
|
|
22
24
|
end
|
|
23
25
|
|
|
24
26
|
it 'returns "false" with false' do
|
|
25
|
-
|
|
27
|
+
assert_equal 'false', converter.raw(false)
|
|
26
28
|
end
|
|
27
29
|
|
|
28
30
|
it 'returns "false" with nil' do
|
|
29
|
-
|
|
31
|
+
assert_equal 'false', converter.raw(nil)
|
|
30
32
|
end
|
|
31
33
|
|
|
32
34
|
it 'returns "true" with everything else' do
|
|
33
|
-
|
|
35
|
+
assert_equal 'true', converter.raw(:default)
|
|
34
36
|
end
|
|
35
37
|
end
|
|
36
38
|
end
|
|
@@ -2,7 +2,7 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe Foraneus::Converters::Date do
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
let(:converter) { Foraneus::Converters::Date.new }
|
|
6
6
|
|
|
7
7
|
describe '#parse' do
|
|
8
8
|
|
|
@@ -11,13 +11,13 @@ describe Foraneus::Converters::Date do
|
|
|
11
11
|
|
|
12
12
|
result = converter.parse(s)
|
|
13
13
|
|
|
14
|
-
result.year
|
|
15
|
-
result.month
|
|
16
|
-
result.day
|
|
14
|
+
assert_equal 2012, result.year
|
|
15
|
+
assert_equal 4, result.month
|
|
16
|
+
assert_equal 13, result.day
|
|
17
17
|
end
|
|
18
18
|
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
describe 'when format is given' do
|
|
20
|
+
let(:converter) {
|
|
21
21
|
Foraneus::Converters::Date.new(:format => '%d/%m/%Y')
|
|
22
22
|
}
|
|
23
23
|
|
|
@@ -26,32 +26,28 @@ describe Foraneus::Converters::Date do
|
|
|
26
26
|
|
|
27
27
|
result = converter.parse(s)
|
|
28
28
|
|
|
29
|
-
result.year
|
|
30
|
-
result.month
|
|
31
|
-
result.day
|
|
29
|
+
assert_equal 2012, result.year
|
|
30
|
+
assert_equal 4, result.month
|
|
31
|
+
assert_equal 13, result.day
|
|
32
32
|
end
|
|
33
33
|
end
|
|
34
34
|
end
|
|
35
35
|
|
|
36
36
|
describe '#raw' do
|
|
37
|
-
let(:d) { Date.
|
|
37
|
+
let(:d) { Date.new(2012, 4, 13) }
|
|
38
38
|
|
|
39
39
|
it 'returns a date representation' do
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
converter.raw(d).should eq(s)
|
|
40
|
+
assert_equal '2012-04-13', converter.raw(d)
|
|
43
41
|
end
|
|
44
42
|
|
|
45
|
-
|
|
43
|
+
describe 'when format is given' do
|
|
46
44
|
let(:format) { '%m/%d/%Y' }
|
|
47
|
-
|
|
45
|
+
let(:converter) {
|
|
48
46
|
Foraneus::Converters::Date.new(:format => format)
|
|
49
47
|
}
|
|
50
48
|
|
|
51
49
|
it 'returns a date representation' do
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
converter.raw(d).should eq(s)
|
|
50
|
+
assert_equal '04/13/2012', converter.raw(d)
|
|
55
51
|
end
|
|
56
52
|
end
|
|
57
53
|
end
|
|
@@ -2,25 +2,25 @@ require 'spec_helper'
|
|
|
2
2
|
|
|
3
3
|
describe Foraneus::Converters::Decimal do
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
let(:converter) { Foraneus::Converters::Decimal.new }
|
|
6
6
|
|
|
7
7
|
describe '#parse' do
|
|
8
8
|
it 'parses a decimal representation' do
|
|
9
9
|
s = '1234.56'
|
|
10
10
|
n = BigDecimal.new('1234.56')
|
|
11
11
|
|
|
12
|
-
converter.parse(s)
|
|
12
|
+
assert_equal n, converter.parse(s)
|
|
13
13
|
end
|
|
14
14
|
|
|
15
15
|
it 'parses a decimal representation when no integer part' do
|
|
16
16
|
s = '.56'
|
|
17
17
|
n = BigDecimal.new('0.56')
|
|
18
18
|
|
|
19
|
-
converter.parse(s)
|
|
19
|
+
assert_equal n, converter.parse(s)
|
|
20
20
|
end
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
describe 'when separator and delimiter are given' do
|
|
23
|
+
let(:converter) {
|
|
24
24
|
Foraneus::Converters::Decimal.new(:delimiter => '.', :separator => ',')
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -28,14 +28,14 @@ describe Foraneus::Converters::Decimal do
|
|
|
28
28
|
s = '1.234.567,89'
|
|
29
29
|
n = BigDecimal.new('1234567.89')
|
|
30
30
|
|
|
31
|
-
converter.parse(s)
|
|
31
|
+
assert_equal n, converter.parse(s)
|
|
32
32
|
end
|
|
33
33
|
|
|
34
34
|
it 'parses a decimal representation when no integer part' do
|
|
35
35
|
s = ',56'
|
|
36
36
|
n = BigDecimal.new('0.56')
|
|
37
37
|
|
|
38
|
-
converter.parse(s)
|
|
38
|
+
assert_equal n, converter.parse(s)
|
|
39
39
|
end
|
|
40
40
|
end
|
|
41
41
|
end
|
|
@@ -46,39 +46,39 @@ describe Foraneus::Converters::Decimal do
|
|
|
46
46
|
it 'returns a decimal representation' do
|
|
47
47
|
s = '1234567.89'
|
|
48
48
|
|
|
49
|
-
converter.raw(n)
|
|
49
|
+
assert_equal s, converter.raw(n)
|
|
50
50
|
end
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
|
|
52
|
+
describe 'when separator and delimiter are given' do
|
|
53
|
+
let(:converter) {
|
|
54
54
|
Foraneus::Converters::Decimal.new(:delimiter => '.', :separator => ',')
|
|
55
55
|
}
|
|
56
56
|
|
|
57
57
|
it 'returns a decimal representation' do
|
|
58
58
|
s = '1.234.567,89'
|
|
59
59
|
|
|
60
|
-
converter.raw(n)
|
|
60
|
+
assert_equal s, converter.raw(n)
|
|
61
61
|
end
|
|
62
62
|
end
|
|
63
63
|
|
|
64
|
-
|
|
65
|
-
|
|
64
|
+
describe 'when precision is given' do
|
|
65
|
+
let(:converter) {
|
|
66
66
|
Foraneus::Converters::Decimal.new(:precision => 2)
|
|
67
67
|
}
|
|
68
68
|
|
|
69
69
|
it 'fills with zeros when value precision is smaller than converter precision' do
|
|
70
70
|
n = BigDecimal.new('3.1')
|
|
71
|
-
|
|
71
|
+
assert_equal '3.10', converter.raw(n)
|
|
72
72
|
end
|
|
73
73
|
|
|
74
74
|
it 'does not affect the representation when precision and converter precision are both equal' do
|
|
75
75
|
n = BigDecimal.new('3.14')
|
|
76
|
-
|
|
76
|
+
assert_equal '3.14', converter.raw(n)
|
|
77
77
|
end
|
|
78
78
|
|
|
79
79
|
it 'does not truncate the representation when precision is larger than converter precision' do
|
|
80
80
|
n = BigDecimal.new('3.145')
|
|
81
|
-
|
|
81
|
+
assert_equal '3.145', converter.raw(n)
|
|
82
82
|
end
|
|
83
83
|
end
|
|
84
84
|
end
|