otoroshi 0.0.7 → 0.1.2

Sign up to get free protection for your applications and to get access to all the features.
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