eapi 0.1.2 → 0.2.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 +1 -2
- data/README.md +85 -26
- data/eapi.gemspec +1 -0
- data/lib/eapi.rb +7 -2
- data/lib/eapi/common.rb +11 -28
- data/lib/eapi/definition_runners.rb +3 -0
- data/lib/eapi/definition_runners/list.rb +60 -0
- data/lib/eapi/definition_runners/property.rb +100 -0
- data/lib/eapi/definition_runners/runner.rb +44 -0
- data/lib/eapi/item.rb +31 -0
- data/lib/eapi/list.rb +145 -0
- data/lib/eapi/methods/accessor.rb +1 -23
- data/lib/eapi/methods/properties.rb +47 -12
- data/lib/eapi/{multiple.rb → multiple_value.rb} +1 -1
- data/lib/eapi/version.rb +1 -1
- data/spec/basic_spec.rb +36 -35
- data/spec/definition_spec.rb +1 -1
- data/spec/extension_spec.rb +2 -2
- data/spec/function_spec.rb +70 -41
- data/spec/list_elements_spec.rb +123 -0
- data/spec/list_spec.rb +378 -81
- data/spec/to_h_spec.rb +1 -1
- data/spec/type_spec.rb +2 -2
- data/spec/validations_spec.rb +8 -8
- metadata +25 -4
- data/lib/eapi/definition_runner.rb +0 -109
@@ -0,0 +1,100 @@
|
|
1
|
+
module Eapi
|
2
|
+
module DefinitionRunners
|
3
|
+
|
4
|
+
class Property < Struct.new(:klass, :field, :definition)
|
5
|
+
def run
|
6
|
+
run_multiple_accessor
|
7
|
+
run_init
|
8
|
+
run_validations
|
9
|
+
end
|
10
|
+
|
11
|
+
private
|
12
|
+
def run_validations
|
13
|
+
run_required
|
14
|
+
run_validate_type
|
15
|
+
run_validate_with
|
16
|
+
run_validate_element_type
|
17
|
+
run_validate_element_with
|
18
|
+
end
|
19
|
+
|
20
|
+
def run_required
|
21
|
+
if required?
|
22
|
+
Runner.required klass: klass, field: field
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def run_validate_element_with
|
27
|
+
if multiple? && validate_element_with
|
28
|
+
Runner.validate_element_with klass: klass, field: field, validate_element_with: validate_element_with
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_validate_element_type
|
33
|
+
if multiple? && element_type
|
34
|
+
Runner.validate_element_type(klass: klass, field: field, element_type: element_type)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def run_validate_type
|
39
|
+
if type
|
40
|
+
klass.send :validates_each, field do |record, attr, value|
|
41
|
+
unless Eapi::TypeChecker.new(type).is_valid_type?(value)
|
42
|
+
record.errors.add(attr, "must be a #{type}")
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def run_validate_with
|
49
|
+
if validate_with
|
50
|
+
klass.send :validates_each, field do |record, attr, value|
|
51
|
+
validate_with.call(record, attr, value)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
def run_init
|
57
|
+
if type || multiple?
|
58
|
+
klass.send :define_init, field, type || Array
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def run_multiple_accessor
|
63
|
+
if multiple?
|
64
|
+
klass.send :define_multiple_accessor, field
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def type_multiple?(type)
|
69
|
+
return false if type.nil?
|
70
|
+
return true if type == Array || type == Set
|
71
|
+
|
72
|
+
type.respond_to?(:is_multiple?) && type.is_multiple?
|
73
|
+
end
|
74
|
+
|
75
|
+
def validate_element_with
|
76
|
+
definition.fetch(:validate_element_with, nil)
|
77
|
+
end
|
78
|
+
|
79
|
+
def multiple?
|
80
|
+
definition.fetch(:multiple, false) || type_multiple?(type)
|
81
|
+
end
|
82
|
+
|
83
|
+
def required?
|
84
|
+
definition.fetch(:required, false)
|
85
|
+
end
|
86
|
+
|
87
|
+
def validate_with
|
88
|
+
definition.fetch(:validate_with, nil)
|
89
|
+
end
|
90
|
+
|
91
|
+
def element_type
|
92
|
+
definition.fetch(:element_type, nil)
|
93
|
+
end
|
94
|
+
|
95
|
+
def type
|
96
|
+
definition.fetch(:type, nil)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
module Eapi
|
2
|
+
module DefinitionRunners
|
3
|
+
class Runner
|
4
|
+
def self.validate_element_with(klass:, field:, validate_element_with:)
|
5
|
+
klass.send :validates_each, field do |record, attr, value|
|
6
|
+
if value.respond_to?(:each)
|
7
|
+
value.each do |v|
|
8
|
+
validate_element_with.call(record, attr, v)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.validate_element_type(klass:, field:, element_type:)
|
15
|
+
klass.send :validates_each, field do |record, attr, value|
|
16
|
+
if value.respond_to?(:each)
|
17
|
+
value.each do |v|
|
18
|
+
unless Eapi::TypeChecker.new(element_type).is_valid_type?(v)
|
19
|
+
record.errors.add(attr, "element must be a #{element_type}")
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def self.required(klass:, field:)
|
27
|
+
klass.send :validates_presence_of, field
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.unique(klass:, field:)
|
31
|
+
klass.send :validates_each, field do |record, attr, value|
|
32
|
+
if value.respond_to?(:group_by)
|
33
|
+
grouped = value.group_by { |i| i }
|
34
|
+
repeated_groups = grouped.select { |k, v| v.size > 1 }
|
35
|
+
unless repeated_groups.empty?
|
36
|
+
repeated = Hash[repeated_groups.map { |k, v| [k, v.size] }]
|
37
|
+
record.errors.add(attr, "elements must be unique (repeated elements: #{repeated})")
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
data/lib/eapi/item.rb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
module Eapi
|
2
|
+
module Item
|
3
|
+
extend Common
|
4
|
+
|
5
|
+
def self.extended(mod)
|
6
|
+
def mod.included(klass)
|
7
|
+
Eapi::Common.add_features klass
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.included(klass)
|
12
|
+
Eapi::Common.add_features klass
|
13
|
+
end
|
14
|
+
|
15
|
+
def render
|
16
|
+
validate!
|
17
|
+
create_hash
|
18
|
+
end
|
19
|
+
|
20
|
+
alias_method :to_h, :render
|
21
|
+
|
22
|
+
def create_hash
|
23
|
+
{}.tap do |hash|
|
24
|
+
_properties.each do |prop|
|
25
|
+
val = converted_value_for(prop)
|
26
|
+
hash[prop] = val unless val.nil?
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
data/lib/eapi/list.rb
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
module Eapi
|
2
|
+
module List
|
3
|
+
include Comparable
|
4
|
+
include Enumerable
|
5
|
+
extend Common
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def is_multiple?
|
9
|
+
true
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.add_features(klass)
|
14
|
+
Eapi::Common.add_features klass
|
15
|
+
klass.extend(ClassMethods)
|
16
|
+
klass.extend(Eapi::Methods::Properties::ListCLassMethods)
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.extended(mod)
|
20
|
+
def mod.included(klass)
|
21
|
+
Eapi::List.add_features klass
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.included(klass)
|
26
|
+
Eapi::List.add_features klass
|
27
|
+
end
|
28
|
+
|
29
|
+
def is_multiple?
|
30
|
+
true
|
31
|
+
end
|
32
|
+
|
33
|
+
def render
|
34
|
+
validate!
|
35
|
+
create_array
|
36
|
+
end
|
37
|
+
|
38
|
+
alias_method :to_a, :render
|
39
|
+
|
40
|
+
def create_array
|
41
|
+
_list.map { |val| convert_value val }
|
42
|
+
end
|
43
|
+
|
44
|
+
def _list
|
45
|
+
@_list ||= []
|
46
|
+
end
|
47
|
+
|
48
|
+
def add(value)
|
49
|
+
self << value
|
50
|
+
self
|
51
|
+
end
|
52
|
+
|
53
|
+
def <=>(other)
|
54
|
+
(_list <=> other) || (other.respond_to?(:_list) && _list <=> other._list)
|
55
|
+
end
|
56
|
+
|
57
|
+
# From Array
|
58
|
+
|
59
|
+
# ary.replace(other_ary) -> ary
|
60
|
+
# ary.initialize_copy(other_ary) -> ary
|
61
|
+
#
|
62
|
+
# Replaces the contents of +self+ with the contents of +other_ary+,
|
63
|
+
# truncating or expanding if necessary.
|
64
|
+
#
|
65
|
+
# a = [ "a", "b", "c", "d", "e" ]
|
66
|
+
# a.replace([ "x", "y", "z" ]) #=> ["x", "y", "z"]
|
67
|
+
# a #=> ["x", "y", "z"]
|
68
|
+
def initialize_copy(other_ary)
|
69
|
+
if other_ary.kind_of? List
|
70
|
+
@_list = other_ary._list.dup
|
71
|
+
elsif other_ary.respond_to? :to_a
|
72
|
+
@_list = other_ary.to_a
|
73
|
+
else
|
74
|
+
raise ArgumentError, 'must be either a List or respond to `to_a`'
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
protected :initialize_copy
|
79
|
+
|
80
|
+
# transpose, assoc, rassoc , permutation, combination, repeated_permutation, repeated_combination, product, pack ?? => do not use the methods
|
81
|
+
end
|
82
|
+
|
83
|
+
class ListMethodDefiner
|
84
|
+
|
85
|
+
DESTRUCTIVE_SELF_OR_NIL = [:uniq!, :compact!, :flatten!, :shuffle!, :concat, :clear, :replace, :fill, :reverse!, :rotate!, :sort!, :keep_if]
|
86
|
+
|
87
|
+
DUP_METHODS = [:uniq, :compact, :flatten, :shuffle, :+, :-, :&, :|, :reverse, :rotate, :sort, :split, :in_groups, :in_groups_of, :from, :to]
|
88
|
+
|
89
|
+
DELEGATED_METHODS = [
|
90
|
+
# normal delegation
|
91
|
+
:frozen?, :[], :[]=, :at, :fetch, :first, :last, :<<, :push, :pop, :shift, :unshift, :insert, :length, :size, :empty?, :rindex, :join, :collect, :map, :select, :values_at, :delete, :delete_at, :delete_if, :reject, :include?, :count, :sample, :bsearch, :to_json_without_active_support_encoder, :slice, :slice!, :sort_by!, :shuffle, :shuffle!,
|
92
|
+
|
93
|
+
# for Enumerable
|
94
|
+
:each, :each_index,
|
95
|
+
|
96
|
+
# pose as array
|
97
|
+
:to_ary, :*,
|
98
|
+
|
99
|
+
# active support
|
100
|
+
:shelljoin, :append, :prepend, :extract_options!, :to_sentence, :to_formatted_s, :to_default_s, :to_xml, :second, :third, :fourth, :fifth, :forty_two, :to_param, :to_query,
|
101
|
+
|
102
|
+
# destructive that return selection
|
103
|
+
:collect!, :map!, :select!, :reject!,
|
104
|
+
]
|
105
|
+
|
106
|
+
def self.finalise(klass)
|
107
|
+
delegate_methods_to_list klass
|
108
|
+
pose_as_array klass
|
109
|
+
destructive_self_or_nil klass
|
110
|
+
dup_methods klass
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
def self.delegate_methods_to_list(klass)
|
115
|
+
klass.send :delegate, *DELEGATED_METHODS, to: :_list
|
116
|
+
end
|
117
|
+
|
118
|
+
|
119
|
+
def self.destructive_self_or_nil(klass)
|
120
|
+
# Destructive methods that return self or nil
|
121
|
+
DESTRUCTIVE_SELF_OR_NIL.each do |m|
|
122
|
+
klass.send :define_method, m do |*args, &block|
|
123
|
+
res = _list.send m, *args, &block
|
124
|
+
res.nil? ? nil : self
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.pose_as_array(klass)
|
130
|
+
klass.send :alias_method, :index, :find_index
|
131
|
+
end
|
132
|
+
|
133
|
+
def self.dup_methods(klass)
|
134
|
+
# Non destructive methods that return a new object
|
135
|
+
DUP_METHODS.each do |m|
|
136
|
+
klass.send :define_method, m do |*args, &block|
|
137
|
+
dup.tap { |n| n.initialize_copy(n._list.send m, *args, &block) }
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
end
|
143
|
+
|
144
|
+
ListMethodDefiner.finalise List
|
145
|
+
end
|
@@ -26,29 +26,7 @@ module Eapi
|
|
26
26
|
end
|
27
27
|
|
28
28
|
def define_accessors(field)
|
29
|
-
|
30
|
-
fluent_setter = Eapi::Methods::Names.fluent_setter field
|
31
|
-
getter = Eapi::Methods::Names.getter field
|
32
|
-
instance_var = Eapi::Methods::Names.instance_var field
|
33
|
-
|
34
|
-
define_method normal_setter do |value|
|
35
|
-
instance_variable_set instance_var, value
|
36
|
-
end
|
37
|
-
|
38
|
-
# fluent setter that calls the normal setter and returns self
|
39
|
-
define_method fluent_setter do |value|
|
40
|
-
send normal_setter, value
|
41
|
-
self
|
42
|
-
end
|
43
|
-
|
44
|
-
# special getter => if no arguments it is a getter, if arguments it calls the fluent setter
|
45
|
-
define_method getter do |*args|
|
46
|
-
if args.empty?
|
47
|
-
instance_variable_get instance_var
|
48
|
-
else
|
49
|
-
send fluent_setter, *args
|
50
|
-
end
|
51
|
-
end
|
29
|
+
fluent_accessor field
|
52
30
|
end
|
53
31
|
end
|
54
32
|
end
|
@@ -3,6 +3,19 @@ module Eapi
|
|
3
3
|
|
4
4
|
module Properties
|
5
5
|
module InstanceMethods
|
6
|
+
|
7
|
+
def _properties
|
8
|
+
self.class.properties
|
9
|
+
end
|
10
|
+
|
11
|
+
def converted_value_for(prop)
|
12
|
+
convert_value get(prop)
|
13
|
+
end
|
14
|
+
|
15
|
+
def convert_value(value)
|
16
|
+
Eapi::ValueConverter.convert_value(value)
|
17
|
+
end
|
18
|
+
|
6
19
|
def get(field)
|
7
20
|
getter = Eapi::Methods::Names.getter field
|
8
21
|
send(getter)
|
@@ -18,30 +31,52 @@ module Eapi
|
|
18
31
|
def property(field, definition = {})
|
19
32
|
fs = field.to_sym
|
20
33
|
define_accessors fs
|
21
|
-
|
22
|
-
|
34
|
+
run_property_definition fs, definition
|
35
|
+
store_property_definition fs, definition
|
23
36
|
end
|
24
37
|
|
25
38
|
def properties
|
26
|
-
@
|
39
|
+
@_property_definitions.keys
|
27
40
|
end
|
28
41
|
|
29
42
|
def definition_for(field)
|
30
|
-
@
|
31
|
-
@
|
43
|
+
@_property_definitions ||= {}
|
44
|
+
@_property_definitions.fetch(field.to_sym, {}).dup
|
45
|
+
end
|
46
|
+
|
47
|
+
def store_property_definition(field, definition)
|
48
|
+
@_property_definitions ||= {}
|
49
|
+
@_property_definitions[field] = definition
|
50
|
+
end
|
51
|
+
|
52
|
+
def run_property_definition(property_field, definition)
|
53
|
+
Eapi::DefinitionRunners::Property.new(self, property_field, definition).run
|
54
|
+
end
|
55
|
+
|
56
|
+
private :run_property_definition
|
57
|
+
private :store_property_definition
|
58
|
+
end
|
59
|
+
|
60
|
+
module ListCLassMethods
|
61
|
+
def elements(definition)
|
62
|
+
run_list_definition definition
|
63
|
+
store_list_definition definition
|
64
|
+
end
|
65
|
+
|
66
|
+
def definition_for_elements
|
67
|
+
@_list_definition
|
32
68
|
end
|
33
69
|
|
34
|
-
def
|
35
|
-
@
|
36
|
-
@_definitions[field] = definition
|
70
|
+
def store_list_definition(definition)
|
71
|
+
@_list_definition = definition
|
37
72
|
end
|
38
73
|
|
39
|
-
def
|
40
|
-
Eapi::
|
74
|
+
def run_list_definition(definition)
|
75
|
+
Eapi::DefinitionRunners::List.new(self, definition).run
|
41
76
|
end
|
42
77
|
|
43
|
-
private :
|
44
|
-
private :
|
78
|
+
private :run_list_definition
|
79
|
+
private :store_list_definition
|
45
80
|
end
|
46
81
|
end
|
47
82
|
|
data/lib/eapi/version.rb
CHANGED