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.
@@ -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
@@ -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
@@ -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
- normal_setter = Eapi::Methods::Names.setter field
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
- run_definition fs, definition
22
- store_definition fs, definition
34
+ run_property_definition fs, definition
35
+ store_property_definition fs, definition
23
36
  end
24
37
 
25
38
  def properties
26
- @_definitions.keys
39
+ @_property_definitions.keys
27
40
  end
28
41
 
29
42
  def definition_for(field)
30
- @_definitions ||= {}
31
- @_definitions.fetch(field.to_sym, {}).dup
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 store_definition(field, definition)
35
- @_definitions ||= {}
36
- @_definitions[field] = definition
70
+ def store_list_definition(definition)
71
+ @_list_definition = definition
37
72
  end
38
73
 
39
- def run_definition(field, definition)
40
- Eapi::DefinitionRunner.new(self, field, definition).run
74
+ def run_list_definition(definition)
75
+ Eapi::DefinitionRunners::List.new(self, definition).run
41
76
  end
42
77
 
43
- private :run_definition
44
- private :store_definition
78
+ private :run_list_definition
79
+ private :store_list_definition
45
80
  end
46
81
  end
47
82
 
@@ -1,5 +1,5 @@
1
1
  module Eapi
2
- module Multiple
2
+ module MultipleValue
3
3
  extend ActiveSupport::Concern
4
4
  included do
5
5
  end
@@ -1,3 +1,3 @@
1
1
  module Eapi
2
- VERSION = "0.1.2"
2
+ VERSION = "0.2.0"
3
3
  end