eapi 0.2.1 → 0.3.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.
@@ -4,6 +4,7 @@ module Eapi
4
4
  class Property < Struct.new(:klass, :field, :definition)
5
5
  def run
6
6
  run_multiple_accessor
7
+ run_multiple_clearer
7
8
  run_init
8
9
  run_validations
9
10
  run_allow_raw
@@ -53,8 +54,10 @@ module Eapi
53
54
  end
54
55
 
55
56
  def run_init
56
- if type || multiple?
57
- Runner.init(klass: klass, field: field, type: type || Array)
57
+ if init_class
58
+ Runner.init(klass: klass, field: field, type: init_class)
59
+ elsif multiple? && (type.blank? || type.to_s == 'Array')
60
+ Runner.init(klass: klass, field: field, type: Array)
58
61
  end
59
62
  end
60
63
 
@@ -64,11 +67,19 @@ module Eapi
64
67
  end
65
68
  end
66
69
 
70
+ def run_multiple_clearer
71
+ if multiple?
72
+ Runner.multiple_clearer(klass: klass, field: field)
73
+ end
74
+ end
75
+
67
76
  def type_multiple?(type)
68
- return false if type.nil?
69
- return true if type == Array || type == Set
77
+ type_class = Eapi::TypeChecker.constant_for_type type
70
78
 
71
- type.respond_to?(:is_multiple?) && type.is_multiple?
79
+ return false if type_class.nil?
80
+ return true if type_class == Array || type_class == Set
81
+
82
+ type_class.respond_to?(:is_multiple?) && type_class.is_multiple?
72
83
  end
73
84
 
74
85
  def validate_element_with
@@ -76,7 +87,7 @@ module Eapi
76
87
  end
77
88
 
78
89
  def multiple?
79
- definition.fetch(:multiple, false) || type_multiple?(type)
90
+ definition.fetch(:multiple, false) || type_multiple?(type) || type_multiple?(init_class)
80
91
  end
81
92
 
82
93
  def required?
@@ -95,6 +106,10 @@ module Eapi
95
106
  definition.fetch(:type, nil)
96
107
  end
97
108
 
109
+ def init_class
110
+ definition.fetch(:init_class, nil)
111
+ end
112
+
98
113
  def allow_raw?
99
114
  definition.fetch(:allow_raw, false)
100
115
  end
@@ -71,6 +71,10 @@ module Eapi
71
71
  def self.multiple_accessor(klass:, field:)
72
72
  klass.send :define_multiple_accessor, field
73
73
  end
74
+
75
+ def self.multiple_clearer(klass:, field:)
76
+ klass.send :define_multiple_clearer, field
77
+ end
74
78
  end
75
79
  end
76
80
  end
data/lib/eapi/errors.rb CHANGED
@@ -2,5 +2,11 @@ module Eapi
2
2
  module Errors
3
3
  class InvalidElementError < StandardError
4
4
  end
5
+
6
+ class InvalidInitClass < StandardError
7
+ end
8
+
9
+ class CannotClearFieldError < StandardError
10
+ end
5
11
  end
6
12
  end
@@ -15,12 +15,35 @@ module Eapi
15
15
  end
16
16
  end
17
17
 
18
- def define_init(field, type_class)
18
+ def define_multiple_clearer(field)
19
+ init = Eapi::Methods::Names.init field
20
+ fluent_clearer = Eapi::Methods::Names.clearer field
21
+ getter = Eapi::Methods::Names.getter field
22
+
23
+ define_method fluent_clearer do
24
+ current = send(getter)
25
+ if current.nil?
26
+ # NOOP
27
+ elsif current.respond_to?(:clear)
28
+ current.clear
29
+ elsif respond_to?(init)
30
+ send(init)
31
+ else
32
+ raise Eapi::Errors::CannotClearFieldError, "#{self} can't clear #{field}: it does not respond to `clear` nor we have defined a `init_#{field}` method"
33
+ end
34
+
35
+ self
36
+ end
37
+ end
38
+
39
+ def define_init(field, init_class)
19
40
  init = Eapi::Methods::Names.init field
20
41
  instance_var = Eapi::Methods::Names.instance_var field
21
42
 
22
43
  define_method init do
23
- value = type_class.new
44
+ klass = Eapi::TypeChecker.constant_for_type init_class
45
+ raise Eapi::Errors::InvalidInitClass, "init_class: #{init_class}" if klass.nil?
46
+ value = klass.new
24
47
  instance_variable_set instance_var, value
25
48
  end
26
49
  end
@@ -10,6 +10,10 @@ module Eapi
10
10
  "add_#{field}"
11
11
  end
12
12
 
