eapi 0.0.1 → 0.1.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/.travis.yml +5 -0
- data/Gemfile +1 -0
- data/README.md +495 -2
- data/eapi.gemspec +0 -1
- data/lib/eapi.rb +10 -2
- data/lib/eapi/children.rb +30 -0
- data/lib/eapi/common.rb +8 -15
- data/lib/eapi/definition_runner.rb +109 -0
- data/lib/eapi/methods/names.rb +0 -4
- data/lib/eapi/methods/properties.rb +1 -109
- data/lib/eapi/methods/types.rb +52 -5
- data/lib/eapi/type_checker.rb +16 -0
- data/lib/eapi/value_converter.rb +40 -0
- data/lib/eapi/version.rb +1 -1
- data/spec/basic_spec.rb +58 -0
- data/spec/definition_spec.rb +26 -0
- data/spec/function_spec.rb +71 -0
- data/spec/list_spec.rb +124 -0
- data/spec/spec_helper.rb +2 -2
- data/spec/to_h_spec.rb +87 -0
- data/spec/type_spec.rb +86 -0
- data/spec/{eapi_validations_spec.rb → validations_spec.rb} +59 -21
- metadata +20 -25
- data/.coveralls.yml +0 -1
- data/spec/eapi_basic_spec.rb +0 -39
- data/spec/eapi_list_spec.rb +0 -92
- data/spec/eapi_to_h_spec.rb +0 -42
@@ -0,0 +1,40 @@
|
|
1
|
+
module Eapi
|
2
|
+
module ValueConverter
|
3
|
+
def self.convert_value(value)
|
4
|
+
if value.nil?
|
5
|
+
nil
|
6
|
+
elsif is_list? value
|
7
|
+
value_from_list value
|
8
|
+
elsif is_hash?(value)
|
9
|
+
value_from_hash value
|
10
|
+
else
|
11
|
+
value
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def self.is_hash?(value)
|
17
|
+
value.respond_to? :to_h
|
18
|
+
end
|
19
|
+
|
20
|
+
def self.is_list?(value)
|
21
|
+
return false if value.kind_of?(Hash) || value.kind_of?(OpenStruct)
|
22
|
+
|
23
|
+
value.respond_to? :to_a
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.value_from_list(value)
|
27
|
+
value.to_a.map { |e| convert_value e }.compact
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.value_from_hash(value)
|
31
|
+
{}.tap do |hash|
|
32
|
+
value.to_h.each_pair do |k, v|
|
33
|
+
val = convert_value v
|
34
|
+
hash[k] = val unless val.nil?
|
35
|
+
end
|
36
|
+
hash.deep_symbolize_keys!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
data/lib/eapi/version.rb
CHANGED
data/spec/basic_spec.rb
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Eapi do
|
4
|
+
|
5
|
+
context 'basic behaviour' do
|
6
|
+
class MyTestKlass
|
7
|
+
include Eapi::Common
|
8
|
+
|
9
|
+
property :something
|
10
|
+
end
|
11
|
+
|
12
|
+
describe '#something (fluent setter/getter)' do
|
13
|
+
describe '#something as getter' do
|
14
|
+
it 'return the value' do
|
15
|
+
eapi = MyTestKlass.new something: :hey
|
16
|
+
expect(eapi.something).to eq :hey
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe '#something("val")' do
|
21
|
+
it 'set the value and return self' do
|
22
|
+
eapi = MyTestKlass.new something: :hey
|
23
|
+
res = eapi.something :other
|
24
|
+
expect(eapi).to be res
|
25
|
+
expect(eapi.something).to eq :other
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe '#set_something("val")' do
|
30
|
+
it 'set the value and return self' do
|
31
|
+
eapi = MyTestKlass.new something: :hey
|
32
|
+
res = eapi.set_something :other
|
33
|
+
expect(eapi).to be res
|
34
|
+
expect(eapi.something).to eq :other
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
describe '#get' do
|
40
|
+
it 'will use the getter' do
|
41
|
+
eapi = MyTestKlass.new something: :hey
|
42
|
+
expect(eapi.get(:something)).to eq :hey
|
43
|
+
expect(eapi.get('something')).to eq :hey
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
describe '#set' do
|
49
|
+
it 'will use the fluent setter' do
|
50
|
+
eapi = MyTestKlass.new
|
51
|
+
expect(eapi.set(:something, :hey)).to equal eapi
|
52
|
+
expect(eapi.get(:something)).to eq :hey
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
end
|
57
|
+
|
58
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Eapi do
|
4
|
+
|
5
|
+
context '#definition_for' do
|
6
|
+
class MyTestKlassDefinition
|
7
|
+
include Eapi::Common
|
8
|
+
|
9
|
+
property :something, type: Hash, unrecognised_option: 1
|
10
|
+
end
|
11
|
+
|
12
|
+
it 'will return the definition of a property, even with the unrecognised options (a copy of the hash, not editable)' do
|
13
|
+
definition = MyTestKlassDefinition.definition_for :something
|
14
|
+
expected = {type: Hash, unrecognised_option: 1}
|
15
|
+
expect(definition).to eq expected
|
16
|
+
|
17
|
+
#changing the definition
|
18
|
+
definition[:type] = Array
|
19
|
+
|
20
|
+
definition_2 = MyTestKlassDefinition.definition_for :something
|
21
|
+
expect(definition_2).to eq expected
|
22
|
+
expect(definition_2[:type]).to eq Hash
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Eapi do
|
4
|
+
|
5
|
+
context 'function initializer' do
|
6
|
+
class MyTestKlassOutside
|
7
|
+
include Eapi::Common
|
8
|
+
|
9
|
+
property :something
|
10
|
+
end
|
11
|
+
|
12
|
+
module Somewhere
|
13
|
+
class TestKlassInModule
|
14
|
+
include Eapi::Common
|
15
|
+
|
16
|
+
property :something
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
describe 'Eapi::Children' do
|
21
|
+
describe '#list' do
|
22
|
+
it 'returns the list of Eapi enabled classes' do
|
23
|
+
list = Eapi::Children.list
|
24
|
+
expect(list).to include(MyTestKlassOutside)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe '#has?' do
|
29
|
+
it 'true if the given class is an Eapi enabled class' do
|
30
|
+
expect(Eapi::Children).not_to be_has('nope')
|
31
|
+
expect(Eapi::Children).to be_has(MyTestKlassOutside)
|
32
|
+
expect(Eapi::Children).to be_has('MyTestKlassOutside')
|
33
|
+
expect(Eapi::Children).to be_has('my_test_klass_outside')
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
describe '#get' do
|
38
|
+
it 'get the given class if it is an Eapi enabled class' do
|
39
|
+
expect(Eapi::Children.get('nope')).to be_nil
|
40
|
+
expect(Eapi::Children.get(MyTestKlassOutside)).to eq MyTestKlassOutside
|
41
|
+
expect(Eapi::Children.get('MyTestKlassOutside')).to eq MyTestKlassOutside
|
42
|
+
expect(Eapi::Children.get('my_test_klass_outside')).to eq MyTestKlassOutside
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
describe 'initialise using method calls to Eapi', :focus do
|
48
|
+
[
|
49
|
+
[:MyTestKlassOutside, MyTestKlassOutside],
|
50
|
+
[:my_test_klass_outside, MyTestKlassOutside],
|
51
|
+
[:Somewhere__TestKlassInModule, Somewhere::TestKlassInModule],
|
52
|
+
[:somewhere__test_klass_in_module, Somewhere::TestKlassInModule],
|
53
|
+
[:Somewhere_TestKlassInModule, Somewhere::TestKlassInModule],
|
54
|
+
[:somewhere_test_klass_in_module, Somewhere::TestKlassInModule],
|
55
|
+
].each do |(meth, klass)|
|
56
|
+
describe "Eapi.#{meth}(...)" do
|
57
|
+
it "calls #{klass}.new" do
|
58
|
+
eapi = Eapi.send meth, something: :hey
|
59
|
+
expect(eapi).to be_a klass
|
60
|
+
expect(eapi.something).to eq :hey
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
it 'will raise NoMethodError if a valid class is not found' do
|
67
|
+
expect { Eapi.this_one_does_not_exist }.to raise_exception NoMethodError
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
data/spec/list_spec.rb
ADDED
@@ -0,0 +1,124 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Eapi do
|
4
|
+
|
5
|
+
context 'list' do
|
6
|
+
class MyTestClassValMult
|
7
|
+
include Eapi::Common
|
8
|
+
|
9
|
+
property :something, multiple: true
|
10
|
+
end
|
11
|
+
|
12
|
+
it '#add_something' do
|
13
|
+
eapi = MyTestClassValMult.new something: [1, 2]
|
14
|
+
res = eapi.add_something 3
|
15
|
+
expect(res).to be eapi
|
16
|
+
expect(eapi.something).to eq [1, 2, 3]
|
17
|
+
end
|
18
|
+
|
19
|
+
it '#init_something called on first add if element is nil' do
|
20
|
+
eapi = MyTestClassValMult.new
|
21
|
+
res = eapi.add_something :a
|
22
|
+
expect(res).to be eapi
|
23
|
+
expect(eapi.something.to_a).to eq [:a]
|
24
|
+
end
|
25
|
+
|
26
|
+
class MyTestClassValMultImpl
|
27
|
+
include Eapi::Common
|
28
|
+
|
29
|
+
property :something, type: Set
|
30
|
+
end
|
31
|
+
|
32
|
+
class MyMultiple
|
33
|
+
def self.is_multiple?
|
34
|
+
true
|
35
|
+
end
|
36
|
+
|
37
|
+
def <<(x)
|
38
|
+
@elements ||= []
|
39
|
+
@elements << x
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_a
|
43
|
+
@elements.to_a
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class MyTestClassValMultImpl2
|
48
|
+
include Eapi::Common
|
49
|
+
|
50
|
+
property :something, type: MyMultiple
|
51
|
+
end
|
52
|
+
|
53
|
+
class MyMultipleEapi
|
54
|
+
include Eapi::Multiple
|
55
|
+
|
56
|
+
def <<(x)
|
57
|
+
@elements ||= []
|
58
|
+
@elements << x
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_a
|
62
|
+
@elements.to_a
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class MyTestClassValMultImpl3
|
67
|
+
include Eapi::Common
|
68
|
+
|
69
|
+
property :something, type: MyMultipleEapi
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'if type is Array or Set, or responds true to is_multiple?, it is multiple implicitly + uses that class to initialize the property when adding' do
|
73
|
+
[
|
74
|
+
[MyTestClassValMult, Array],
|
75
|
+
[MyTestClassValMultImpl, Set],
|
76
|
+
[MyTestClassValMultImpl2, MyMultiple],
|
77
|
+
[MyTestClassValMultImpl3, MyMultipleEapi],
|
78
|
+
].each do |(eapi_class, type_class)|
|
79
|
+
eapi = eapi_class.new
|
80
|
+
res = eapi.add_something :a
|
81
|
+
expect(res).to be eapi
|
82
|
+
expect(eapi.something.to_a).to eq [:a]
|
83
|
+
expect(eapi.something).to be_a_kind_of type_class
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
describe 'element validation' do
|
88
|
+
class MyTestClassValElements
|
89
|
+
include Eapi::Common
|
90
|
+
property :something, multiple: true, element_type: Hash
|
91
|
+
property :other, multiple: true, validate_element_with: ->(record, attr, value) do
|
92
|
+
record.errors.add(attr, "element must pass my custom validation") unless value == :valid_val
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
describe 'using `type_element` property in definition' do
|
97
|
+
it 'will validate the type of all the elements in the list' do
|
98
|
+
eapi = MyTestClassValElements.new
|
99
|
+
eapi.add_something 1
|
100
|
+
expect(eapi).not_to be_valid
|
101
|
+
expect(eapi.errors.full_messages).to eq ["Something element must be a Hash"]
|
102
|
+
expect(eapi.errors.messages).to eq({something: ["element must be a Hash"]})
|
103
|
+
|
104
|
+
eapi.something [{a: :b}]
|
105
|
+
expect(eapi).to be_valid
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe 'using `validate_element_with` property in definition' do
|
110
|
+
it 'will run that custom validation for all the elements in the list' do
|
111
|
+
eapi = MyTestClassValElements.new
|
112
|
+
eapi.add_other 1
|
113
|
+
expect(eapi).not_to be_valid
|
114
|
+
expect(eapi.errors.full_messages).to eq ["Other element must pass my custom validation"]
|
115
|
+
expect(eapi.errors.messages).to eq({other: ["element must pass my custom validation"]})
|
116
|
+
|
117
|
+
eapi.other [:valid_val]
|
118
|
+
expect(eapi).to be_valid
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
end
|
data/spec/spec_helper.rb
CHANGED
data/spec/to_h_spec.rb
ADDED
@@ -0,0 +1,87 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Eapi do
|
4
|
+
|
5
|
+
context '#to_h' do
|
6
|
+
class MyTestClassToH
|
7
|
+
include Eapi::Common
|
8
|
+
|
9
|
+
property :something, required: true
|
10
|
+
property :other
|
11
|
+
end
|
12
|
+
|
13
|
+
class MyTestObject
|
14
|
+
def to_h
|
15
|
+
{
|
16
|
+
a: 'hello'
|
17
|
+
}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class MyTestObjectComplex
|
22
|
+
def to_h
|
23
|
+
{
|
24
|
+
a: Set.new(['hello', 'world', MyTestObject.new])
|
25
|
+
}
|
26
|
+
end
|
27
|
+
|
28
|
+
def expected
|
29
|
+
{
|
30
|
+
a: [
|
31
|
+
'hello',
|
32
|
+
'world',
|
33
|
+
{a: 'hello'}
|
34
|
+
]
|
35
|
+
}
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'raise error if invalid' do
|
40
|
+
eapi = MyTestClassToH.new
|
41
|
+
expect { eapi.to_h }.to raise_error do |error|
|
42
|
+
expect(error).to be_a_kind_of Eapi::Errors::InvalidElementError
|
43
|
+
expect(error.message).to be_start_with "errors: [\"Something can't be blank\"], self: #<MyTestClassToH"
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
|
48
|
+
it 'create a hash with elements (calling to_h to each element), avoiding nils' do
|
49
|
+
eapi = MyTestClassToH.new
|
50
|
+
eapi.something 'hi'
|
51
|
+
expected = {something: 'hi'}
|
52
|
+
expect(eapi.to_h).to eq expected
|
53
|
+
|
54
|
+
eapi = MyTestClassToH.new
|
55
|
+
eapi.something(MyTestObject.new).other(true)
|
56
|
+
expected = {something: {a: 'hello'}, other: true}
|
57
|
+
|
58
|
+
expect(eapi.to_h).to eq expected
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'hash with elements, all converted to basic Arrays and Hashes (keys as symbols), and exluding nils (if all validations pass)' do
|
62
|
+
list = Set.new [
|
63
|
+
OpenStruct.new(a: 1, 'b' => 2),
|
64
|
+
{c: 3, 'd' => 4},
|
65
|
+
nil
|
66
|
+
]
|
67
|
+
|
68
|
+
other = MyTestObjectComplex.new
|
69
|
+
|
70
|
+
expected = {
|
71
|
+
something: [
|
72
|
+
{a: 1, b: 2},
|
73
|
+
{c: 3, d: 4},
|
74
|
+
],
|
75
|
+
|
76
|
+
other: other.expected
|
77
|
+
}
|
78
|
+
|
79
|
+
eapi = MyTestClassToH.new
|
80
|
+
eapi.something list
|
81
|
+
eapi.other other
|
82
|
+
|
83
|
+
expect(eapi.to_h).to eq expected
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
data/spec/type_spec.rb
ADDED
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe Eapi do
|
4
|
+
|
5
|
+
context 'type check' do
|
6
|
+
class OtherType
|
7
|
+
end
|
8
|
+
|
9
|
+
class MyTestTypeKlass
|
10
|
+
include Eapi::Common
|
11
|
+
|
12
|
+
is :one_thing, :other_thing, OtherType
|
13
|
+
end
|
14
|
+
|
15
|
+
describe '#is? and .is?' do
|
16
|
+
it 'true if asked for the same type' do
|
17
|
+
expect(MyTestTypeKlass).to be_is MyTestTypeKlass
|
18
|
+
expect(MyTestTypeKlass).to be_is 'MyTestTypeKlass'
|
19
|
+
expect(MyTestTypeKlass).to be_is :MyTestTypeKlass
|
20
|
+
expect(MyTestTypeKlass).to be_is :my_test_type_klass
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'false if asked for other class' do
|
24
|
+
expect(MyTestTypeKlass).not_to be_is Hash
|
25
|
+
expect(MyTestTypeKlass).not_to be_is 'Hash'
|
26
|
+
expect(MyTestTypeKlass).not_to be_is :Hash
|
27
|
+
end
|
28
|
+
|
29
|
+
it 'true if asked for a type specified on the class with `is` method' do
|
30
|
+
expect(MyTestTypeKlass).not_to be_is :not_you
|
31
|
+
expect(MyTestTypeKlass).to be_is :one_thing
|
32
|
+
expect(MyTestTypeKlass).to be_is :other_thing
|
33
|
+
expect(MyTestTypeKlass).to be_is OtherType
|
34
|
+
expect(MyTestTypeKlass).to be_is :OtherType
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe '#is?' do
|
39
|
+
it 'behaves exactly like class method `.is?`' do
|
40
|
+
obj = MyTestTypeKlass.new
|
41
|
+
|
42
|
+
expect(obj).to be_is MyTestTypeKlass
|
43
|
+
expect(obj).to be_is 'MyTestTypeKlass'
|
44
|
+
expect(obj).to be_is :MyTestTypeKlass
|
45
|
+
expect(obj).to be_is :my_test_type_klass
|
46
|
+
|
47
|
+
expect(obj).not_to be_is Hash
|
48
|
+
expect(obj).not_to be_is 'Hash'
|
49
|
+
expect(obj).not_to be_is :Hash
|
50
|
+
|
51
|
+
expect(obj).not_to be_is :not_you
|
52
|
+
expect(obj).to be_is :one_thing
|
53
|
+
expect(obj).to be_is :other_thing
|
54
|
+
expect(obj).to be_is OtherType
|
55
|
+
expect(obj).to be_is :OtherType
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
describe '`#is_a_specific_type?` and `#is_an_other_type?`' do
|
60
|
+
it 'will use #is? inside' do
|
61
|
+
obj = MyTestTypeKlass.new
|
62
|
+
expect(obj).to be_is_a_my_test_type_klass
|
63
|
+
expect(obj).not_to be_is_a_not_you
|
64
|
+
expect(obj).to be_is_an_one_thing
|
65
|
+
expect(obj).to be_is_an_other_thing
|
66
|
+
end
|
67
|
+
|
68
|
+
it 'method_missing still works' do
|
69
|
+
expect { MyTestTypeKlass.new.some_other_method? }.to raise_exception(NoMethodError)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
describe '`.is_a_specific_type?` and `.is_an_other_type?`' do
|
74
|
+
it 'will use .is? inside' do
|
75
|
+
expect(MyTestTypeKlass).to be_is_a_my_test_type_klass
|
76
|
+
expect(MyTestTypeKlass).not_to be_is_a_not_you
|
77
|
+
expect(MyTestTypeKlass).to be_is_an_one_thing
|
78
|
+
expect(MyTestTypeKlass).to be_is_an_other_thing
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'method_missing still works' do
|
82
|
+
expect { MyTestTypeKlass.some_other_method? }.to raise_exception(NoMethodError)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|