otoroshi 0.0.7 → 0.0.8

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: fba1623080c055e87c2055001a7f2370e459b5fe1a28774375cef3dd9e7da802
4
+ data.tar.gz: 4c01c52a123157e572b4fdcef7c2e7eb6f0cc6ef2162e49237b5a49785708b70
5
5
  SHA512:
6
- metadata.gz: 11a8543961a2de58d5004968557c68db65f85251403a552a5f7a50c5b946234f215d812859f72a7fa044a03f4116d61b14b01116f9986676a0d0945a9f8f719f
7
- data.tar.gz: e27749e5ce0065a0ae3ee551538a1bbf7f191bb33a29f34cdcad1f173b463d429e846b1973d2b096b5004bce450a625ffbe8d407a301535eca51d8ff6f618dab
6
+ metadata.gz: 8b89127bff4d54b14be27fe723db61e6f0786b7f0f9f6d4df612ed49b3d86d59f1a1df55038178662e5ce247c4b2f6af16b35e05650730d8b9889debd354ed48
7
+ data.tar.gz: 177fbc07e846a1cb9cbc7b3e595ce5c0878ff153d6293d9e0ccf986745a0b898e3b2a9be144218b97425ad87b78b4170848b20496ca39bf232ee898f5b1fcb19
@@ -9,11 +9,11 @@ module Otoroshi
9
9
  #
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)
12
+ # @param collection [true, false] define if it is a collection
13
+ def initialize(property, type, collection: false)
14
14
  expected_type = type.is_a?(Array) ? type.first || Object : type
15
15
  msg =
16
- if array
16
+ if collection
17
17
  ":#{property} contains elements that are not instances of #{expected_type}"
18
18
  else
19
19
  ":#{property} is not an instance of #{expected_type}"
@@ -22,7 +22,7 @@ module Otoroshi
22
22
  end
23
23
  end
24
24
 
25
- # Manages errors raised when value should be an array
25
+ # Manages errors raised when value should be an collection
26
26
  class NotAnArray < Error
27
27
  # Initialize an error
28
28
  #
@@ -39,13 +39,13 @@ module Otoroshi
39
39
  #
40
40
  # @param property [Symbol] name of the property
41
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)
42
+ # @param collection [true, false] define if it is an collection
43
+ def initialize(property, accepted_values, collection: false)
44
44
  # reintegrate the colon for symbols which is lost during interpolation
45
45
  to_s = ->(v) { v.is_a?(Symbol) ? ":#{v}" : v }
46
46
  accepted_values_list = accepted_values.map { |v| to_s.call(v) }.join(', ')
47
47
  msg =
48
- if array
48
+ if collection
49
49
  ":#{property} contains elements that are not included in [#{accepted_values_list}]"
50
50
  else
51
51
  ":#{property} is not included in [#{accepted_values_list}]"
@@ -59,10 +59,10 @@ module Otoroshi
59
59
  # Initialize an error
60
60
  #
61
61
  # @param property [Symbol] name of the property
62
- # @param array [true, false] define if it is an array
63
- def initialize(property, array: false)
62
+ # @param collection [true, false] define if it is an collection
63
+ def initialize(property, collection: false)
64
64
  msg =
65
- if array
65
+ if collection
66
66
  ":#{property} contains elements that do not respect the assertion"
67
67
  else
68
68
  ":#{property} does not respect the assertion"
