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 +4 -4
- data/lib/otoroshi.rb +4 -5
- data/lib/otoroshi/exceptions.rb +68 -52
- data/lib/otoroshi/initializer.rb +127 -0
- data/lib/otoroshi/sanctuary.rb +100 -273
- metadata +3 -20
- data/lib/doc/Example.html +0 -244
- data/lib/doc/Otoroshi.html +0 -115
- data/lib/doc/Otoroshi/Sanctuary.html +0 -223
- data/lib/doc/Otoroshi/Sanctuary/ClassMethods.html +0 -1511
- data/lib/doc/_index.html +0 -137
- data/lib/doc/class_list.html +0 -51
- data/lib/doc/css/common.css +0 -1
- data/lib/doc/css/full_list.css +0 -58
- data/lib/doc/css/style.css +0 -496
- data/lib/doc/file.README.html +0 -290
- data/lib/doc/file_list.html +0 -56
- data/lib/doc/frames.html +0 -17
- data/lib/doc/index.html +0 -290
- data/lib/doc/js/app.js +0 -314
- data/lib/doc/js/full_list.js +0 -216
- data/lib/doc/js/jquery.js +0 -4
- data/lib/doc/method_list.html +0 -155
- data/lib/doc/top-level-namespace.html +0 -110
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dc27b22c72bffa23a8dc1d4438ec57c246ac7277c4f505c5e4f812b938cda424
|
4
|
+
data.tar.gz: fc408beba9ffae09f881407b2ec36abe200d41cd02505a2ca731773b1ed7c56c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2d1183f56272d15fdf58ba062a6f153b8e2679adffc4da06c3a75bafaab0ae5be1f155cd85df8dfc015b83df6476538013015eeea4a55f791616c486f7a86142
|
7
|
+
data.tar.gz: 94eb25ded2a7fced1518ce173a6633c5863188b456b883458b2ec9dafe97b0e456dad15c3c5cebbb72d341af3eadc887863867410dfbe847fdfc52587c4bcff8
|
data/lib/otoroshi.rb
CHANGED
@@ -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
|
-
|
4
|
+
module Otoroshi
|
5
|
+
require_relative 'otoroshi/sanctuary'
|
6
|
+
require_relative 'otoroshi/exceptions'
|
7
|
+
end
|
data/lib/otoroshi/exceptions.rb
CHANGED
@@ -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
|
7
|
-
class
|
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
|
-
# @
|
13
|
-
|
14
|
-
|
15
|
-
|
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
|
37
|
-
class
|
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
|
42
|
-
# @
|
43
|
-
|
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
|
-
|
47
|
-
|
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
|
59
|
-
# Initialize an error
|
60
|
-
#
|
34
|
+
class AssertError < Error
|
61
35
|
# @param property [Symbol] name of the property
|
62
|
-
# @
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
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
|
data/lib/otoroshi/sanctuary.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
|
3
|
+
require_relative 'initializer'
|
4
4
|
|
5
5
|
module Otoroshi
|
6
|
-
# This module is designed to be
|
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 :
|
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
|
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(
|
47
|
-
name,
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
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
|
-
|
56
|
+
class_eval Initializer.draw(properties), __FILE__, __LINE__ + 1
|
61
57
|
end
|
62
58
|
|
63
|
-
#
|
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
|
-
# @
|
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
|
-
#
|
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
|
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
|
-
#
|
87
|
+
# Given name = :score, type = Integer, allow_nil = false
|
106
88
|
#
|
107
|
-
#
|
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
|
93
|
+
# raise Otoroshi::TypeError, :score, Integer
|
115
94
|
# end
|
116
|
-
def define_validate_type
|
117
|
-
|
118
|
-
|
119
|
-
|
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
|
-
#
|
132
|
-
#
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
#
|
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
|
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
|
-
#
|
120
|
+
# Given name = :side, collection = false, one_of = [:left, :right], allow_nil = false
|
167
121
|
#
|
168
|
-
#
|
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
|
126
|
+
# raise Otoroshi::OneOfError, :side, [:left, :right]
|
176
127
|
# end
|
177
|
-
def
|
178
|
-
validator =
|
179
|
-
|
180
|
-
|
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}
|
133
|
+
private :"validate_#{name}_one_of"
|
187
134
|
end
|
188
135
|
|
189
|
-
#
|
190
|
-
#
|
191
|
-
|
192
|
-
|
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
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
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
|
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
|
-
#
|
154
|
+
# Given name = :score, assert = ->(v) { v >= 0 }, allow_nil = false
|
220
155
|
#
|
221
|
-
#
|
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
|
160
|
+
# raise Otoroshi::AssertError, :score
|
229
161
|
# end
|
230
|
-
def define_validate_assertion
|
231
|
-
validator =
|
232
|
-
define_method :"validate_#{name}_assertion
|
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
|
-
#
|
239
|
-
#
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
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
|
180
|
+
# Defines a private method that calls all validations
|
253
181
|
#
|
254
|
-
#
|
255
|
-
# @param validate [Proc] a lambda processing the value and returning true or false
|
182
|
+
# Given name = :score
|
256
183
|
#
|
257
|
-
#
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
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
|
267
|
-
#
|
268
|
-
# @param name [Symbol] the property name
|
198
|
+
# Defines a getter
|
269
199
|
#
|
270
|
-
#
|
200
|
+
# Given name = :score
|
271
201
|
#
|
272
|
-
#
|
273
|
-
#
|
274
|
-
#
|
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
|
209
|
+
# Defines a setter
|
283
210
|
#
|
284
|
-
#
|
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
|
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}
|
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
|