otoroshi 0.0.1 → 0.0.6

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.
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: []