@@ -0,0 +1,137 @@
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(foo:, bar: 0)
17
+ # self.foo = foo
18
+ # self.bar = bar
19
+ # end
20
+ # RUBY
21
+ def draw(properties)
22
+ new(properties).draw
23
+ end
24
+ end
25
+
26
+ # Initialize an instance
27
+ #
28
+ # @param properties [Hash] a description of the class properties
29
+ def initialize(properties = {})
30
+ @properties = properties
31
+ end
32
+
33
+ # Draws a stringified initialize method
34
+ #
35
+ # @return [String]
36
+ #
37
+ # @example
38
+ # <<-RUBY
39
+ # def initialize(foo:, bar: 0)
40
+ # self.foo = foo
41
+ # self.bar = bar
42
+ # end
43
+ # RUBY
44
+ def draw
45
+ <<-RUBY
46
+ def initialize(#{initialize_parameters})
47
+ #{initialize_assignments}
48
+ #{initialize_push_singletons}
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 Given properties { foo: { allow_nil: false, default: nil }, { allow_nil: true, default: 0 } }
62
+ # redefine_initialize #=> "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
+ # Fixes 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_parameter_for #=> " 0"
77
+ # @example when nil is allowed and default is not set
78
+ # default_parameter_for #=> " nil"
79
+ # @example when nil is not allowed
80
+ # default_parameter_for #=> ""
81
+ def default_parameter_for(options)
82
+ default, allow_nil = options.values_at(:default, :allow_nil)
83
+ if default
84
+ symbol_prefix = default.is_a?(Symbol) ? ':' : ''
85
+ " #{symbol_prefix}#{default}"
86
+ else
87
+ allow_nil ? ' nil' : ''
88
+ end
89
+ end
90
+
91
+ # Generates initialize method assignments
92
+ #
93
+ # @return [String]
94
+ #
95
+ # @example Given properties { foo: { allow_nil: false, default: nil }, { allow_nil: true, default: 0 } }
96
+ # initialize_body #=> "self.foo = foo\nself.bar = bar"
97
+ def initialize_assignments
98
+ assignments =
99
+ properties.keys.map do |name|
100
+ "self.#{name} = #{name}"
101
+ end
102
+ assignments.join("\n")
103
+ end
104
+
105
+ # Generates push singleton for each array property
106
+ #
107
+ # @return [String]
108
+ def initialize_push_singletons
109
+ collections =
110
+ properties.select do |_, options|
111
+ options[:type].is_a?(Array) || options[:type] == Array
112
+ end
113
+ singletons =
114
+ collections.keys.map do |name|
115
+ initialize_push_singleton(name)
116
+ end
117
+ singletons.join("\n")
118
+ end
119
+
120
+ # Generates singleton on collection instance variable to overide <<
121
+ # so value is validated before being added to the collection
122
+ #
123
+ # @return [String]
124
+ def initialize_push_singleton(name)
125
+ <<-RUBY
126
+ bind = self
127
+ @#{name}.singleton_class.send(:define_method, :<<) do |v|
128
+ bind.send(:"validate_#{name}_type!", [v])
129
+ bind.send(:"validate_#{name}_inclusion!", [v])
130
+ bind.send(:"validate_#{name}_assertion!", [v])
131
+
132
+ push(v)
133
+ end
134
+ RUBY
135
+ end
136
+ end
137
+ end
@@ -1,6 +1,6 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'awesome_print'
3
+ require_relative 'initializer'
4
4
 
5
5
  module Otoroshi
6
6
  # This module is designed to be included in a class. This will provide
@@ -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,30 +43,15 @@ 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
+ collection = type == Array || type.is_a?(Array)
49
+ define_validate_type!(name, type, collection, allow_nil)
50
+ define_validate_inclusion!(name, collection, one_of, allow_nil)
51
+ define_validate_assertion!(name, collection, assert, allow_nil)
58
52
  define_getter(name)
59
53
  define_setter(name)
60
- redefine_initialize
61
- end
62
-
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)
54
+ class_eval Initializer.draw(properties), __FILE__, __LINE__ + 1
70
55
  end
71
56
 
72
57
  # Returns the class properties
@@ -83,12 +68,10 @@ module Otoroshi
83
68
  # Updates {properties} to add new property to the returned ones
84
69
  #
85
70
  # @return [void]
86
- def add_to_properties(name, type, one_of, assert, allow_nil, default) # rubocop:disable Metrics/ParameterLists
71
+ def add_to_properties(name, type, allow_nil, default)
87
72
  current_state = properties
88
73
  current_state[name] = {
89
74
  type: type,
90
- one_of: one_of,
91
- assert: assert,
92
75
  allow_nil: allow_nil,
93
76
  default: default
94
77
  }
@@ -97,48 +80,42 @@ module Otoroshi
97
80
 
98
81
  # Defines a private method that raises an error if type is not respected
99
82
  #
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
104
- #
105
83
  # @return [void]
106
84
  #
107
85
  # @example
108
- # define_validate_type!(score, Integer, false) => def validate_score_type!(value) ...
86
+ # define_validate_type!(:score, Integer, false) => def validate_score_type!(value) ...
109
87
  # @example Generated method
