otoroshi 0.0.1 → 0.0.6

Sign up to get free protection for your applications and to get access to all the features.
Files changed (3) hide show
  1. checksums.yaml +4 -4
  2. data/lib/otoroshi/sanctuary.rb +175 -97
  3. metadata +2 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2993a278f341533c1590cd6bdea9291033167104f94018808577f67cbcd2f0ae
4
- data.tar.gz: 725a187567625f428485a80edd5025b0ae7e3a859bebd4d73ea14557353a89ad
3
+ metadata.gz: e31c6e00090297fb9709600d1580a24a4729ee1a9af094015ea7c197fde884d3
4
+ data.tar.gz: 033526ab6473e809a19c812104cf05cadba29485d617161c1799f6664777f9fc
5
5
  SHA512:
6
- metadata.gz: c14d31b349b599ff5ef0db40dbaed8723b47bd53de648589ace6ac8ccfeba85b926d263070fe7d13a0746168399508127cd4e3a33df032ff8cfef5d5a9d5380c
7
- data.tar.gz: 19311d09a63ee50e89bd3b3c7dead8faeaba0bd1a4803dfba363c422b0c13704fbeb3bb60799cfac9d681736e209d1587b44be76840bc5736aff4822c6cf9c24
6
+ metadata.gz: a5e9595b7c246dde6897fc4f314cdf0c9ef00576dd042cc9c4e34c60de249d4d346ebc2f661f1190708a26211cacbdd1a9f457a9e5802e57049b5f5d831dc9d1
7
+ data.tar.gz: 42aace4283c39b3d956022e643635d73a9b3a5feb74070f0dd441d8ef165b5d9c76475a7bd274b94e4da8f85aa0ecb08868116842c6af98b1cdf4112a93bbe77
@@ -1,77 +1,109 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Otoroshi
4
- # help setting and validating instance arguments
5
- class Sanctuary
4
+ # This module is designed to be included in a class. This will provide
5
+ # the "property" ({Sanctuary::ClassMethods.property}) method for defining class properties.
6
+ #
7
+ # @example
8
+ # class Importer
9
+ # include Otoroshi::Sanctuary
10
+ #
11
+ # property :file_path, String, validate: ->(v) { v.match? /.+\.csv/ }
12
+ # property :headers, [TrueClass, FalseClass], default: false
13
+ # property :col_sep, String, validate: ->(v) { v.in? [',', ';', '\s', '\t', '|'] }, default: ','
14
+ # property :converters, Symbol, validate: ->(v) { v.in? %i[integer float date] }, allow_nil: true
15
+ # end
16
+ module Sanctuary
6
17
  class << self
7
- # Add a new property to the class (called by inherited class)
8
- # Example: property name, type: String, validate: ->(v) { v.length > 3 }, optional: true
9
- def property(name, type = Object, validate: ->(_) { true }, optional: false, default: nil)
10
- define_default(name, default)
11
- define_validate_type!(name, type, optional)
12
- define_validate!(name, validate, optional)
18
+ # Extend ClassMethods for the base class
19
+ def included(base)
20
+ base.extend ClassMethods
21
+ end
22
+ end
23
+
24
+ # Class methods extended for the base class
25
+ module ClassMethods
26
+ # Adds a new "property" to the class
27
+ #
28
+ # @param name [Symbol] the property name
29
+ # @param type [Class] the expected value type
30
+ # @param validate [Proc] a lambda processing the value and returning true or false
31
+ # @param allow_nil [true, false] allow nil as a value
32
+ # @param default [Object] default value if not set on initialization
33
+ #
34
+ # @return [void]
35
+ #
36
+ # @example
37
+ # property name, type: String, validate: ->(v) { v.length > 3 }, allow_nil: true
38
+ # @example
39
+ # property score, type: Integer, validate: ->(v) { v >= 0 }, default: 0
40
+ def property(name, type = Object, validate: ->(_) { true }, allow_nil: false, default: nil)
41
+ add_to_properties(name, allow_nil, default)
42
+ define_validate_type!(name, type, allow_nil)
43
+ define_validate_lambda!(name, validate, allow_nil)
13
44
  define_getter(name)
14
45
  define_setter(name)
15
- add_to_properties(name)
46
+ redefine_initialize
16
47
  end
17
48
 
18
- # Return the (inherited) class properties
19
- # (this method will be updated by ::add_to_properties(name))
49
+ # Returns the class properties
50
+ #
51
+ # @return [Hash]
52
+ #
53
+ # @note this method will be updated by {add_to_properties}
20
54
  def properties
21
- []
55
+ {}
22
56
  end
23
57
 
24
58
  private
25
59
 
26
- # Update the ::properties method to add new property to the current list
27
- def add_to_properties(name)
28
- current = properties
29
- define_singleton_method :properties do
30
- current << name
31
- end
32
- end
33
-
34
- # Define a private method that returns the default value
35
- #
36
- # ::define_default("score", 0)
37
- # --------------------------
38
- # def default_score
39
- # 0
40
- # end
60
+ # Updates {properties} to add new property to the returned ones
41
61
  #
