dry-validation 1.0.0 → 1.5.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +379 -141
- data/LICENSE +1 -1
- data/README.md +7 -8
- data/dry-validation.gemspec +41 -0
- data/lib/dry-validation.rb +1 -1
- data/lib/dry/validation.rb +10 -5
- data/lib/dry/validation/config.rb +2 -2
- data/lib/dry/validation/constants.rb +9 -2
- data/lib/dry/validation/contract.rb +34 -17
- data/lib/dry/validation/contract/class_interface.rb +62 -56
- data/lib/dry/validation/evaluator.rb +50 -13
- data/lib/dry/validation/extensions/hints.rb +1 -3
- data/lib/dry/validation/extensions/monads.rb +1 -1
- data/lib/dry/validation/extensions/predicates_as_macros.rb +75 -0
- data/lib/dry/validation/failures.rb +15 -3
- data/lib/dry/validation/function.rb +13 -10
- data/lib/dry/validation/macro.rb +2 -2
- data/lib/dry/validation/macros.rb +3 -3
- data/lib/dry/validation/message.rb +4 -4
- data/lib/dry/validation/message_set.rb +6 -51
- data/lib/dry/validation/messages/resolver.rb +60 -5
- data/lib/dry/validation/result.rb +47 -8
- data/lib/dry/validation/rule.rb +43 -12
- data/lib/dry/validation/schema_ext.rb +19 -0
- data/lib/dry/validation/values.rb +38 -11
- data/lib/dry/validation/version.rb +1 -1
- metadata +31 -24
data/LICENSE
CHANGED
data/README.md
CHANGED
@@ -1,26 +1,25 @@
|
|
1
1
|
[gem]: https://rubygems.org/gems/dry-validation
|
2
|
-
[
|
3
|
-
[
|
2
|
+
[actions]: https://github.com/dry-rb/dry-validation/actions
|
3
|
+
[codacy]: https://www.codacy.com/gh/dry-rb/dry-validation
|
4
4
|
[chat]: https://dry-rb.zulipchat.com
|
5
5
|
[inchpages]: http://inch-ci.org/github/dry-rb/dry-validation
|
6
6
|
|
7
7
|
# dry-validation [][chat]
|
8
8
|
|
9
9
|
[][gem]
|
10
|
-
[][actions]
|
11
|
+
[][codacy]
|
12
|
+
[][codacy]
|
13
13
|
[][inchpages]
|
14
14
|
|
15
15
|
## Links
|
16
16
|
|
17
|
-
* [User documentation](
|
17
|
+
* [User documentation](http://dry-rb.org/gems/dry-validation)
|
18
18
|
* [API documentation](http://rubydoc.info/gems/dry-validation)
|
19
|
-
* [Guidelines for contributing](CONTRIBUTING.md)
|
20
19
|
|
21
20
|
## Supported Ruby versions
|
22
21
|
|
23
|
-
This library officially supports following Ruby versions:
|
22
|
+
This library officially supports the following Ruby versions:
|
24
23
|
|
25
24
|
* MRI >= `2.4`
|
26
25
|
* jruby >= `9.2`
|
@@ -0,0 +1,41 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
# this file is managed by dry-rb/devtools project
|
3
|
+
|
4
|
+
lib = File.expand_path('lib', __dir__)
|
5
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
6
|
+
require 'dry/validation/version'
|
7
|
+
|
8
|
+
Gem::Specification.new do |spec|
|
9
|
+
spec.name = 'dry-validation'
|
10
|
+
spec.authors = ["Piotr Solnica"]
|
11
|
+
spec.email = ["piotr.solnica@gmail.com"]
|
12
|
+
spec.license = 'MIT'
|
13
|
+
spec.version = Dry::Validation::VERSION.dup
|
14
|
+
|
15
|
+
spec.summary = "Validation library"
|
16
|
+
spec.description = spec.summary
|
17
|
+
spec.homepage = 'https://dry-rb.org/gems/dry-validation'
|
18
|
+
spec.files = Dir["CHANGELOG.md", "LICENSE", "README.md", "dry-validation.gemspec", "lib/**/*", "config/*.yml"]
|
19
|
+
spec.bindir = 'bin'
|
20
|
+
spec.executables = []
|
21
|
+
spec.require_paths = ['lib']
|
22
|
+
|
23
|
+
spec.metadata['allowed_push_host'] = 'https://rubygems.org'
|
24
|
+
spec.metadata['changelog_uri'] = 'https://github.com/dry-rb/dry-validation/blob/master/CHANGELOG.md'
|
25
|
+
spec.metadata['source_code_uri'] = 'https://github.com/dry-rb/dry-validation'
|
26
|
+
spec.metadata['bug_tracker_uri'] = 'https://github.com/dry-rb/dry-validation/issues'
|
27
|
+
|
28
|
+
spec.required_ruby_version = ">= 2.4.0"
|
29
|
+
|
30
|
+
# to update dependencies edit project.yml
|
31
|
+
spec.add_runtime_dependency "concurrent-ruby", "~> 1.0"
|
32
|
+
spec.add_runtime_dependency "dry-container", "~> 0.7", ">= 0.7.1"
|
33
|
+
spec.add_runtime_dependency "dry-core", "~> 0.4"
|
34
|
+
spec.add_runtime_dependency "dry-equalizer", "~> 0.2"
|
35
|
+
spec.add_runtime_dependency "dry-initializer", "~> 3.0"
|
36
|
+
spec.add_runtime_dependency "dry-schema", "~> 1.5", ">= 1.5.2"
|
37
|
+
|
38
|
+
spec.add_development_dependency "bundler"
|
39
|
+
spec.add_development_dependency "rake"
|
40
|
+
spec.add_development_dependency "rspec"
|
41
|
+
end
|
data/lib/dry-validation.rb
CHANGED
data/lib/dry/validation.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
3
|
+
require "dry/validation/constants"
|
4
|
+
require "dry/validation/contract"
|
5
|
+
require "dry/validation/macros"
|
6
6
|
|
7
7
|
# Main namespace
|
8
8
|
#
|
@@ -16,11 +16,15 @@ module Dry
|
|
16
16
|
extend Macros::Registrar
|
17
17
|
|
18
18
|
register_extension(:monads) do
|
19
|
-
require
|
19
|
+
require "dry/validation/extensions/monads"
|
20
20
|
end
|
21
21
|
|
22
22
|
register_extension(:hints) do
|
23
|
-
require
|
23
|
+
require "dry/validation/extensions/hints"
|
24
|
+
end
|
25
|
+
|
26
|
+
register_extension(:predicates_as_macros) do
|
27
|
+
require "dry/validation/extensions/predicates_as_macros"
|
24
28
|
end
|
25
29
|
|
26
30
|
# Define a contract and build its instance
|
@@ -41,6 +45,7 @@ module Dry
|
|
41
45
|
# @return [Contract]
|
42
46
|
#
|
43
47
|
# @api public
|
48
|
+
#
|
44
49
|
def self.Contract(options = EMPTY_HASH, &block)
|
45
50
|
Contract.build(options, &block)
|
46
51
|
end
|
@@ -1,16 +1,23 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "pathname"
|
4
|
+
require "dry/core/constants"
|
4
5
|
|
5
6
|
module Dry
|
6
7
|
module Validation
|
7
8
|
include Dry::Core::Constants
|
8
9
|
|
9
|
-
DOT =
|
10
|
+
DOT = "."
|
10
11
|
|
11
12
|
# Root path is used for base errors in hash representation of error messages
|
12
13
|
ROOT_PATH = [nil].freeze
|
13
14
|
|
15
|
+
# Path to the default errors locale file
|
16
|
+
DEFAULT_ERRORS_NAMESPACE = "dry_validation"
|
17
|
+
|
18
|
+
# Path to the default errors locale file
|
19
|
+
DEFAULT_ERRORS_PATH = Pathname(__FILE__).join("../../../../config/errors.yml").realpath.freeze
|
20
|
+
|
14
21
|
# Mapping for block kwarg options used by block_options
|
15
22
|
#
|
16
23
|
# @see Rule#block_options
|
@@ -1,18 +1,18 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "concurrent/map"
|
4
4
|
|
5
|
-
require
|
6
|
-
require
|
7
|
-
require
|
5
|
+
require "dry/equalizer"
|
6
|
+
require "dry/initializer"
|
7
|
+
require "dry/schema/path"
|
8
8
|
|
9
|
-
require
|
10
|
-
require
|
11
|
-
require
|
12
|
-
require
|
13
|
-
require
|
14
|
-
require
|
15
|
-
require
|
9
|
+
require "dry/validation/config"
|
10
|
+
require "dry/validation/constants"
|
11
|
+
require "dry/validation/rule"
|
12
|
+
require "dry/validation/evaluator"
|
13
|
+
require "dry/validation/messages/resolver"
|
14
|
+
require "dry/validation/result"
|
15
|
+
require "dry/validation/contract/class_interface"
|
16
16
|
|
17
17
|
module Dry
|
18
18
|
module Validation
|
@@ -54,8 +54,8 @@ module Dry
|
|
54
54
|
extend Dry::Initializer
|
55
55
|
extend ClassInterface
|
56
56
|
|
57
|
-
config.messages.top_namespace =
|
58
|
-
config.messages.load_paths <<
|
57
|
+
config.messages.top_namespace = DEFAULT_ERRORS_NAMESPACE
|
58
|
+
config.messages.load_paths << DEFAULT_ERRORS_PATH
|
59
59
|
|
60
60
|
# @!attribute [r] config
|
61
61
|
# @return [Config] Contract's configuration object
|
@@ -98,7 +98,7 @@ module Dry
|
|
98
98
|
rule_result = rule.(self, result)
|
99
99
|
|
100
100
|
rule_result.failures.each do |failure|
|
101
|
-
result.add_error(message_resolver
|
101
|
+
result.add_error(message_resolver.(**failure))
|
102
102
|
end
|
103
103
|
end
|
104
104
|
end
|
@@ -116,9 +116,26 @@ module Dry
|
|
116
116
|
private
|
117
117
|
|
118
118
|
# @api private
|
119
|
-
def error?(result,
|
120
|
-
path = Schema::Path[
|
121
|
-
|
119
|
+
def error?(result, spec)
|
120
|
+
path = Schema::Path[spec]
|
121
|
+
|
122
|
+
if path.multi_value?
|
123
|
+
return path.expand.any? { |nested_path| error?(result, nested_path) }
|
124
|
+
end
|
125
|
+
|
126
|
+
return true if result.schema_error?(path)
|
127
|
+
|
128
|
+
path
|
129
|
+
.to_a[0..-2]
|
130
|
+
.any? { |key|
|
131
|
+
curr_path = Schema::Path[path.keys[0..path.keys.index(key)]]
|
132
|
+
|
133
|
+
return false unless result.schema_error?(curr_path)
|
134
|
+
|
135
|
+
result.errors.any? { |err|
|
136
|
+
(other = Schema::Path[err.path]).same_root?(curr_path) && other == curr_path
|
137
|
+
}
|
138
|
+
}
|
122
139
|
end
|
123
140
|
|
124
141
|
# Get a registered macro
|
@@ -1,40 +1,15 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
6
|
-
require
|
3
|
+
require "dry/schema"
|
4
|
+
require "dry/schema/messages"
|
5
|
+
require "dry/schema/path"
|
6
|
+
require "dry/schema/key_map"
|
7
7
|
|
8
|
-
require
|
9
|
-
require
|
8
|
+
require "dry/validation/constants"
|
9
|
+
require "dry/validation/macros"
|
10
|
+
require "dry/validation/schema_ext"
|
10
11
|
|
11
12
|
module Dry
|
12
|
-
module Schema
|
13
|
-
# @api private
|
14
|
-
class Key
|
15
|
-
# @api private
|
16
|
-
def to_dot_notation
|
17
|
-
[name.to_s]
|
18
|
-
end
|
19
|
-
|
20
|
-
# @api private
|
21
|
-
class Hash < Key
|
22
|
-
# @api private
|
23
|
-
def to_dot_notation
|
24
|
-
[name].product(members.map(&:to_dot_notation).flatten(1)).map { |e| e.join(DOT) }
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
|
29
|
-
# @api private
|
30
|
-
class KeyMap
|
31
|
-
# @api private
|
32
|
-
def to_dot_notation
|
33
|
-
@to_dot_notation ||= map(&:to_dot_notation).flatten
|
34
|
-
end
|
35
|
-
end
|
36
|
-
end
|
37
|
-
|
38
13
|
module Validation
|
39
14
|
class Contract
|
40
15
|
# Contract's class interface
|
@@ -48,7 +23,7 @@ module Dry
|
|
48
23
|
# @api private
|
49
24
|
def inherited(klass)
|
50
25
|
super
|
51
|
-
klass.instance_variable_set(
|
26
|
+
klass.instance_variable_set("@config", config.dup)
|
52
27
|
end
|
53
28
|
|
54
29
|
# Configuration
|
@@ -78,36 +53,36 @@ module Dry
|
|
78
53
|
#
|
79
54
|
# This type of schema is suitable for HTTP parameters
|
80
55
|
#
|
81
|
-
# @return [Dry::Schema::Params]
|
56
|
+
# @return [Dry::Schema::Params,NilClass]
|
82
57
|
# @see https://dry-rb.org/gems/dry-schema/params/
|
83
58
|
#
|
84
59
|
# @api public
|
85
|
-
def params(&block)
|
86
|
-
define(:Params, &block)
|
60
|
+
def params(*external_schemas, &block)
|
61
|
+
define(:Params, external_schemas, &block)
|
87
62
|
end
|
88
63
|
|
89
64
|
# Define a JSON schema for your contract
|
90
65
|
#
|
91
66
|
# This type of schema is suitable for JSON data
|
92
67
|
#
|
93
|
-
# @return [Dry::Schema::JSON]
|
68
|
+
# @return [Dry::Schema::JSON,NilClass]
|
94
69
|
# @see https://dry-rb.org/gems/dry-schema/json/
|
95
70
|
#
|
96
71
|
# @api public
|
97
|
-
def json(&block)
|
98
|
-
define(:JSON, &block)
|
72
|
+
def json(*external_schemas, &block)
|
73
|
+
define(:JSON, external_schemas, &block)
|
99
74
|
end
|
100
75
|
|
101
76
|
# Define a plain schema for your contract
|
102
77
|
#
|
103
78
|
# This type of schema does not offer coercion out of the box
|
104
79
|
#
|
105
|
-
# @return [Dry::Schema::Processor]
|
80
|
+
# @return [Dry::Schema::Processor,NilClass]
|
106
81
|
# @see https://dry-rb.org/gems/dry-schema/
|
107
82
|
#
|
108
83
|
# @api public
|
109
|
-
def schema(&block)
|
110
|
-
define(:schema, &block)
|
84
|
+
def schema(*external_schemas, &block)
|
85
|
+
define(:schema, external_schemas, &block)
|
111
86
|
end
|
112
87
|
|
113
88
|
# Define a rule for your contract
|
@@ -126,7 +101,7 @@ module Dry
|
|
126
101
|
#
|
127
102
|
# @api public
|
128
103
|
def rule(*keys, &block)
|
129
|
-
ensure_valid_keys(*keys)
|
104
|
+
ensure_valid_keys(*keys) if __schema__
|
130
105
|
|
131
106
|
Rule.new(keys: keys, block: block).tap do |rule|
|
132
107
|
rules << rule
|
@@ -148,7 +123,7 @@ module Dry
|
|
148
123
|
#
|
149
124
|
# @api public
|
150
125
|
def build(options = EMPTY_HASH, &block)
|
151
|
-
Class.new(self, &block).new(options)
|
126
|
+
Class.new(self, &block).new(**options)
|
152
127
|
end
|
153
128
|
|
154
129
|
# @api private
|
@@ -180,11 +155,14 @@ module Dry
|
|
180
155
|
|
181
156
|
# @api private
|
182
157
|
def ensure_valid_keys(*keys)
|
183
|
-
valid_paths = key_map.to_dot_notation
|
158
|
+
valid_paths = key_map.to_dot_notation
|
159
|
+
key_paths = key_paths(keys)
|
184
160
|
|
185
|
-
invalid_keys =
|
186
|
-
.
|
187
|
-
|
161
|
+
invalid_keys = key_paths.map { |(key, path)|
|
162
|
+
unless valid_paths.any? { |vp| vp.include?(path) || vp.include?("#{path}[]") }
|
163
|
+
key
|
164
|
+
end
|
165
|
+
}.compact.uniq
|
188
166
|
|
189
167
|
return if invalid_keys.empty?
|
190
168
|
|
@@ -193,29 +171,57 @@ module Dry
|
|
193
171
|
STR
|
194
172
|
end
|
195
173
|
|
174
|
+
# @api private
|
175
|
+
def key_paths(keys)
|
176
|
+
keys.map { |key|
|
177
|
+
case key
|
178
|
+
when Hash
|
179
|
+
path = Schema::Path[key]
|
180
|
+
if path.multi_value?
|
181
|
+
*head, tail = Array(path)
|
182
|
+
[key].product(
|
183
|
+
tail.map { |el| [*head, *el] }.map { |parts| parts.join(DOT) }
|
184
|
+
)
|
185
|
+
else
|
186
|
+
[[key, path.to_a.join(DOT)]]
|
187
|
+
end
|
188
|
+
when Array
|
189
|
+
[[key, Schema::Path[key].to_a.join(DOT)]]
|
190
|
+
else
|
191
|
+
[[key, key.to_s]]
|
192
|
+
end
|
193
|
+
}.flatten(1)
|
194
|
+
end
|
195
|
+
|
196
196
|
# @api private
|
197
197
|
def key_map
|
198
198
|
__schema__.key_map
|
199
199
|
end
|
200
200
|
|
201
201
|
# @api private
|
202
|
-
def
|
203
|
-
{
|
202
|
+
def core_schema_opts
|
203
|
+
{parent: superclass&.__schema__, config: config}
|
204
204
|
end
|
205
205
|
|
206
206
|
# @api private
|
207
|
-
def define(method_name, &block)
|
208
|
-
if
|
209
|
-
|
207
|
+
def define(method_name, external_schemas, &block)
|
208
|
+
return __schema__ if external_schemas.empty? && block.nil?
|
209
|
+
|
210
|
+
unless __schema__.nil?
|
211
|
+
raise ::Dry::Validation::DuplicateSchemaError, "Schema has already been defined"
|
210
212
|
end
|
211
213
|
|
214
|
+
schema_opts = core_schema_opts
|
215
|
+
|
216
|
+
schema_opts.update(parent: external_schemas) if external_schemas.any?
|
217
|
+
|
212
218
|
case method_name
|
213
219
|
when :schema
|
214
|
-
@__schema__ = Schema.define(schema_opts, &block)
|
220
|
+
@__schema__ = Schema.define(**schema_opts, &block)
|
215
221
|
when :Params
|
216
|
-
@__schema__ = Schema.Params(schema_opts, &block)
|
222
|
+
@__schema__ = Schema.Params(**schema_opts, &block)
|
217
223
|
when :JSON
|
218
|
-
@__schema__ = Schema.JSON(schema_opts, &block)
|
224
|
+
@__schema__ = Schema.JSON(**schema_opts, &block)
|
219
225
|
end
|
220
226
|
end
|
221
227
|
end
|
@@ -1,9 +1,10 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/initializer"
|
4
|
+
require "dry/core/deprecations"
|
4
5
|
|
5
|
-
require
|
6
|
-
require
|
6
|
+
require "dry/validation/constants"
|
7
|
+
require "dry/validation/failures"
|
7
8
|
|
8
9
|
module Dry
|
9
10
|
module Validation
|
@@ -16,6 +17,9 @@ module Dry
|
|
16
17
|
# @api public
|
17
18
|
class Evaluator
|
18
19
|
extend Dry::Initializer
|
20
|
+
extend Dry::Core::Deprecations[:'dry-validation']
|
21
|
+
|
22
|
+
deprecate :error?, :schema_error?
|
19
23
|
|
20
24
|
# @!attribute [r] _contract
|
21
25
|
# @return [Contract]
|
@@ -63,19 +67,19 @@ module Dry
|
|
63
67
|
# Initialize a new evaluator
|
64
68
|
#
|
65
69
|
# @api private
|
66
|
-
def initialize(contract, options, &block)
|
67
|
-
super(contract, options)
|
70
|
+
def initialize(contract, **options, &block)
|
71
|
+
super(contract, **options)
|
68
72
|
|
69
73
|
@_options = options
|
70
74
|
|
71
75
|
if block
|
72
76
|
exec_opts = block_options.map { |key, value| [key, _options[value]] }.to_h
|
73
|
-
instance_exec(exec_opts, &block)
|
77
|
+
instance_exec(**exec_opts, &block)
|
74
78
|
end
|
75
79
|
|
76
80
|
macros.each do |args|
|
77
81
|
macro = macro(*args.flatten(1))
|
78
|
-
instance_exec(macro.extract_block_options(_options.merge(macro: macro)), ¯o.block)
|
82
|
+
instance_exec(**macro.extract_block_options(_options.merge(macro: macro)), ¯o.block)
|
79
83
|
end
|
80
84
|
end
|
81
85
|
|
@@ -117,7 +121,7 @@ module Dry
|
|
117
121
|
|
118
122
|
# @api private
|
119
123
|
def with(new_opts, &block)
|
120
|
-
self.class.new(_contract, _options
|
124
|
+
self.class.new(_contract, **_options, **new_opts, &block)
|
121
125
|
end
|
122
126
|
|
123
127
|
# Return default (first) key name
|
@@ -142,20 +146,52 @@ module Dry
|
|
142
146
|
#
|
143
147
|
# @return [Object]
|
144
148
|
#
|
145
|
-
# @public
|
149
|
+
# @api public
|
146
150
|
def value
|
147
151
|
values[key_name]
|
148
152
|
end
|
149
153
|
|
150
|
-
#
|
154
|
+
# Return if the value under the default key is available
|
155
|
+
#
|
156
|
+
# This is useful when dealing with rules for optional keys
|
157
|
+
#
|
158
|
+
# @example use the default key name
|
159
|
+
# rule(:age) do
|
160
|
+
# key.failure(:invalid) if key? && value < 18
|
161
|
+
# end
|
162
|
+
#
|
163
|
+
# @example specify the key name
|
164
|
+
# rule(:start_date, :end_date) do
|
165
|
+
# if key?(:start_date) && !key?(:end_date)
|
166
|
+
# key(:end_date).failure("must provide an end_date with start_date")
|
167
|
+
# end
|
168
|
+
# end
|
169
|
+
#
|
170
|
+
# @return [Boolean]
|
171
|
+
#
|
172
|
+
# @api public
|
173
|
+
def key?(name = key_name)
|
174
|
+
values.key?(name)
|
175
|
+
end
|
176
|
+
|
177
|
+
# Check if there are any errors on the schema under the provided path
|
178
|
+
#
|
179
|
+
# @param path [Symbol, String, Array] A Path-compatible spec
|
151
180
|
#
|
152
|
-
# @
|
181
|
+
# @return [Boolean]
|
182
|
+
#
|
183
|
+
# @api public
|
184
|
+
def schema_error?(path)
|
185
|
+
result.schema_error?(path)
|
186
|
+
end
|
187
|
+
|
188
|
+
# Check if there are any errors on the current rule
|
153
189
|
#
|
154
190
|
# @return [Boolean]
|
155
191
|
#
|
156
192
|
# @api public
|
157
|
-
def
|
158
|
-
|
193
|
+
def rule_error?
|
194
|
+
!key(path).empty?
|
159
195
|
end
|
160
196
|
|
161
197
|
# @api private
|
@@ -176,6 +212,7 @@ module Dry
|
|
176
212
|
super
|
177
213
|
end
|
178
214
|
end
|
215
|
+
ruby2_keywords(:method_missing) if respond_to?(:ruby2_keywords, true)
|
179
216
|
end
|
180
217
|
end
|
181
218
|
end
|