88
+ # # given name: :score, type: Integer, allow_nil: false
110
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
93
  # raise ArgumentError, ":score does not match required type"
115
94
  # end
116
- def define_validate_type!(name, type, allow_nil)
95
+ def define_validate_type!(name, type, collection, allow_nil)
117
96
  lambda = type_validation(type)
118
- is_array = collection?(type)
119
- check_array = ->(v) { v.is_a?(Array) || raise(Otoroshi::NotAnArray, name) }
97
+ check_collection = ->(v) { v.is_a?(Array) || raise(Otoroshi::NotAnArray, name) }
120
98
  define_method :"validate_#{name}_type!" do |value|
121
99
  return if allow_nil && value.nil?
122
100
 
123
- is_array && check_array.call(value)
101
+ collection && check_collection.call(value)
124
102
  return if lambda.call(value)
125
103
 
126
- raise Otoroshi::WrongTypeError.new(name, type, array: is_array)
104
+ raise Otoroshi::WrongTypeError.new(name, type, collection: collection)
127
105
  end
128
106
  private :"validate_#{name}_type!"
129
107
  end
130
108
 
131
109
  # Defines a lambda to be called to validate that value matches the type
132
110
  #
133
- # @param type [Class] the type to test against
134
- # @param array [true, false] define if the value is an array
135
- #
136
111
  # @return [Proc] the lambda to use in order to test that the value matches the type
137
112
  #
138
113
  # @example
139
114
  # type_validation(Integer) #=> ->(v) { v.is_a? Integer }
140
115
  # @example
141
116
  # type_validation([String, Symbol]) #=> ->(v) { [String, Symbol].any? { |t| v.is_a? t } }
117
+ #
118
+ # @note params are used for binding in define_method scope
142
119
  def type_validation(type)
143
120
  if type == Array
144
121
  # no expected type for each element, return nil
@@ -158,25 +135,22 @@ module Otoroshi
158
135
 
159
136
  # Defines a private method that raises an error if value is not included in the accepted ones
160
137
  #
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
165
- #
166
138
  # @return [void]
167
139
  #
168
140
  # @example
169
- # define_validate_inclusion!(side, [:left, :right], false) => def validate_side_type!(value) ...
141
+ # # given @name = :side
142
+ # define_validate_inclusion!(:side, ...) => def validate_side_type!(value) ...
170
143
  # @example Generated method
144
+ # # given name: :side, collection: false, one_of: [:left, :right], allow_nil: false
171
145
  # def validate_side_type!(value)
172
146
  # return if false && value.nil?
173
147
  # return if [:left, :right].include? value
174
148
  #
175
149
  # raise ArgumentError, ":side is not included in accepted values"
176
150
  # 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
151
+ def define_validate_inclusion!(name, collection, one_of, allow_nil)
152
+ validator = collection ? each_inside?(name, one_of) : inside?(name, one_of)
153
+ if one_of
180
154
  define_method(:"validate_#{name}_inclusion!") do |value|
181
155
  allow_nil && value.nil? || validator.call(value)
182
156
  end
@@ -188,47 +162,38 @@ module Otoroshi
188
162
 
189
163
  # Defines a lambda to be called to validate that value is included in accepted ones
190
164
  #
191
- # @param name [Symbol] the property name
192
- # @param values [Array, nil] the values to test against
193
- #
194
165
  # @return [Proc] the lambda to use in order to test that value is included in accepted ones
195
- def inside?(name, values)
166
+ def inside?(name, one_of)
196
167
  lambda do |v|
197
- values.include?(v) || raise(Otoroshi::NotAcceptedError.new(name, values))
168
+ one_of.include?(v) || raise(Otoroshi::NotAcceptedError.new(name, one_of))
198
169
  end
199
170
  end
200
171
 
201
172
  # Defines a lambda to be called to validate that each value is included in accepted ones
202
173
  #
203
- # @param name [Symbol] the property name
204
- # @param values [Array, nil] the values to test against
205
- #
206
174
  # @return [Proc] the lambda to use in order to test that each value is included in accepted ones
207
- def each_inside?(name, values)
175
+ def each_inside?(name, one_of)
208
176
  lambda do |v|