42
- def define_default(name, default)
43
- define_method(:"default_#{name}") { default }
44
- private :"default_#{name}"
62
+ # @return [void]
63
+ def add_to_properties(name, allow_nil, default)
64
+ current_state = properties
65
+ current_state[name] = { allow_nil: allow_nil, default: default }
66
+ define_singleton_method(:properties) { current_state }
45
67
  end
46
68
 
47
- # Define a private method that raises an error if type is not respected
69
+ # Defines a private method that raises an error if type is not respected
48
70
  #
49
- # ::define_validate_type!("score", Integer, false)
50
- # ----------------------------------------------
51
- # def validate_score_type!(value)
52
- # return if optional && value.nil?
53
- # return if value.is_a?(Integer)
71
+ # @param name [Symbol] the property name
72
+ # @param type [Class] the type to test against
73
+ # @param allow_nil [true, false] allow nil as a value
54
74
  #
55
- # raise ArgumentError, ":score does not match type validation"
56
- # end
75
+ # @return [void]
57
76
  #
58
- def define_validate_type!(name, type, optional)
77
+ # @example
78
+ # define_validate_type!(score, Integer, false) => def validate_score_type!(value) ...
79
+ # @example Generated method
80
+ # def validate_score_type!(value)
81
+ # return if Integer.nil? || false && value.nil?
82
+ # return if value.is_a? Integer
83
+ #
84
+ # raise ArgumentError, ":score does not match required type"
85
+ # end
86
+ def define_validate_type!(name, type, allow_nil)
59
87
  lambda = type_validation(type)
60
88
  define_method :"validate_#{name}_type!" do |value|
61
- return if type.nil? || optional && value.nil?
89
+ return if type.nil? || allow_nil && value.nil?
62
90
  return if lambda.call(value)
63
91
 
64
- raise ArgumentError, ":#{name} does not match type validation"
92
+ raise ArgumentError, ":#{name} does not match required type"
65
93
  end
66
94
  private :"validate_#{name}_type!"
67
95
  end
68
96
 
69
- # Define a lambda to be call to validate that value match the type
97
+ # Defines a lambda to be call to validate that value matches the type
98
+ #
99
+ # @param type [Class] the type to test against
70
100
  #
71
- # ::type_validation(Integer)
72
- # ----------------------------------------------
73
- # ->(v) { v.is_a? Integer }
101
+ # @return [Proc] the lambda to use in order to test the value matches the type
74
102
  #
103
+ # @example
104
+ # type_validation(Integer) #=> ->(v) { v.is_a? Integer }
105
+ # @example
106
+ # type_validation([String, Symbol]) #=> ->(v) { [String, Symbol].any? { |t| v.is_a? t } }
75
107
  def type_validation(type)
76
108
  if type.is_a? Array
77
109
  ->(v) { type.any? { |t| v.is_a? t } }
@@ -80,85 +112,131 @@ module Otoroshi
80
112
  end
81
113
  end
82
114
 
83
- # Define a private method that raises an error if validate block returns false
115
+ # Defines a private method that raises an error if validate block returns false
84
116
  #
85
- # ::define_validate!("score", ->(v) { v >= 0 }, false)
86
- # --------------------------------------------------
87
- # def validate_score!(value)
88
- # return if false && value.nil?
89
- # return if value >= 0
117
+ # @param name [Symbol] the property name
118
+ # @param validate [Proc] a lambda processing the value and returning true or false
119
+ # @param allow_nil [true, false] allow nil as a value
90
120
  #
91
- # raise ArgumentError, ":score does not match validation"
92
- # end
121
+ # @return [void]
93
122
  #
94
- def define_validate!(name, validate, optional)
95
- define_method :"validate_#{name}!" do |value|
96
- return if optional && value.nil?
123
+ # @example
124
+ # define_validate_lambda!("score", ->(v) { v >= 0 }, false) #=> def validate_score_lambda!(value) ...
125
+ # @example Generated instance method
126
+ # def validate_score_lambda!(value)
127
+ # return if false && value.nil?
128
+ # return if value >= 0
129
+ #
130
+ # raise ArgumentError, ":score does not match validation"
131
+ # end
132
+ def define_validate_lambda!(name, validate, allow_nil)
133
+ define_method :"validate_#{name}_lambda!" do |value|
134
+ return if allow_nil && value.nil?
97
135
  return if instance_exec(value, &validate)
98
136
 
99
137
  raise ArgumentError, ":#{name} does not match validation"
100
138
  end
101
- private :"validate_#{name}!"
139
+ private :"validate_#{name}_lambda!"
102
140
  end
103
141
 
104
- # Define a getter method for the property
142
+ # Defines a getter method for the property
143
+ #
144
+ # @param name [Symbol] the property name
105
145
  #
