eapi 0.5.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 +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
|