13
+ def self.clearer(field)
14
+ "clear_#{field}"
15
+ end
16
+
13
17
  def self.getter(field)
14
18
  "#{field}"
15
19
  end
@@ -29,21 +29,15 @@ module Eapi
29
29
 
30
30
  module ClassMethods
31
31
  def property_allow_raw(field)
32
- @_property_allow_raw ||= {}
33
-
34
- @_property_allow_raw[field.to_sym] = true
32
+ _property_allow_raw[field.to_sym] = true
35
33
  end
36
34
 
37
35
  def property_disallow_raw(field)
38
- @_property_allow_raw ||= {}
39
-
40
- @_property_allow_raw[field.to_sym] = false
36
+ _property_allow_raw[field.to_sym] = false
41
37
  end
42
38
 
43
39
  def property_allow_raw?(field)
44
- @_property_allow_raw ||= {}
45
-
46
- @_property_allow_raw.fetch(field.to_sym, false)
40
+ _property_allow_raw.fetch(field.to_sym, false)
47
41
  end
48
42
 
49
43
  def property(field, definition = {})
@@ -54,23 +48,31 @@ module Eapi
54
48
  end
55
49
 
56
50
  def properties
57
- @_property_definitions.keys
51
+ _property_definitions.keys
58
52
  end
59
53
 
60
54
  def definition_for(field)
61
- @_property_definitions ||= {}
62
- @_property_definitions.fetch(field.to_sym, {}).dup
55
+ _property_definitions.fetch(field.to_sym, {}).dup
63
56
  end
64
57
 
65
58
  def store_property_definition(field, definition)
66
- @_property_definitions ||= {}
67
- @_property_definitions[field] = definition
59
+ _property_definitions[field] = definition.tap { |x| x.freeze }
68
60
  end
69
61
 
70
62
  def run_property_definition(property_field, definition)
71
63
  Eapi::DefinitionRunners::Property.new(self, property_field, definition).run
72
64
  end
73
65
 
66
+ def _property_allow_raw
67
+ @_property_allow_raw ||= {}
68
+ end
69
+
70
+ def _property_definitions
71
+ @_property_definitions ||= {}
72
+ end
73
+
74
+ private :_property_allow_raw
75
+ private :_property_definitions
74
76
  private :run_property_definition
75
77
  private :store_property_definition
76
78
  end
@@ -98,7 +100,7 @@ module Eapi
98
100
  end
99
101
 
100
102
  def store_list_definition(definition)
101
- @_list_definition = definition
103
+ @_list_definition = definition.tap { |x| x.freeze }
102
104
  end
103
105
 
104
106
  def run_list_definition(definition)
@@ -47,6 +47,7 @@ module Eapi
47
47
 
48
48
  module InstanceMethods
49
49
  def is?(type)
50
+ return true if type.kind_of?(Module) && kind_of?(type)
50
51
  self.class.is?(type)
51
52
  end
52
53
 
@@ -61,11 +62,12 @@ module Eapi
61
62
  end
62
63
 
63
64
  def is?(type)
65
+ return true if Checker._is_type_module?(self, type)
66
+
64
67
  type_sym = Types.to_type_sym type
68
+ return true if Checker._is_type_module_sym?(self, type_sym)
65
69
 
66
- return true if self == type || Types.to_type_sym(self) == type_sym
67
70
  return false unless @i_am_a.present?
68
-
69
71
  !!@i_am_a.include?(type_sym) # force it to be a bool
70
72
  end
71
73
 
@@ -77,6 +79,16 @@ module Eapi
77
79
  end
78
80
  end
79
81
 
82
+ module Checker
83
+ def self._is_type_module?(klass, mod_or_class)
84
+ return false unless mod_or_class.kind_of?(Module)
85
+ klass == mod_or_class || klass.ancestors.include?(mod_or_class)
86
+ end
87
+
88
+ def self._is_type_module_sym?(klass, type_sym)
89
+ Types.to_type_sym(self) == type_sym || klass.ancestors.any? { |a| Types.to_type_sym(a) == type_sym }
90
+ end
91
+ end
80
92
  end
81
93
  end
82
94
  end
@@ -1,6 +1,18 @@
1
1
  module Eapi
2
2
  class TypeChecker
3
3
 
4
+ def self.constant_for_type(type)
5
+ if type.kind_of? Module
6
+ type
7
+ else
8
+ begin
9
+ type.to_s.constantize
10
+ rescue NameError
11
+ nil
12
+ end
13
+ end
14
+ end
15
+
4
16
  attr_reader :given_type, :allow_raw
5
17
 
6
18
  def initialize(given_type, allow_raw = false)
@@ -9,7 +21,7 @@ module Eapi
9
21
  end
