otoroshi 0.0.7 → 0.1.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: cded2f7b556f68f150dbccb50c566a6b131dac220eedd220b039ee96f17fb083
4
- data.tar.gz: 2ab1413e813f5cf917bed90e0bd8f7c6928827af24acdf6618e1f29e66c82dc9
3
+ metadata.gz: dc27b22c72bffa23a8dc1d4438ec57c246ac7277c4f505c5e4f812b938cda424
4
+ data.tar.gz: fc408beba9ffae09f881407b2ec36abe200d41cd02505a2ca731773b1ed7c56c
5
5
  SHA512:
6
- metadata.gz: 11a8543961a2de58d5004968557c68db65f85251403a552a5f7a50c5b946234f215d812859f72a7fa044a03f4116d61b14b01116f9986676a0d0945a9f8f719f
7
- data.tar.gz: e27749e5ce0065a0ae3ee551538a1bbf7f191bb33a29f34cdcad1f173b463d429e846b1973d2b096b5004bce450a625ffbe8d407a301535eca51d8ff6f618dab
6
+ metadata.gz: 2d1183f56272d15fdf58ba062a6f153b8e2679adffc4da06c3a75bafaab0ae5be1f155cd85df8dfc015b83df6476538013015eeea4a55f791616c486f7a86142
7
+ data.tar.gz: 94eb25ded2a7fced1518ce173a6633c5863188b456b883458b2ec9dafe97b0e456dad15c3c5cebbb72d341af3eadc887863867410dfbe847fdfc52587c4bcff8
@@ -1,8 +1,7 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require_relative 'otoroshi/sanctuary'
4
- require_relative 'otoroshi/exceptions'
5
-
6
3
  # Otoroshi entry-point
7
- #
8
- module Otoroshi; end
4
+ module Otoroshi
5
+ require_relative 'otoroshi/sanctuary'
6
+ require_relative 'otoroshi/exceptions'
7
+ end
@@ -1,73 +1,89 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require_relative './initializer'
4
+
3
5
  module Otoroshi
4
6
  class Error < StandardError; end
5
7
 
6
- # Manages errors raised when value type is not as expected
7
- class WrongTypeError < Error
8
- # Initialize an error
9
- #
8
+ # Manages errors raised when value is not an instance of the expected class
9
+ class TypeError < Error
10
10
  # @param property [Symbol] name of the property
11
11
  # @param type [Class] class to match
12
- # @param array [true, false] define if it is an array
13
- def initialize(property, type, array: false)
14
- expected_type = type.is_a?(Array) ? type.first || Object : type
15
- msg =
16
- if array
17
- ":#{property} contains elements that are not instances of #{expected_type}"
18
- else
19
- ":#{property} is not an instance of #{expected_type}"
20
- end
21
- super(msg)
22
- end
23
- end
24
-
25
- # Manages errors raised when value should be an array
26
- class NotAnArray < Error
27
- # Initialize an error
28
- #
29
- # @param property [Symbol] name of the property
30
- def initialize(property)
31
- msg = ":#{property} is not an array"
32
- super(msg)
12
+ # @example
13
+ # ":number is not an instance of Integer"
14
+ def initialize(property, type)
15
+ super ":#{property} is not an instance of #{type}"
33
16
  end
34
17
  end
35
18
 
36
- # Manages errors raised when value is not accepted (not included in the "one_of")
37
- class NotAcceptedError < Error
38
- # Initialize an error
39
- #
19
+ # Manages errors raised when value is not accepted (not in the "one_of")
20
+ class OneOfError < Error
40
21
  # @param property [Symbol] name of the property
41
- # @param accepted_values [Array] accepted values
42
- # @param array [true, false] define if it is an array
43
- def initialize(property, accepted_values, array: false)
22
+ # @param values [Array] accepted values
23
+ # @example
24
+ # ":fruit is not in [:apple, :pear]"
25
+ def initialize(property, values)
44
26
  # reintegrate the colon for symbols which is lost during interpolation
45
27
  to_s = ->(v) { v.is_a?(Symbol) ? ":#{v}" : v }
46
- accepted_values_list = accepted_values.map { |v| to_s.call(v) }.join(', ')
47
- msg =
48
- if array
49
- ":#{property} contains elements that are not included in [#{accepted_values_list}]"
50
- else
51
- ":#{property} is not included in [#{accepted_values_list}]"
52
- end
53
- super(msg)
28
+ list = values.map { |v| to_s.call(v) }.join(', ')
29
+ super ":#{property} is not in [#{list}]"
54
30
  end