209
- v.all? { |e| values.include? e } || raise(Otoroshi::NotAcceptedError.new(name, values, array: true))
177
+ v.all? { |e| one_of.include? e } || raise(Otoroshi::NotAcceptedError.new(name, one_of, collection: true))
210
178
  end
211
179
  end
212
180
 
213
181
  # Defines a private method that raises an error if assert lambda returns false
214
182
  #
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
218
- #
219
183
  # @return [void]
220
184
  #
221
185
  # @example
222
- # define_validate_assertion!("score", ->(v) { v >= 0 }, false) #=> def validate_score_assertion!(value) ...
186
+ # define_validate_assertion!(:score, ...) #=> def validate_score_assertion!(value) ...
223
187
  # @example Generated instance method
188
+ # # given name: :score, assert: >(v) { v >= 0 }, allow_nil: false
224
189
  # def validate_score_assertion!(value)
225
190
  # return if false && value.nil?
226
191
  # return if value >= 0
227
192
  #
228
193
  # raise ArgumentError, ":score does not match validation"
229
194
  # end
230
- def define_validate_assertion!(name, type, assert, allow_nil)
231
- validator = collection?(type) ? each_assert?(name, assert) : assert?(name, assert)
195
+ def define_validate_assertion!(name, collection, assert, allow_nil)
196
+ validator = collection ? each_assert?(name, assert) : assert?(name, assert)
232
197
  define_method :"validate_#{name}_assertion!" do |value|
233
198
  allow_nil && value.nil? || validator.call(value)
234
199
  end
@@ -237,57 +202,46 @@ module Otoroshi
237
202
 
238
203
  # Defines a lambda to be called to validate that value respects the specific
239
204
  #
240
- # @param name [Symbol] the property name
241
- # @param assert [Proc] a lambda processing the value and returning true or false
242
- #
243
205
  # @return [Proc] the lambda to use in order to test that value respects the specific
244
206
  def assert?(name, assert)
245
207
  lambda do |value|
246
- return if instance_exec(value, &assert)
247
-
248
- raise Otoroshi::AssertionError, name
208
+ instance_exec(value, &assert) || raise(Otoroshi::AssertionError, name)
249
209
  end
250
210
  end
251
211
 
252
212
  # Defines a lambda to be called to validate that value respects the specific
253
213
  #
254
- # @param name [Symbol] the property name
255
- # @param validate [Proc] a lambda processing the value and returning true or false
256
- #
257
214
  # @return [Proc] the lambda to use in order to test that each value respects the specific
258
- def each_assert?(name, validate)
215
+ def each_assert?(name, assert)
259
216
  lambda do |value|
260
- return if value.all? { |e| instance_exec(e, &validate) }
261
-
262
- raise Otoroshi::AssertionError.new(name, array: true)
217
+ value.all? { |e| instance_exec(e, &assert) } ||
218
+ raise(Otoroshi::AssertionError.new(name, collection: true))
263
219
  end
264
220
  end
265
221
 
266
222
  # Defines a getter method for the property
267
223
  #
268
- # @param name [Symbol] the property name
269
- #
270
224
  # @return [void]
271
225
  #
272
226
  # @example
273
227
  # define_getter(:score) #=> def score ...
274
228
  # @example Generated instance method
275
- # def score
276
- # instance_variable_get(@score)
277
- # end
229
+ # # given name: :score
230
+ # def score
231
+ # instance_variable_get(@score)
232
+ # end
278
233
  def define_getter(name)
279
234
  define_method(name) { instance_variable_get("@#{name}") }
280
235
  end
281
236
 
282
237
  # Defines a setter method for the property
283
238
  #
284
- # @param name [Symbol] the property name
285
- #
286
239
  # @return [void]
287
240
  #
288
241
  # @example
289
- # define_getter(:score) #=> def score=(value) ...
242
+ # define_setter(:score) #=> def score=(value) ...
290
243
  # @example Generated instance method
244
+ # # given name: :score
291
245
  # def score=(value)
292
246
  # validate_score_type!(value)
293
247
  # validate_score!(value)
@@ -301,99 +255,6 @@ module Otoroshi
301
255
  instance_variable_set("@#{name}", value)
302
256
  end
303
257
  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
258
  end
398
259
  end
399
260
  end