10
22
 
11
23
  def is_valid_type?(value)
12
- value.nil? || is_same_type?(value) || poses_as_type?(value) || valid_raw?(value)
24
+ value.nil? || valid_raw?(value) || is_same_type?(value) || poses_as_type?(value)
13
25
  end
14
26
 
15
27
  private
@@ -20,7 +32,7 @@ module Eapi
20
32
  end
21
33
 
22
34
  def is_same_type?(value)
23
- value.kind_of?(type)
35
+ value.kind_of?(type_class) if type_class.present?
24
36
  end
25
37
 
26
38
  def poses_as_type?(value)
@@ -28,11 +40,15 @@ module Eapi
28
40
  end
29
41
 
30
42
  def type
31
- if given_type.kind_of? Module
32
- given_type
33
- else
34
- given_type.to_s.constantize
35
- end
43
+ given_type
44
+ end
45
+
46
+ def type_class
47
+ @type_class ||= load_type_class
48
+ end
49
+
50
+ def load_type_class
51
+ Eapi::TypeChecker.constant_for_type given_type
36
52
  end
37
53
 
38
54
  def allow_raw?
@@ -3,6 +3,8 @@ module Eapi
3
3
  def self.convert_value(value)
4
4
  if value.nil?
5
5
  nil
6
+ elsif can_render? value
7
+ value_from_render value
6
8
  elsif is_list? value
7
9
  value_from_list value
8
10
  elsif is_hash?(value)
@@ -13,6 +15,10 @@ module Eapi
13
15
  end
14
16
 
15
17
  private
18
+ def self.can_render?(value)
19
+ value.respond_to? :render
20
+ end
21
+
16
22
  def self.is_hash?(value)
17
23
  value.respond_to? :to_h
18
24
  end
@@ -23,6 +29,10 @@ module Eapi
23
29
  value.respond_to? :to_a
24
30
  end
25
31
 
32
+ def self.value_from_render(value)
33
+ value.render
34
+ end
35
+
26
36
  def self.value_from_list(value)
27
37
  value.to_a.map { |e| convert_value e }.compact
28
38
  end
data/lib/eapi/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Eapi
2
- VERSION = "0.2.1"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -2,74 +2,103 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe Eapi do
4
4
 
5
- context 'list elements' do
6
- class MyTestClassValMult
7
- include Eapi::Item
8
-
9
- property :something, multiple: true
5
+ class MyMultiple
6
+ def self.is_multiple?
7
+ true
10
8
  end
11
9
 
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]
10
+ def <<(x)
11
+ @elements ||= []
12
+ @elements << x
17
13
  end
18
14
 
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]
15
+ def to_a
16
+ @elements.to_a
24
17
  end
18
+ end
25
19
 
26
- class MyTestClassValMultImpl
27
- include Eapi::Item
20
+ class MyMultipleValueTestKlass
21
+ include Eapi::MultipleValue
28
22
 
29
- property :something, type: Set
23
+ def <<(x)
24
+ @elements ||= []
25
+ @elements << x
30
26
  end
31
27
 
32
- class MyMultiple
33
- def self.is_multiple?
34
- true
35
- end
28
+ def to_a
29
+ @elements.to_a
30
+ end
31
+ end
36
32
 
37
- def <<(x)
38
- @elements ||= []
39
- @elements << x
40
- end
33
+ class MyTestClassValMult
34
+ include Eapi::Item
41
35
 
42
- def to_a
43
- @elements.to_a
44
- end
45
- end
36
+ property :something, multiple: true
37
+ end
46
38
 
47
- class MyTestClassValMultImpl2
48
- include Eapi::Item
39
+ class MyTestClassValMultImpl
40
+ include Eapi::Item
49
41
 
50
- property :something, type: MyMultiple
51
- end
42
+ property :something, init_class: Set
43
+ end
52
44
 
53
- class MyMultipleValueTestKlass
54
- include Eapi::MultipleValue
45
+ class MyTestClassValMultImpl2
46
+ include Eapi::Item
55
47
 
56
- def <<(x)
57
- @elements ||= []
58
- @elements << x
59
- end
48
+ property :something, init_class: :MyMultiple
49
+ end
60
50
 
61
- def to_a
62
- @elements.to_a
63
- end
64
- end
65
51
 