55
31
  end
56
32
 
57
33
  # Manages errors raised when value does not pass the assertion
58
- class AssertionError < Error
59
- # Initialize an error
60
- #
34
+ class AssertError < Error
61
35
  # @param property [Symbol] name of the property
62
- # @param array [true, false] define if it is an array
63
- def initialize(property, array: false)
64
- msg =
65
- if array
66
- ":#{property} contains elements that do not respect the assertion"
67
- else
68
- ":#{property} does not respect the assertion"
69
- end
70
- super(msg)
36
+ # @example
37
+ # ":number does not respect the assertion"
38
+ def initialize(property)
39
+ super ":#{property} does not respect the assertion"
40
+ end
41
+ end
42
+
43
+ module Collection
44
+ # Manages errors raised when value should be an collection
45
+ class ArrayError < Error
46
+ # @param property [Symbol] name of the property
47
+ # @example
48
+ # ":numbers is not an array"
49
+ def initialize(property)
50
+ super ":#{property} is not an array"
51
+ end
52
+ end
53
+
54
+ # Manages errors raised when at least one element of the collection is not an instance of the expected class
55
+ class TypeError < Error
56
+ # @param property [Symbol] name of the property
57
+ # @param type [Class] class to match
58
+ # @example
59
+ # ":numbers contains elements that are not instances of Integer"
60
+ def initialize(property, type)
61
+ super ":#{property} contains elements that are not instances of #{type}"
62
+ end
63
+ end
64
+
65
+ # Manages errors raised when at least one element of the collection is not accepted (not in the "one_of")
66
+ class OneOfError < Error
67
+ # @param property [Symbol] name of the property
68
+ # @param values [Array] accepted values
69
+ # @example
70
+ # ":fruits contains elements that are not in [:apple, :pear]"
71
+ def initialize(property, values)
72
+ # reintegrate the colon for symbols which is lost during interpolation
73
+ to_s = ->(v) { v.is_a?(Symbol) ? ":#{v}" : v }
74
+ list = values.map { |v| to_s.call(v) }.join(', ')
75
+ super ":#{property} contains elements that are not in [#{list}]"
76
+ end
77
+ end
78
+
79
+ # Manages errors raised when value does not pass the assertion
80
+ class AssertError < Error
81
+ # @param property [Symbol] name of the property
82
+ # @example
83
+ # ":numbers contains elements that do not respect the assertion"
84
+ def initialize(property)
85
+ super ":#{property} contains elements that do not respect the assertion"
86
+ end
71
87
  end
72
88
  end
