eapi 0.5.0 → 0.6.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 +100 -0
- data/lib/eapi/common.rb +5 -0
- data/lib/eapi/definition_runners/property.rb +4 -3
- data/lib/eapi/item.rb +9 -0
- data/lib/eapi/list.rb +12 -6
- data/lib/eapi/methods/properties.rb +39 -3
- data/lib/eapi/value_converter.rb +35 -8
- data/lib/eapi/version.rb +1 -1
- data/spec/conversion_spec.rb +117 -0
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 4673ef60bc4acd96d0ae165935fab2e1b901cf53
|
4
|
+
data.tar.gz: d04635f645f8638fdb8a224dc5ae3320e5fe90fd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 4da861d047452f5057a2cc1f778ea2073198afab91a926fd7cdc1c36e35222ca6f7e02aa547b5683099d21020d2d1171825562a66a94c47663316f2fcf65240d
|
7
|
+
data.tar.gz: 8d9f6a3d3d5a8e18a07cf03f22b40724acd98a9a736985bf01093e2204f77a633b7d79c3a2603c4ab1ce1e5378dd32d4be47d3d9c82333aed375f4ff86971203
|
data/README.md
CHANGED
@@ -88,6 +88,106 @@ By default, each property will be converted into a simple element (Array, Hash,
|
|
88
88
|
2. If a value is an Array or a Set, `to_a` will be invoked and all values will be converted in the same way.
|
89
89
|
3. If a value respond to `to_h`, it will be called.
|
90
90
|
|
91
|
+
#### Custom value conversion
|
92
|
+
|
93
|
+
We can override the default value conversion using the `convert_with` option in the property definition. It will accept:
|
94
|
+
|
95
|
+
* a symbol (message to be sent to the value)
|
96
|
+
* callable object (lambda, proc...) that accepts either 1 parameter (the value) or 2 parameters (the value and the object)
|
97
|
+
|
98
|
+
```ruby
|
99
|
+
class ExampleItem
|
100
|
+
include Eapi::Item
|
101
|
+
|
102
|
+
property :something, convert_with: :to_s
|
103
|
+
property :other, convert_with: ->(val) { "This is #{val}" }
|
104
|
+
property :third, convert_with: ->(val, obj) do
|
105
|
+
s = obj.something
|
106
|
+
c = obj.send :converted_value_for, :something
|
107
|
+
"I am #{val} with some #{s.inspect} as #{c.inspect}"
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
x = ExampleItem.new something: :x, other: 1, third: 'the third'
|
112
|
+
x.render # =>
|
113
|
+
# {
|
114
|
+
# # converted to string (`to_s` method called)
|
115
|
+
# something: 'x',
|
116
|
+
#
|
117
|
+
# # converted using the lambda
|
118
|
+
# other: 'This is 1',
|
119
|
+
#
|
120
|
+
# # converted using the lambda, with access to the context object
|
121
|
+
# third: "I am the third with some :x as \"x\""
|
122
|
+
# }
|
123
|
+
```
|
124
|
+
|
125
|
+
On a `List` object, it will work as conversion for every element.
|
126
|
+
|
127
|
+
```ruby
|
128
|
+
class ExampleList
|
129
|
+
include Eapi::List
|
130
|
+
|
131
|
+
elements convert_with: :to_s
|
132
|
+
end
|
133
|
+
|
134
|
+
x = ExampleItem.new.add(1).add(2)
|
135
|
+
x.render # => ["1", "2"]
|
136
|
+
```
|
137
|
+
|
138
|
+
#### Convert values before validation using `convert_before_validation` option
|
139
|
+
|
140
|
+
If `convert_before_validation` option is enabled, then the conversion will occur before validation
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
class ExampleItem
|
144
|
+
include Eapi::Item
|
145
|
+
|
146
|
+
property :something, convert_with: :to_i, convert_before_validation: true
|
147
|
+
property :normal, convert_with: :to_i
|
148
|
+
|
149
|
+
validates :something, inclusion: { in: [1, 2, 3] }
|
150
|
+
validates :normal, inclusion: { in: [1, 2, 3] }
|
151
|
+
end
|
152
|
+
|
153
|
+
# the conversion happens before validation, so it passes
|
154
|
+
x = ExampleItem.new something: '1', normal: 2
|
155
|
+
x.valid? # => true
|
156
|
+
|
157
|
+
# the conversion happens on rendering, after validation, so it fails
|
158
|
+
x = ExampleItem.new something: 1, normal: '2'
|
159
|
+
x.valid? # => false
|
160
|
+
```
|
161
|
+
|
162
|
+
In `List` it will work on elements.
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
class ExampleListConvertBeforeValidationEnabled
|
166
|
+
include Eapi::List
|
167
|
+
|
168
|
+
elements convert_with: :to_i, convert_before_validation: true, validate_with: ->(record, attr, value) do
|
169
|
+
record.errors.add(attr, 'must pass my custom validation') unless value.kind_of?(Fixnum)
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class ExampleListConvertBeforeValidationDisabled
|
174
|
+
include Eapi::List
|
175
|
+
|
176
|
+
elements convert_with: :to_i, validate_with: ->(record, attr, value) do
|
177
|
+
record.errors.add(attr, 'must pass my custom validation') unless value.kind_of?(Fixnum)
|
178
|
+
end
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
# the conversion happens before validation, so it passes
|
183
|
+
x = ExampleListConvertBeforeValidationEnabled.new.add('1')
|
184
|
+
x.valid? # => true
|
185
|
+
|
186
|
+
# the conversion happens on rendering, after validation, so it fails
|
187
|
+
x = ExampleListConvertBeforeValidationDisabled.new.add('1')
|
188
|
+
x.valid? # => false
|
189
|
+
```
|
190
|
+
|
91
191
|
#### Ignoring values
|
92
192
|
|
93
193
|
By default, any `nil` values will be omitted in the final structure by the `perform_render` method, in both `Item` and `List`.
|
data/lib/eapi/common.rb
CHANGED
@@ -94,6 +94,10 @@ module Eapi
|
|
94
94
|
definition.fetch(:required, false)
|
95
95
|
end
|
96
96
|
|
97
|
+
def allow_raw?
|
98
|
+
definition.fetch(:allow_raw, false)
|
99
|
+
end
|
100
|
+
|
97
101
|
def validate_with
|
98
102
|
definition.fetch(:validate_with, nil)
|
99
103
|
end
|
@@ -110,9 +114,6 @@ module Eapi
|
|
110
114
|
definition.fetch(:init_class, nil)
|
111
115
|
end
|
112
116
|
|
113
|
-
def allow_raw?
|
114
|
-
definition.fetch(:allow_raw, false)
|
115
|
-
end
|
116
117
|
end
|
117
118
|
end
|
118
119
|
end
|
data/lib/eapi/item.rb
CHANGED
@@ -16,6 +16,7 @@ module Eapi
|
|
16
16
|
render
|
17
17
|
end
|
18
18
|
|
19
|
+
private
|
19
20
|
def perform_render
|
20
21
|
{}.tap do |hash|
|
21
22
|
_properties.each do |prop|
|
@@ -23,5 +24,13 @@ module Eapi
|
|
23
24
|
end
|
24
25
|
end
|
25
26
|
end
|
27
|
+
|
28
|
+
def perform_before_validation
|
29
|
+
_properties.each do |property|
|
30
|
+
if self.class.convert_before_validation?(property)
|
31
|
+
self.set(property, converted_value_for(property))
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
26
35
|
end
|
27
36
|
end
|
data/lib/eapi/list.rb
CHANGED
@@ -13,6 +13,7 @@ module Eapi
|
|
13
13
|
def self.add_features(klass)
|
14
14
|
Eapi::Common.add_features klass
|
15
15
|
klass.extend(ClassMethods)
|
16
|
+
klass.include(Eapi::Methods::Properties::ListInstanceMethods)
|
16
17
|
klass.extend(Eapi::Methods::Properties::ListCLassMethods)
|
17
18
|
end
|
18
19
|
|
@@ -34,10 +35,6 @@ module Eapi
|
|
34
35
|
render
|
35
36
|
end
|
36
37
|
|
37
|
-
def perform_render
|
38
|
-
_list.map { |val| convert_value val }.reject { |x| to_be_ignored x }
|
39
|
-
end
|
40
|
-
|
41
38
|
def _list
|
42
39
|
@_list ||= []
|
43
40
|
end
|
@@ -75,8 +72,17 @@ module Eapi
|
|
75
72
|
protected :initialize_copy
|
76
73
|
|
77
74
|
private
|
78
|
-
def
|
79
|
-
|
75
|
+
def perform_render
|
76
|
+
_list.reduce([]) do |array, value|
|
77
|
+
set_value_in_final_array(array, value)
|
78
|
+
array
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def perform_before_validation
|
83
|
+
if self.class.elements_convert_before_validation?
|
84
|
+
_list.map! { |v| convert_value_for_element(v) }
|
85
|
+
end
|
80
86
|
end
|
81
87
|
|
82
88
|
# transpose, assoc, rassoc , permutation, combination, repeated_permutation, repeated_combination, product, pack ?? => do not use the methods
|
@@ -19,11 +19,11 @@ module Eapi
|
|
19
19
|
end
|
20
20
|
|
21
21
|
def converted_value_for(prop)
|
22
|
-
convert_value get(prop)
|
22
|
+
convert_value get(prop), self.class.defined_convert_with_for(prop)
|
23
23
|
end
|
24
24
|
|
25
|
-
def convert_value(value)
|
26
|
-
Eapi::ValueConverter.convert_value(value)
|
25
|
+
def convert_value(value, convert_with = nil)
|
26
|
+
Eapi::ValueConverter.convert_value(value, self, convert_with)
|
27
27
|
end
|
28
28
|
|
29
29
|
def converted_or_default_value_for(property)
|
@@ -112,12 +112,40 @@ module Eapi
|
|
112
112
|
definition_for(property).fetch(:default, nil)
|
113
113
|
end
|
114
114
|
|
115
|
+
def defined_convert_with_for(property)
|
116
|
+
definition_for(property).fetch(:convert_with, nil)
|
117
|
+
end
|
118
|
+
|
119
|
+
def convert_before_validation?(property)
|
120
|
+
definition_for(property).fetch(:convert_before_validation, false)
|
121
|
+
end
|
122
|
+
|
115
123
|
private :_property_allow_raw
|
116
124
|
private :_property_definitions
|
117
125
|
private :run_property_definition
|
118
126
|
private :store_property_definition
|
119
127
|
end
|
120
128
|
|
129
|
+
module ListInstanceMethods
|
130
|
+
def set_value_in_final_array(array, value)
|
131
|
+
yield_final_value_for_elements(value) do |val|
|
132
|
+
array << val
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def yield_final_value_for_elements(value)
|
137
|
+
yield convert_value_for_element(value) unless to_be_ignored?(value)
|
138
|
+
end
|
139
|
+
|
140
|
+
def to_be_ignored?(value)
|
141
|
+
Eapi::ValueIgnoreChecker.to_be_ignored? value, self.class.elements_ignore_definition
|
142
|
+
end
|
143
|
+
|
144
|
+
def convert_value_for_element(value)
|
145
|
+
convert_value(value, self.class.elements_defined_convert_with_for)
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
121
149
|
module ListCLassMethods
|
122
150
|
def elements_allow_raw
|
123
151
|
property_allow_raw(:_list)
|
@@ -135,11 +163,19 @@ module Eapi
|
|
135
163
|
definition_for_elements.fetch(:ignore, :nil?)
|
136
164
|
end
|
137
165
|
|
166
|
+
def elements_convert_before_validation?
|
167
|
+
definition_for_elements.fetch(:convert_before_validation, false)
|
168
|
+
end
|
169
|
+
|
138
170
|
def elements(definition)
|
139
171
|
run_list_definition definition
|
140
172
|
store_list_definition definition
|
141
173
|
end
|
142
174
|
|
175
|
+
def elements_defined_convert_with_for
|
176
|
+
definition_for_elements.fetch(:convert_with, nil)
|
177
|
+
end
|
178
|
+
|
143
179
|
def definition_for_elements
|
144
180
|
@_list_definition ||= {}
|
145
181
|
end
|
data/lib/eapi/value_converter.rb
CHANGED
@@ -1,20 +1,47 @@
|
|
1
1
|
module Eapi
|
2
2
|
module ValueConverter
|
3
|
-
def self.convert_value(value)
|
4
|
-
|
3
|
+
def self.convert_value(value, context, convert_with = nil)
|
4
|
+
|
5
|
+
if convert_with.present?
|
6
|
+
value_using_convert_with(value, context, convert_with)
|
7
|
+
elsif value.nil?
|
5
8
|
nil
|
6
9
|
elsif can_render? value
|
7
10
|
value_from_render value
|
8
11
|
elsif is_list? value
|
9
|
-
value_from_list value
|
12
|
+
value_from_list value, context
|
10
13
|
elsif is_hash?(value)
|
11
|
-
value_from_hash value
|
14
|
+
value_from_hash value, context
|
12
15
|
else
|
13
16
|
value
|
14
17
|
end
|
15
18
|
end
|
16
19
|
|
17
20
|
private
|
21
|
+
def self.value_using_convert_with(value, context, convert_with)
|
22
|
+
if convert_with.respond_to? :call
|
23
|
+
value_using_callable value, context, convert_with
|
24
|
+
else
|
25
|
+
value_using_message value, convert_with
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.value_using_callable(value, context, callable)
|
30
|
+
a = callable.try(:arity) || callable.method(:call).arity
|
31
|
+
case a
|
32
|
+
when 0
|
33
|
+
callable.call
|
34
|
+
when 1
|
35
|
+
callable.call value
|
36
|
+
else
|
37
|
+
callable.call value, context
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.value_using_message(value, message)
|
42
|
+
value.send message
|
43
|
+
end
|
44
|
+
|
18
45
|
def self.can_render?(value)
|
19
46
|
value.respond_to? :render
|
20
47
|
end
|
@@ -33,14 +60,14 @@ module Eapi
|
|
33
60
|
value.render
|
34
61
|
end
|
35
62
|
|
36
|
-
def self.value_from_list(value)
|
37
|
-
value.to_a.map { |e| convert_value e }.compact
|
63
|
+
def self.value_from_list(value, context)
|
64
|
+
value.to_a.map { |e| convert_value e, context }.compact
|
38
65
|
end
|
39
66
|
|
40
|
-
def self.value_from_hash(value)
|
67
|
+
def self.value_from_hash(value, context)
|
41
68
|
{}.tap do |hash|
|
42
69
|
value.to_h.each_pair do |k, v|
|
43
|
-
val = convert_value v
|
70
|
+
val = convert_value v, context
|
44
71
|
hash[k] = val unless val.nil?
|
45
72
|
end
|
46
73
|
hash.deep_symbolize_keys!
|
data/lib/eapi/version.rb
CHANGED
@@ -0,0 +1,117 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Eapi do
|
4
|
+
|
5
|
+
describe 'convert_with' do
|
6
|
+
|
7
|
+
context 'Item' do
|
8
|
+
|
9
|
+
class ExampleItemConvertWith
|
10
|
+
include Eapi::Item
|
11
|
+
|
12
|
+
property :something, convert_with: :to_s
|
13
|
+
property :other, convert_with: ->(val) { "This is #{val}" }
|
14
|
+
property :third, convert_with: ->(val, obj) do
|
15
|
+
s = obj.something
|
16
|
+
c = obj.send :converted_value_for, :something
|
17
|
+
"I am #{val} with some #{s.inspect} as #{c.inspect}"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
subject { ExampleItemConvertWith.new something: :x, other: 1, third: 'the third' }
|
22
|
+
|
23
|
+
context 'message (symbol or string)' do
|
24
|
+
it 'in the rendered hash, the value is converted by sending the message to the value' do
|
25
|
+
expect(subject.render[:something]).to eq 'x'
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'callable object with 1 argument' do
|
30
|
+
it 'in the rendered hash, the value is converted by sending `call` to the callable object and passing the value as single argument' do
|
31
|
+
expect(subject.render[:other]).to eq 'This is 1'
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
context 'callable object with 2 arguments' do
|
36
|
+
it 'in the rendered hash, the value is converted by sending `call` to the callable object and passing the value as first argument and the context object (item) as second argument' do
|
37
|
+
expect(subject.render[:third]).to eq "I am the third with some :x as \"x\""
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
context 'List' do
|
43
|
+
class ExampleListConvertWith
|
44
|
+
include Eapi::List
|
45
|
+
|
46
|
+
elements convert_with: :to_s
|
47
|
+
end
|
48
|
+
|
49
|
+
subject { ExampleListConvertWith.new.add(1).add(2) }
|
50
|
+
|
51
|
+
it 'with a given option for `context_with`, it will use to convert all elements of the list' do
|
52
|
+
expect(subject.render).to eq ['1', '2']
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
describe 'convert_before_validation option' do
|
58
|
+
|
59
|
+
context 'Item' do
|
60
|
+
class ExampleItemConvertBeforeValidation
|
61
|
+
include Eapi::Item
|
62
|
+
|
63
|
+
property :something, convert_with: :to_i, convert_before_validation: true
|
64
|
+
property :normal, convert_with: :to_i
|
65
|
+
|
66
|
+
validates :something, inclusion: {in: [1, 2, 3]}
|
67
|
+
validates :normal, inclusion: {in: [1, 2, 3]}
|
68
|
+
end
|
69
|
+
|
70
|
+
context 'enabled' do
|
71
|
+
subject { ExampleItemConvertBeforeValidation.new something: '1', normal: 2 }
|
72
|
+
it 'the conversion happens before validation' do
|
73
|
+
expect(subject).to be_valid
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
context 'disabled' do
|
78
|
+
subject { ExampleItemConvertBeforeValidation.new something: 1, normal: '2' }
|
79
|
+
it 'the conversion happens on rendering, after validation' do
|
80
|
+
expect(subject).not_to be_valid
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
context 'List' do
|
86
|
+
class ExampleListConvertBeforeValidationEnabled
|
87
|
+
include Eapi::List
|
88
|
+
|
89
|
+
elements convert_with: :to_i, convert_before_validation: true, validate_with: ->(record, attr, value) do
|
90
|
+
record.errors.add(attr, 'must pass my custom validation') unless value.kind_of?(Fixnum)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
class ExampleListConvertBeforeValidationDisabled
|
95
|
+
include Eapi::List
|
96
|
+
|
97
|
+
elements convert_with: :to_i, validate_with: ->(record, attr, value) do
|
98
|
+
record.errors.add(attr, 'must pass my custom validation') unless value.kind_of?(Fixnum)
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
context 'enabled' do
|
103
|
+
subject { ExampleListConvertBeforeValidationEnabled.new.add('1') }
|
104
|
+
it 'the conversion happens before validation' do
|
105
|
+
expect(subject).to be_valid
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
context 'disabled' do
|
110
|
+
subject { ExampleListConvertBeforeValidationDisabled.new.add('1') }
|
111
|
+
it 'the conversion happens on rendering, after validation' do
|
112
|
+
expect(subject).not_to be_valid
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: eapi
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Eduardo Turiño
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-07-
|
11
|
+
date: 2014-07-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -214,6 +214,7 @@ files:
|
|
214
214
|
- lib/eapi/value_ignore_checker.rb
|
215
215
|
- lib/eapi/version.rb
|
216
216
|
- spec/basic_spec.rb
|
217
|
+
- spec/conversion_spec.rb
|
217
218
|
- spec/definition_spec.rb
|
218
219
|
- spec/extension_spec.rb
|
219
220
|
- spec/function_spec.rb
|
@@ -252,6 +253,7 @@ summary: ruby gem for building complex structures that will end up in hashes (in
|
|
252
253
|
devised for ElasticSearch search requests)
|
253
254
|
test_files:
|
254
255
|
- spec/basic_spec.rb
|
256
|
+
- spec/conversion_spec.rb
|
255
257
|
- spec/definition_spec.rb
|
256
258
|
- spec/extension_spec.rb
|
257
259
|
- spec/function_spec.rb
|