66
- class MyTestClassValMultImpl3
67
- include Eapi::Item
52
+ class MyTestClassValMultImpl3
53
+ include Eapi::Item
54
+
55
+ property :something, init_class: "MyMultipleValueTestKlass"
56
+ end
57
+
58
+
59
+ class MyTestClassValMultTypeTest
60
+ include Eapi::Item
61
+
62
+ property :something, multiple: true
63
+ end
64
+
65
+ class MyTestClassValMultImplTypeTest
66
+ include Eapi::Item
67
+
68
+ property :something, type: Set
69
+ end
70
+
71
+ class MyTestClassValMultImpl2TypeTest
72
+ include Eapi::Item
73
+
74
+ property :something, type: :MyMultiple
75
+ end
76
+
77
+
78
+ class MyTestClassValMultImpl3TypeTest
79
+ include Eapi::Item
80
+
81
+ property :something, type: "MyMultipleValueTestKlass"
82
+ end
68
83
 
69
- property :something, type: MyMultipleValueTestKlass
84
+
85
+ context 'list elements' do
86
+ it '#add_something' do
87
+ eapi = MyTestClassValMult.new something: [1, 2]
88
+ res = eapi.add_something 3
89
+ expect(res).to be eapi
90
+ expect(eapi.something).to eq [1, 2, 3]
70
91
  end
71
92
 
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
93
+ it '#init_something called on first add if element is nil' do
94
+ eapi = MyTestClassValMult.new
95
+ res = eapi.add_something :a
96
+ expect(res).to be eapi
97
+ expect(eapi.something.to_a).to eq [:a]
98
+ end
99
+
100
+
101
+ it 'if init_class 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
102
  [
74
103
  [MyTestClassValMult, Array],
75
104
  [MyTestClassValMultImpl, Set],
@@ -84,6 +113,78 @@ RSpec.describe Eapi do
84
113
  end
85
114
  end
86
115
 
116
+ it 'if type is Array or Set, or responds true to is_multiple?, it is multiple implicitly but it does not use it to initialize the property when adding (unless type == Array)' do
117
+ [
118
+ [MyTestClassValMultTypeTest, Array],
119
+ [MyTestClassValMultImplTypeTest, Set],
120
+ [MyTestClassValMultImpl2TypeTest, MyMultiple],
121
+ [MyTestClassValMultImpl3TypeTest, MyMultipleValueTestKlass],
122
+ ].each do |(eapi_class, type_class)|
123
+ eapi = eapi_class.new
124
+
125
+ unless type_class == Array
126
+ expect { eapi.add_something :a }.to raise_exception
127
+ end
128
+
129
+ eapi.something type_class.new
130
+
131
+ res = eapi.add_something :a
132
+ expect(res).to be eapi
133
+ expect(eapi.something.to_a).to eq [:a]
134
+ expect(eapi.something).to be_a_kind_of type_class
135
+ end
136
+ end
137
+
138
+ context '#clear_something' do
139
+ it 'if value is nil, returns self' do
140
+ [
141
+ MyTestClassValMultTypeTest,
142
+ MyTestClassValMultImplTypeTest,
143
+ MyTestClassValMultImpl2TypeTest,
144
+ MyTestClassValMultImpl3TypeTest,
145
+ ].each do |eapi_class|
146
+ eapi = eapi_class.new
147
+ expect(eapi.clear_something).to be eapi
148
+ expect(eapi.something).to be_nil
149
+ end
150
+ end
151
+
152
+ it 'if value respond to clear, it will call it' do
153
+ [
154
+ MyTestClassValMultTypeTest,
155
+ MyTestClassValMultImplTypeTest,
156
+ MyTestClassValMultImpl2TypeTest,
157
+ MyTestClassValMultImpl3TypeTest,
158
+ ].each do |eapi_class|
159
+ o = double()
160
+ expect(o).to receive(:clear)
161
+
162
+ eapi = eapi_class.new something: o
163
+ expect(eapi.clear_something).to be eapi
164
+ end
165
+ end
166
+
167
+ it 'if value do not respond to clear, it will call the `init_something` method' do
168
+ [
169
+ MyTestClassValMultTypeTest,
170
+ MyTestClassValMultImplTypeTest,
171
+ MyTestClassValMultImpl2TypeTest,
172
+ MyTestClassValMultImpl3TypeTest,
173
+ ].each do |eapi_class|
174
+ o = double()
175
+ eapi = eapi_class.new something: o
176
+ expect(eapi).to receive(:init_something)
177
+ expect(eapi.clear_something).to be eapi
178
+ end
179
+ end
180
+
181
+ it 'if value do not respond to clear and we do not have `init_something` method, raise exception' do
182
+ eapi = MyTestClassValMultImpl3TypeTest.new something: MyMultipleValueTestKlass.new
183
+ expect { eapi.clear_something }.to raise_exception()
184
+ end
185
+
186
+ end
187
+
87
188
  describe 'element validation' do
88
189
  class MyTestClassValElements
89
190
  include Eapi::Item