73
89
  end
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Otoroshi
4
+ # Drawing of #initialize method
5
+ #
6
+ class Initializer
7
+ class << self
8
+ # Draw a stringified initialize method
9
+ #
10
+ # @param properties [Hash] a description of the class properties
11
+ #
12
+ # @return [String]
13
+ #
14
+ # @example
15
+ # <<-RUBY
16
+ # def initialize(number: 0, message:, fruits: [])
17
+ # self.number = number
18
+ # self.message = message
19
+ # self.fruits = fruits
20
+ # end
21
+ # RUBY
22
+ def draw(properties)
23
+ new(properties).draw
24
+ end
25
+ end
26
+
27
+ # Initialize an instance
28
+ #
29
+ # @param properties [Hash] a description of the class properties
30
+ def initialize(properties = {})
31
+ @properties = properties
32
+ end
33
+
34
+ # Draws a stringified initialize method
35
+ #
36
+ # @return [String]
37
+ #
38
+ # @example
39
+ # <<-RUBY
40
+ # def initialize(foo:, bar: 0)
41
+ # self.foo = foo
42
+ # self.bar = bar
43
+ # end
44
+ # RUBY
45
+ def draw
46
+ <<~RUBY
47
+ def initialize(#{initialize_parameters})
48
+ #{initialize_body}
49
+ end
50
+ RUBY
51
+ end
52
+
53
+ private
54
+
55
+ attr_reader :properties
56
+
57
+ # Generates initialize method parameters
58
+ #
59
+ # @return [String]
60
+ #
61
+ # @example
62
+ # "foo:, bar: 0"
63
+ def initialize_parameters
64
+ parameters =
65
+ properties.map do |key, options|
66
+ "#{key}:#{default_parameter_for(options)}"
67
+ end
68
+ parameters.join(', ')
69
+ end
70
+
71
+ # Generates the default value of a parameter depending on options
72
+ #
73
+ # @return [String]
74
+ #
75
+ # @example when nil is allowed and default is set
76
+ # " \"default\""
77
+ # @example when nil is allowed and default is not set
78
+ # " nil"
79
+ # @example when nil is not allowed
80
+ # ""
81
+ def default_parameter_for(options)
82
+ default, allow_nil = options.values_at(:default, :allow_nil)
83
+ return " #{default.call}" if default.is_a? Proc
84
+ return ' nil' if default.nil? && allow_nil
85
+ return '' if default.nil? && !allow_nil
86
+
87
+ " #{prefix(default)}#{default}#{suffix(default)}"
88
+ end
89
+
90
+ # Generates the characters to put before the value
91
+ # @note it avoids symbol without colon or string without quotes
92
+ # which would be interpreted as methods
93
+ def prefix(default)
94
+ case default
95
+ when Symbol then ':'
96
+ when String then '"'
97
+ when Time, Date, DateTime then '"'
98
+ end
99
+ end
100
+
101
+ # Generates the characters to put after the value
102
+ # @note it avoids string without quotes which would be interpreted as method
103
+ def suffix(default)
104
+ case default
105
+ when String then '"'
106
+ when Time, Date, DateTime then '"'
107
+ end
108
+ end
109
+
110
+ # Generates initialize method assignments
111
+ #
112
+ # @return [String]
113
+ #
114
+ # @example Given properties { foo: { allow_nil: false, default: nil }, { allow_nil: true, default: 0 } }
115
+ # <<-RUBY
116
+ # self.foo = foo
117
+ # self.bar = bar
118
+ # RUBY
119
+ def initialize_body
120
+ assignments =
121
+ properties.keys.map do |name|
122
+ "self.#{name} = #{name}"
123
+ end
124
+ assignments.join("\n ")
125
+ end
126
+ end
127
+ end
@@ -1,9 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'awesome_print'
3
+ require_relative 'initializer'
4
4
 
5
5
  module Otoroshi
6
- # This module is designed to be included in a class. This will provide
6
+ # This module is designed to be in a class. This will provide
7
7
  # the "property" ({Sanctuary::ClassMethods.property}) method for defining class properties.
8
8
  #
9
9
  # @example
@@ -16,7 +16,7 @@ module Otoroshi
16
16
  # property :positive_number, Integer, verify: ->(v) { v >= 0 }
17
17
  # property :number_or_nil, Integer, allow_nil: true
18
18
  # property :number_with_default, Integer, default: 42
19
- # property :number_from_collection, Integer, one_of: [1, 2, 3, 5, 8, 13, 21, 34]
19
+ # property :number_in_collection, Integer, one_of: [1, 2, 3, 5, 8, 13, 21, 34]
20
20
  # end
21
21
  module Sanctuary
22
22
  class << self
@@ -27,7 +27,7 @@ module Otoroshi
27
27
  end
28
28
 
29
29
  # Class methods extended for the base class
30
- module ClassMethods # rubocop:disable Metrics/ModuleLength
30
+ module ClassMethods
31
31
  # Adds a new "property" to the class
32
32
  #
33
33
  # @param name [Symbol] the property name
@@ -43,357 +43,184 @@ module Otoroshi
43
43
  # property name, type: String, assert: ->(v) { v.length > 3 }, allow_nil: true
44
44
  # @example
45
45
  # property scores, type: [Integer], assert: ->(v) { v >= 0 }, default: []
46
- def property( # rubocop:disable Metrics/ParameterLists
47
- name,
48
- type = Object,
49
- one_of: nil,
50
- assert: ->(_) { true },
51
- allow_nil: false,
52
- default: nil
53
- )
54
- add_to_properties(name, type, one_of, assert, allow_nil, default)
55
- define_validate_type!(name, type, allow_nil)
56
- define_validate_inclusion!(name, type, one_of, allow_nil)
57
- define_validate_assertion!(name, type, assert, allow_nil)
46
+ def property(name, type = Object, one_of: nil, assert: ->(_) { true }, allow_nil: false, default: nil)
47
+ add_to_properties(name, type, allow_nil, default)
48
+ expected_type = type.is_a?(Array) ? type.first || Object : type
49
+ collection = expected_type == Array || type.is_a?(Array)
50
+ define_validate_type(name, expected_type, collection, allow_nil)
51
+ define_validate_one_of(name, collection, one_of, allow_nil)
52
+ define_validate_assertion(name, collection, assert, allow_nil)
53
+ define_validate(name)
58
54
  define_getter(name)
59
55
  define_setter(name)
60
- redefine_initialize
56
+ class_eval Initializer.draw(properties), __FILE__, __LINE__ + 1
61
57
  end
62
58
 
63
- # Checks the type is an array
64
- #
65
- # @param type [Class, Array<Class>] the tested type
66
- #
67
- # @return [true, false]
68
- def collection?(type)
69
- type == Array || type.is_a?(Array)
70
- end
71
-
72
- # Returns the class properties
59
+ # Class properties
73
60
  #
74
61
  # @return [Hash]
75
62
  #
76
- # @note this method will be updated by {add_to_properties}
63
+ # @example
64
+ # {
65
+ # number: { type: Integer, allow_nil: false, default: 0 },
66
+ # message: { type: Integer, allow_nil: true, default: nil }
67
+ # }
77
68
  def properties
78
69
  {}
79
70
  end
80
71
 
81
72
  private
82
73
 
83
- # Updates {properties} to add new property to the returned ones
84
- #
85
- # @return [void]
86
- def add_to_properties(name, type, one_of, assert, allow_nil, default) # rubocop:disable Metrics/ParameterLists
74
+ # Adds a properties to the {properties}
75
+ def add_to_properties(name, type, allow_nil, default)
87
76
  current_state = properties
88
77
  current_state[name] = {
89
78
  type: type,
90
- one_of: one_of,
91
- assert: assert,
92
79
  allow_nil: allow_nil,
93
80
  default: default
94
81
  }
95
82
  define_singleton_method(:properties) { current_state }
96
83
  end
97
84
 
98
- # Defines a private method that raises an error if type is not respected
99
- #
100
- # @param name [Symbol] the property name
101
- # @param type [Class] the type to test against
102
- # @param array [true, false] define if the value is an array
103
- # @param allow_nil [true, false] allow nil as a value
85
+ # Defines a private method that validates type condition
104
86
  #
105
- # @return [void]
87
+ # Given name = :score, type = Integer, allow_nil = false
106
88
  #
107
- # @example
108
- # define_validate_type!(score, Integer, false) => def validate_score_type!(value) ...
109
- # @example Generated method
110
- # def validate_score_type!(value)
89
+ # def validate_score_type(value)
111
90
  # return if Integer.nil? || false && value.nil?
112
91
  # return if value.is_a? Integer
113
92
  #
114
- # raise ArgumentError, ":score does not match required type"
93
+ # raise Otoroshi::TypeError, :score, Integer
115
94
  # end
116
- def define_validate_type!(name, type, allow_nil)
117
- lambda = type_validation(type)
118
- is_array = collection?(type)
119
- check_array = ->(v) { v.is_a?(Array) || raise(Otoroshi::NotAnArray, name) }
120
- define_method :"validate_#{name}_type!" do |value|
121
- return if allow_nil && value.nil?
122
-
123
- is_array && check_array.call(value)
124
- return if lambda.call(value)
125
-
126
- raise Otoroshi::WrongTypeError.new(name, type, array: is_array)
95
+ def define_validate_type(name, type, collection, allow_nil)
96
+ validator = validate_type?(name, type, collection)
97
+ define_method :"validate_#{name}_type" do |value|
98
+ allow_nil && value.nil? || validator.call(value)
127
99
  end
128
- private :"validate_#{name}_type!"
100
+ private :"validate_#{name}_type"
129
101
  end
130
102
 
131
- # Defines a lambda to be called to validate that value matches the type
132
- #
133
- # @param type [Class] the type to test against
134
- # @param array [true, false] define if the value is an array
135
- #
136
- # @return [Proc] the lambda to use in order to test that the value matches the type
137
- #
138
- # @example
139
- # type_validation(Integer) #=> ->(v) { v.is_a? Integer }
140
- # @example
141
- # type_validation([String, Symbol]) #=> ->(v) { [String, Symbol].any? { |t| v.is_a? t } }
142
- def type_validation(type)
143
- if type == Array
144
- # no expected type for each element, return nil
145
- ->(_) {}
146
- elsif type.is_a?(Array)
147
- # get the real expected type
148
- # e.g. if type is [Integer] then element_type should be Integer
149
- # e.g. if type is [] then element_type should be Object
150
- element_type = type.first || Object
151
- # apply type_validation lambda on each element
152
- ->(v) { v.all? { |e| type_validation(element_type).call(e) } }
103
+ # Lambda that validates (value) respects the type condition
104
+ # @return [Proc]
105
+ def validate_type?(name, type, collection)
106
+ if collection
107
+ # validate each element of (v) is an instance of the type
108
+ lambda do |v|
109
+ v.is_a?(Array) || raise(Otoroshi::Collection::ArrayError, name)
110
+ v.all? { |elt| elt.is_a? type } || raise(Otoroshi::Collection::TypeError.new(name, type))
111
+ end
153
112
  else
154
- # check the value matches the type
155
- ->(v) { v.is_a? type }
113
+ # validate (v) is an instance of the type
114
+ ->(v) { v.is_a?(type) || raise(Otoroshi::TypeError.new(name, type)) }
156
115
  end
157
116
  end
158
117
 
159
- # Defines a private method that raises an error if value is not included in the accepted ones
160
- #
161
- # @param name [Symbol] the property name
162
- # @param array [true, false] define if the value is an array
163
- # @param values [Array, nil] the values to test against
164
- # @param allow_nil [true, false] allow nil as a value
118
+ # Defines a private method that validates one_of condition
165
119
  #
166
- # @return [void]
120
+ # Given name = :side, collection = false, one_of = [:left, :right], allow_nil = false
167
121
  #
168
- # @example
169
- # define_validate_inclusion!(side, [:left, :right], false) => def validate_side_type!(value) ...
170
- # @example Generated method
171
- # def validate_side_type!(value)
122
+ # def validate_side_type(value)
172
123
  # return if false && value.nil?
173
124
  # return if [:left, :right].include? value
174
125
  #
175
- # raise ArgumentError, ":side is not included in accepted values"
126
+ # raise Otoroshi::OneOfError, :side, [:left, :right]
176
127
  # end
177
- def define_validate_inclusion!(name, type, values, allow_nil)
178
- validator = collection?(type) ? each_inside?(name, values) : inside?(name, values)
179
- if values
180
- define_method(:"validate_#{name}_inclusion!") do |value|
181
- allow_nil && value.nil? || validator.call(value)
182
- end
183
- else
184
- define_method(:"validate_#{name}_inclusion!") { |_| }
128
+ def define_validate_one_of(name, collection, one_of, allow_nil)
129
+ validator = validate_one_of?(name, one_of, collection)
130
+ define_method(:"validate_#{name}_one_of") do |value|
131
+ allow_nil && value.nil? || validator.call(value)
185
132
  end
186
- private :"validate_#{name}_inclusion!"
133
+ private :"validate_#{name}_one_of"
187
134
  end
188
135
 
189
- # Defines a lambda to be called to validate that value is included in accepted ones
190
- #
191
- # @param name [Symbol] the property name
192
- # @param values [Array, nil] the values to test against
193
- #
194
- # @return [Proc] the lambda to use in order to test that value is included in accepted ones
195
- def inside?(name, values)
196
- lambda do |v|
197
- values.include?(v) || raise(Otoroshi::NotAcceptedError.new(name, values))
198
- end
199
- end
136
+ # Lambda that validates (value) respects the one_of condition
137
+ # @return [Proc]
138
+ def validate_one_of?(name, one_of, collection)
139
+ return ->(_) {} unless one_of
200
140
 
201
- # Defines a lambda to be called to validate that each value is included in accepted ones
202
- #
203
- # @param name [Symbol] the property name
204
- # @param values [Array, nil] the values to test against
205
- #
206
- # @return [Proc] the lambda to use in order to test that each value is included in accepted ones
207
- def each_inside?(name, values)
208
- lambda do |v|
209
- v.all? { |e| values.include? e } || raise(Otoroshi::NotAcceptedError.new(name, values, array: true))
141
+ if collection
142
+ lambda do |v|
143
+ v.all? { |e| one_of.include? e } || raise(Otoroshi::Collection::OneOfError.new(name, one_of))
144
+ end
145
+ else
146
+ lambda do |v|
147
+ one_of.include?(v) || raise(Otoroshi::OneOfError.new(name, one_of))
148
+ end
210
149
  end
211
150
  end
212
151
 
213
- # Defines a private method that raises an error if assert lambda returns false
214
- #
215
- # @param name [Symbol] the property name
216
- # @param assert [Proc] a lambda processing the value and returning true or false
217
- # @param allow_nil [true, false] allow nil as a value
152
+ # Defines a private method that validates assert condition
218
153
  #
219
- # @return [void]
154
+ # Given name = :score, assert = ->(v) { v >= 0 }, allow_nil = false
220
155
  #
221
- # @example
222
- # define_validate_assertion!("score", ->(v) { v >= 0 }, false) #=> def validate_score_assertion!(value) ...
223
- # @example Generated instance method
224
- # def validate_score_assertion!(value)
156
+ # def validate_score_assertion(value)
225
157
  # return if false && value.nil?
226
158
  # return if value >= 0
227
159
  #
228
- # raise ArgumentError, ":score does not match validation"
160
+ # raise Otoroshi::AssertError, :score
229
161
  # end
230
- def define_validate_assertion!(name, type, assert, allow_nil)
231
- validator = collection?(type) ? each_assert?(name, assert) : assert?(name, assert)
232
- define_method :"validate_#{name}_assertion!" do |value|
162
+ def define_validate_assertion(name, collection, assert, allow_nil)
163
+ validator = validate_assert?(name, assert, collection)
164
+ define_method :"validate_#{name}_assertion" do |value|
233
165
  allow_nil && value.nil? || validator.call(value)
234
166
  end
235
- private :"validate_#{name}_assertion!"
167
+ private :"validate_#{name}_assertion"
236
168
  end
237
169
 
238
- # Defines a lambda to be called to validate that value respects the specific
239
- #
240
- # @param name [Symbol] the property name
241
- # @param assert [Proc] a lambda processing the value and returning true or false
242
- #
243
- # @return [Proc] the lambda to use in order to test that value respects the specific
244
- def assert?(name, assert)
245
- lambda do |value|
246
- return if instance_exec(value, &assert)
247
-
248
- raise Otoroshi::AssertionError, name
170
+ # Lambda that validates (value) respects the assert condition
171
+ # @return [Proc]
172
+ def validate_assert?(name, assert, collection)
173
+ if collection
174
+ ->(v) { v.all? { |e| instance_exec(e, &assert) } || raise(Otoroshi::Collection::AssertError, name) }
175
+ else
176
+ ->(v) { instance_exec(v, &assert) || raise(Otoroshi::AssertError, name) }
249
177
  end
250
178
  end
251
179
 
252
- # Defines a lambda to be called to validate that value respects the specific
180
+ # Defines a private method that calls all validations
253
181
  #
254
- # @param name [Symbol] the property name
255
- # @param validate [Proc] a lambda processing the value and returning true or false
182
+ # Given name = :score
256
183
  #
257
- # @return [Proc] the lambda to use in order to test that each value respects the specific
258
- def each_assert?(name, validate)
259
- lambda do |value|
260
- return if value.all? { |e| instance_exec(e, &validate) }
261
-
262
- raise Otoroshi::AssertionError.new(name, array: true)
184
+ # def validate_score!(value)
185
+ # validate_score_type(value)
186
+ # validate_score_one_of(value)
187
+ # validate_score_assert(value)
188
+ # end
189
+ def define_validate(name)
190
+ define_method :"validate_#{name}!" do |value|
191
+ __send__(:"validate_#{name}_type", value)
192
+ __send__(:"validate_#{name}_one_of", value)
193
+ __send__(:"validate_#{name}_assertion", value)
263
194
  end
195
+ private :"validate_#{name}!"
264
196
  end
265
197
 
266
- # Defines a getter method for the property
267
- #
268
- # @param name [Symbol] the property name
198
+ # Defines a getter
269
199
  #
270
- # @return [void]
200
+ # Given name = :score
271
201
  #
272
- # @example
273
- # define_getter(:score) #=> def score ...
274
- # @example Generated instance method
275
- # def score
276
- # instance_variable_get(@score)
277
- # end
202
+ # def score
203
+ # instance_variable_get(@score)
204
+ # end
278
205
  def define_getter(name)
279
- define_method(name) { instance_variable_get("@#{name}") }
206
+ define_method(name) { instance_variable_get("@#{name}").clone.freeze }
280
207
  end
281
208
 
282
- # Defines a setter method for the property
209
+ # Defines a setter
283
210
  #
284
- # @param name [Symbol] the property name
285
- #
286
- # @return [void]
211
+ # Given name = :score
287
212
  #
288
- # @example
289
- # define_getter(:score) #=> def score=(value) ...
290
- # @example Generated instance method
291
213
  # def score=(value)
292
- # validate_score_type!(value)
214
+ # validate_score_type(value)
293
215
  # validate_score!(value)
294
216
  # instance_variable_set(@score, value)
295
217
  # end
296
218
  def define_setter(name)
297
219
  define_method :"#{name}=" do |value|
298
- __send__(:"validate_#{name}_type!", value)
299
- __send__(:"validate_#{name}_inclusion!", value)
300
- __send__(:"validate_#{name}_assertion!", value)
220
+ __send__(:"validate_#{name}!", value)
301
221
  instance_variable_set("@#{name}", value)
302
222
  end
303
223
  end
304
-
305
- # Redefines initialize method
306
- #
307
- # @return [void]
308
- #
309
- # @note method is defined with `class_eval`
310
- #
311
- # @example Generated method
312
- # def initialize(foo:, bar: 0)
313
- # self.foo = foo
314
- # self.bar = bar
315
- # end
316
- def redefine_initialize
317
- class_eval <<-RUBY, __FILE__, __LINE__ + 1
318
- def initialize(#{initialize_parameters})
319
- #{initialize_assignments}
320
- #{initialize_push_singletons}
321
- end
322
- RUBY
323
- end
324
-
325
- # Generates initialize method parameters
326
- #
327
- # @return [String]
328
- #
329
- # @example Given properties { foo: { allow_nil: false, default: nil }, { allow_nil: true, default: 0 } }
330
- # redefine_initialize #=> "foo:, bar: 0"
331
- def initialize_parameters
332
- parameters =
333
- properties.map do |key, options|
334
- allow_nil, default = options.values
335
- "#{key}:#{default_parameter_for(allow_nil, default)}"
336
- end
337
- parameters.join(', ')
338
- end
339
-
340
- # Fixes the default value of a parameter depending on options
341
- #
342
- # @param options [Hash]
343
- #
344
- # @return [String]
345
- #
346
- # @example when nil is allowed and default is set
347
- # default_parameter_for(true, 0) #=> " 0"
348
- # @example when nil is allowed and default is not set
349
- # default_parameter_for(true, nil) #=> " nil"
350
- # @example when nil is not allowed
351
- # default_parameter_for(false, nil) #=> ""
352
- def default_parameter_for(allow_nil, default)
353
- if default
354
- symbol_prefix = default.is_a?(Symbol) ? ':' : ''
355
- " #{symbol_prefix}#{default}"
356
- else
357
- allow_nil ? ' nil' : ''
358
- end
359
- end
360
-
361
- # Generates initialize method assignments
362
- #
363
- # @return [String]
364
- #
365
- # @example Given properties { foo: { allow_nil: false, default: nil }, { allow_nil: true, default: 0 } }
366
- # initialize_body #=> "self.foo = foo\nself.bar = bar"
367
- def initialize_assignments
368
- properties.keys.map { |name| "self.#{name} = #{name}" }.join("\n")
369
- end
370
-
371
- # Generates push singleton for each array property
372
- #
373
- # @return [String]
374
- def initialize_push_singletons
375
- collections = properties.select { |_, options| collection?(options[:type]) }
376
- singletons =
377
- collections.keys.map { |name| initialize_push_singleton(name) }
378
- singletons.join("\n")
379
- end
380
-
381
- # Generates singleton on array instance variable to overide <<
382
- # so value is validated before being added to the array
383
- #
384
- # @return [String]
385
- def initialize_push_singleton(name)
386
- <<-RUBY
387
- bind = self
388
- @#{name}.singleton_class.send(:define_method, :<<) do |v|
389
- bind.send(:"validate_#{name}_type!", [v])
390
- bind.send(:"validate_#{name}_inclusion!", [v])
391
- bind.send(:"validate_#{name}_assertion!", [v])
392
-
393
- push(v)
394
- end
395
- RUBY
396
- end
397
224
  end
398
225
  end
399
226
  end