106
- # ::define_getter("score")
107
- # ----------------------
108
- # def score
109
- # @score
110
- # end
146
+ # @return [void]
111
147
  #
148
+ # @example
149
+ # define_getter(:score) #=> def score ...
150
+ # @example Generated instance method
151
+ # def score
152
+ # instance_variable_get(@score)
153
+ # end
112
154
  def define_getter(name)
113
155
  define_method(name) { instance_variable_get("@#{name}") }
114
156
  end
115
157
 
116
- # Define a setter method for the property
158
+ # Defines a setter method for the property
117
159
  #
118
- # ::define_setter("score")
119
- # ----------------------
120
- # def score=(value)
121
- # validate_score_type!(value)
122
- # validate_score!(value)
123
- # @score = value
124
- # end
160
+ # @param name [Symbol] the property name
125
161
  #
162
+ # @return [void]
163
+ #
164
+ # @example
165
+ # define_getter(:score) #=> def score=(value) ...
166
+ # @example Generated instance method
167
+ # def score=(value)
168
+ # validate_score_type!(value)
169
+ # validate_score!(value)
170
+ # instance_variable_set(@score, value)
171
+ # end
126
172
  def define_setter(name)
127
173
  define_method :"#{name}=" do |value|
128
174
  __send__(:"validate_#{name}_type!", value)
129
- __send__(:"validate_#{name}!", value)
175
+ __send__(:"validate_#{name}_lambda!", value)
130
176
  instance_variable_set("@#{name}", value)
131
177
  end
132
178
  end
133
- end
134
179
 
135
- # Initialize an instance and validate provided args
136
- def initialize(args = {}) # rubocop:disable Style/OptionHash
137
- validate_keys!(args.keys)
138
- assign_values(args)
139
- end
140
-
141
- private
180
+ # Redefines initialize method
181
+ #
182
+ # @return [void]
183
+ #
184
+ # @note method is defined with `class_eval`
185
+ #
186
+ # @example Generated method
187
+ # def initialize(foo:, bar: 0)
188
+ # self.foo = foo
189
+ # self.bar = bar
190
+ # end
191
+ def redefine_initialize
192
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
193
+ def initialize(#{initialize_parameters})
194
+ #{initialize_body}
195
+ end
196
+ RUBY
197
+ end
142
198
 
143
- # validate that provided keys match class properties
144
- def validate_keys!(keys)
145
- errors = keys.reject { |key| self.class.properties.include? key }
146
- return if errors.empty?
199
+ # Defines initialize method parameters
200
+ #
201
+ # @return [String]
202
+ #
203
+ # @example Given properties { foo: { allow_nil: false, default: nil }, { allow_nil: true, default: 0 } }
204
+ # redefine_initialize #=> "foo:, bar: 0"
205
+ def initialize_parameters
206
+ parameters =
207
+ properties.map do |key, options|
208
+ allow_nil, default = options.values
209
+ "#{key}:#{default_parameter_for(allow_nil, default)}"
210
+ end
211
+ parameters.join(', ')
212
+ end
147
213
 
148
- message =
149
- if errors.one?
150
- ":#{errors[0]} is not a valid property"
151
- else
152
- ":#{errors.join(', :')} are not valid properties"
153
- end
154
- raise ArgumentError, message
155
- end
214
+ # Defines the default value of a parameter depending on options
215
+ #
216
+ # @param options [Hash]
217
+ #
218
+ # @return [String]
219
+ #
220
+ # @example when nil is allowed and default is set
221
+ # default_parameter_for(true, 0) #=> " 0"
222
+ # @example when nil is allowed and default is not set
223
+ # default_parameter_for(true, nil) #=> " nil"
224
+ # @example when nil is not allowed
225
+ # default_parameter_for(false, nil) #=> ""
226
+ def default_parameter_for(allow_nil, default)
227
+ return " #{default}" if default
228
+
229
+ allow_nil ? ' nil' : ''
230
+ end
156
231
 
157
- # assign value to each property
158
- def assign_values(args)
159
- self.class.properties.each do |property|
160
- value = args.key?(property) ? args[property] : __send__(:"default_#{property}")
161
- public_send(:"#{property}=", value)
232
+ # Defines initialize method body
233
+ #
234
+ # @return [String]
235
+ #
236
+ # @example Given properties { foo: { allow_nil: false, default: nil }, { allow_nil: true, default: 0 } }
237
+ # initialize_body #=> "self.foo = foo\nself.bar = bar"
238
+ def initialize_body
239
+ properties.keys.map { |key| "self.#{key} = #{key}" }.join("\n")
162
240
  end
163
241
  end
164
242
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: otoroshi
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Edouard Piron
@@ -10,7 +10,7 @@ bindir: bin
10
10
  cert_chain: []
11
11
  date: 2020-10-10 00:00:00.000000000 Z
12
12
  dependencies: []
13
- description: Help setting and validating instance arguments
13
+ description: Help defining class properties
14
14
  email: ed.piron@gmail.com
15
15
  executables: []
16
16
  extensions: []