dry-types 0.15.0 → 1.5.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +547 -161
- data/LICENSE +17 -17
- data/README.md +15 -13
- data/dry-types.gemspec +27 -30
- data/lib/dry/types/any.rb +23 -12
- data/lib/dry/types/array/constructor.rb +32 -0
- data/lib/dry/types/array/member.rb +74 -15
- data/lib/dry/types/array.rb +18 -2
- data/lib/dry/types/builder.rb +118 -22
- data/lib/dry/types/builder_methods.rb +46 -16
- data/lib/dry/types/coercions/json.rb +43 -7
- data/lib/dry/types/coercions/params.rb +117 -32
- data/lib/dry/types/coercions.rb +76 -22
- data/lib/dry/types/compiler.rb +44 -21
- data/lib/dry/types/constrained/coercible.rb +36 -6
- data/lib/dry/types/constrained.rb +79 -31
- data/lib/dry/types/constraints.rb +18 -4
- data/lib/dry/types/constructor/function.rb +216 -0
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/constructor.rb +110 -61
- data/lib/dry/types/container.rb +6 -1
- data/lib/dry/types/core.rb +34 -11
- data/lib/dry/types/decorator.rb +38 -17
- data/lib/dry/types/default.rb +61 -16
- data/lib/dry/types/enum.rb +36 -20
- data/lib/dry/types/errors.rb +74 -8
- data/lib/dry/types/extensions/maybe.rb +65 -17
- data/lib/dry/types/extensions/monads.rb +29 -0
- data/lib/dry/types/extensions.rb +7 -1
- data/lib/dry/types/fn_container.rb +6 -1
- data/lib/dry/types/hash/constructor.rb +17 -4
- data/lib/dry/types/hash.rb +32 -20
- data/lib/dry/types/inflector.rb +3 -1
- data/lib/dry/types/json.rb +18 -16
- data/lib/dry/types/lax.rb +75 -0
- data/lib/dry/types/map.rb +70 -32
- data/lib/dry/types/meta.rb +51 -0
- data/lib/dry/types/module.rb +16 -11
- data/lib/dry/types/nominal.rb +113 -22
- data/lib/dry/types/options.rb +12 -25
- data/lib/dry/types/params.rb +39 -25
- data/lib/dry/types/predicate_inferrer.rb +238 -0
- data/lib/dry/types/predicate_registry.rb +34 -0
- data/lib/dry/types/primitive_inferrer.rb +97 -0
- data/lib/dry/types/printable.rb +5 -1
- data/lib/dry/types/printer.rb +63 -57
- data/lib/dry/types/result.rb +29 -3
- data/lib/dry/types/schema/key.rb +62 -36
- data/lib/dry/types/schema.rb +201 -91
- data/lib/dry/types/spec/types.rb +99 -37
- data/lib/dry/types/sum.rb +75 -25
- data/lib/dry/types/type.rb +49 -0
- data/lib/dry/types/version.rb +3 -1
- data/lib/dry/types.rb +106 -48
- data/lib/dry-types.rb +3 -1
- metadata +55 -78
- data/.codeclimate.yml +0 -15
- data/.gitignore +0 -10
- data/.rspec +0 -2
- data/.rubocop.yml +0 -43
- data/.travis.yml +0 -28
- data/.yardopts +0 -5
- data/CONTRIBUTING.md +0 -29
- data/Gemfile +0 -23
- data/Rakefile +0 -20
- data/benchmarks/hash_schemas.rb +0 -51
- data/lib/dry/types/safe.rb +0 -61
- data/log/.gitkeep +0 -0
@@ -1,12 +1,42 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module Dry
|
2
4
|
module Types
|
3
5
|
class Constrained
|
6
|
+
# Common coercion-related API for constrained types
|
7
|
+
#
|
8
|
+
# @api public
|
4
9
|
class Coercible < Constrained
|
5
|
-
# @
|
6
|
-
#
|
7
|
-
# @
|
8
|
-
|
9
|
-
|
10
|
+
# @return [Object]
|
11
|
+
#
|
12
|
+
# @api private
|
13
|
+
def call_unsafe(input)
|
14
|
+
coerced = type.call_unsafe(input)
|
15
|
+
result = rule.(coerced)
|
16
|
+
|
17
|
+
if result.success?
|
18
|
+
coerced
|
19
|
+
else
|
20
|
+
raise ConstraintError.new(result, input)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Object]
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
def call_safe(input)
|
28
|
+
coerced = type.call_safe(input) { return yield }
|
29
|
+
|
30
|
+
if rule[coerced]
|
31
|
+
coerced
|
32
|
+
else
|
33
|
+
yield(coerced)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
# @see Dry::Types::Constrained#try
|
38
|
+
#
|
39
|
+
# @api public
|
10
40
|
def try(input, &block)
|
11
41
|
result = type.try(input)
|
12
42
|
|
@@ -16,7 +46,7 @@ module Dry
|
|
16
46
|
if validation.success?
|
17
47
|
result
|
18
48
|
else
|
19
|
-
failure = failure(result.input, validation)
|
49
|
+
failure = failure(result.input, ConstraintError.new(validation, input))
|
20
50
|
block ? yield(failure) : failure
|
21
51
|
end
|
22
52
|
else
|
@@ -1,94 +1,142 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "dry/types/decorator"
|
5
|
+
require "dry/types/constraints"
|
6
|
+
require "dry/types/constrained/coercible"
|
4
7
|
|
5
8
|
module Dry
|
6
9
|
module Types
|
10
|
+
# Constrained types apply rules to the input
|
11
|
+
#
|
12
|
+
# @api public
|
7
13
|
class Constrained
|
8
14
|
include Type
|
9
15
|
include Decorator
|
10
16
|
include Builder
|
11
17
|
include Printable
|
12
|
-
include Dry::Equalizer(:type, :
|
18
|
+
include Dry::Equalizer(:type, :rule, inspect: false, immutable: true)
|
13
19
|
|
14
20
|
# @return [Dry::Logic::Rule]
|
15
21
|
attr_reader :rule
|
16
22
|
|
17
23
|
# @param [Type] type
|
24
|
+
#
|
18
25
|
# @param [Hash] options
|
19
|
-
|
26
|
+
#
|
27
|
+
# @api public
|
28
|
+
def initialize(type, **options)
|
20
29
|
super
|
21
30
|
@rule = options.fetch(:rule)
|
22
31
|
end
|
23
32
|
|
24
|
-
# @param [Object] input
|
25
33
|
# @return [Object]
|
26
|
-
#
|
27
|
-
|
28
|
-
|
34
|
+
#
|
35
|
+
# @api private
|
36
|
+
def call_unsafe(input)
|
37
|
+
result = rule.(input)
|
38
|
+
|
39
|
+
if result.success?
|
40
|
+
type.call_unsafe(input)
|
41
|
+
else
|
29
42
|
raise ConstraintError.new(result, input)
|
30
|
-
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# @return [Object]
|
47
|
+
#
|
48
|
+
# @api private
|
49
|
+
def call_safe(input, &block)
|
50
|
+
if rule[input]
|
51
|
+
type.call_safe(input, &block)
|
52
|
+
else
|
53
|
+
yield
|
54
|
+
end
|
31
55
|
end
|
32
|
-
|
33
|
-
|
34
|
-
#
|
35
|
-
#
|
36
|
-
#
|
37
|
-
# @
|
38
|
-
#
|
39
|
-
#
|
56
|
+
|
57
|
+
# Safe coercion attempt. It is similar to #call with a
|
58
|
+
# block given but returns a Result instance with metadata
|
59
|
+
# about errors (if any).
|
60
|
+
#
|
61
|
+
# @overload try(input)
|
62
|
+
# @param [Object] input
|
63
|
+
# @return [Logic::Result]
|
64
|
+
#
|
65
|
+
# @overload try(input)
|
66
|
+
# @param [Object] input
|
67
|
+
# @yieldparam [Failure] failure
|
68
|
+
# @yieldreturn [Object]
|
69
|
+
# @return [Object]
|
70
|
+
#
|
71
|
+
# @api public
|
40
72
|
def try(input, &block)
|
41
73
|
result = rule.(input)
|
42
74
|
|
43
75
|
if result.success?
|
44
76
|
type.try(input, &block)
|
45
77
|
else
|
46
|
-
failure = failure(input, result)
|
47
|
-
|
78
|
+
failure = failure(input, ConstraintError.new(result, input))
|
79
|
+
block_given? ? yield(failure) : failure
|
48
80
|
end
|
49
81
|
end
|
50
82
|
|
51
|
-
# @param [Object] value
|
52
|
-
# @return [Boolean]
|
53
|
-
def valid?(value)
|
54
|
-
rule.(value).success? && type.valid?(value)
|
55
|
-
end
|
56
|
-
|
57
83
|
# @param [Hash] options
|
58
84
|
# The options hash provided to {Types.Rule} and combined
|
59
85
|
# using {&} with previous {#rule}
|
86
|
+
#
|
60
87
|
# @return [Constrained]
|
88
|
+
#
|
61
89
|
# @see Dry::Logic::Operators#and
|
90
|
+
#
|
91
|
+
# @api public
|
62
92
|
def constrained(options)
|
63
93
|
with(rule: rule & Types.Rule(options))
|
64
94
|
end
|
65
95
|
|
66
96
|
# @return [true]
|
97
|
+
#
|
98
|
+
# @api public
|
67
99
|
def constrained?
|
68
100
|
true
|
69
101
|
end
|
70
102
|
|
71
103
|
# @param [Object] value
|
104
|
+
#
|
72
105
|
# @return [Boolean]
|
106
|
+
#
|
107
|
+
# @api public
|
73
108
|
def ===(value)
|
74
109
|
valid?(value)
|
75
110
|
end
|
76
111
|
|
77
|
-
#
|
112
|
+
# Build lax type. Constraints are not applicable to lax types hence unwrapping
|
78
113
|
#
|
114
|
+
# @return [Lax]
|
115
|
+
# @api public
|
116
|
+
def lax
|
117
|
+
type.lax
|
118
|
+
end
|
119
|
+
|
79
120
|
# @see Nominal#to_ast
|
121
|
+
# @api public
|
80
122
|
def to_ast(meta: true)
|
81
|
-
[:constrained, [type.to_ast(meta: meta),
|
82
|
-
|
83
|
-
|
123
|
+
[:constrained, [type.to_ast(meta: meta), rule.to_ast]]
|
124
|
+
end
|
125
|
+
|
126
|
+
# @api private
|
127
|
+
def constructor_type
|
128
|
+
type.constructor_type
|
84
129
|
end
|
85
130
|
|
86
131
|
private
|
87
132
|
|
88
133
|
# @param [Object] response
|
134
|
+
#
|
89
135
|
# @return [Boolean]
|
136
|
+
#
|
137
|
+
# @api private
|
90
138
|
def decorate?(response)
|
91
|
-
super || response.
|
139
|
+
super || response.is_a?(Constructor)
|
92
140
|
end
|
93
141
|
end
|
94
142
|
end
|
@@ -1,18 +1,32 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/logic/rule_compiler"
|
4
|
+
require "dry/logic/predicates"
|
5
|
+
require "dry/logic/rule/predicate"
|
4
6
|
|
5
7
|
module Dry
|
8
|
+
# Helper methods for constraint types
|
9
|
+
#
|
10
|
+
# @api public
|
6
11
|
module Types
|
7
12
|
# @param [Hash] options
|
13
|
+
#
|
8
14
|
# @return [Dry::Logic::Rule]
|
15
|
+
#
|
16
|
+
# @api public
|
9
17
|
def self.Rule(options)
|
10
18
|
rule_compiler.(
|
11
|
-
options.map { |key, val|
|
19
|
+
options.map { |key, val|
|
20
|
+
Logic::Rule::Predicate.build(
|
21
|
+
Logic::Predicates[:"#{key}?"]
|
22
|
+
).curry(val).to_ast
|
23
|
+
}
|
12
24
|
).reduce(:and)
|
13
25
|
end
|
14
26
|
|
15
27
|
# @return [Dry::Logic::RuleCompiler]
|
28
|
+
#
|
29
|
+
# @api private
|
16
30
|
def self.rule_compiler
|
17
31
|
@rule_compiler ||= Logic::RuleCompiler.new(Logic::Predicates)
|
18
32
|
end
|
@@ -0,0 +1,216 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "concurrent/map"
|
5
|
+
|
6
|
+
module Dry
|
7
|
+
module Types
|
8
|
+
class Constructor < Nominal
|
9
|
+
# Function is used internally by Constructor types
|
10
|
+
#
|
11
|
+
# @api private
|
12
|
+
class Function
|
13
|
+
# Wrapper for unsafe coercion functions
|
14
|
+
#
|
15
|
+
# @api private
|
16
|
+
class Safe < Function
|
17
|
+
def call(input, &block)
|
18
|
+
@fn.(input, &block)
|
19
|
+
rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
|
20
|
+
CoercionError.handle(e, &block)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
# Coercion via a method call on a known object
|
25
|
+
#
|
26
|
+
# @api private
|
27
|
+
class MethodCall < Function
|
28
|
+
@cache = ::Concurrent::Map.new
|
29
|
+
|
30
|
+
# Choose or build the base class
|
31
|
+
#
|
32
|
+
# @return [Function]
|
33
|
+
def self.call_class(method, public, safe)
|
34
|
+
@cache.fetch_or_store([method, public, safe]) do
|
35
|
+
if public
|
36
|
+
::Class.new(PublicCall) do
|
37
|
+
include PublicCall.call_interface(method, safe)
|
38
|
+
|
39
|
+
define_method(:__to_s__) do
|
40
|
+
"#<PublicCall for :#{method}>"
|
41
|
+
end
|
42
|
+
end
|
43
|
+
elsif safe
|
44
|
+
PrivateCall
|
45
|
+
else
|
46
|
+
PrivateSafeCall
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Coercion with a publicly accessible method call
|
52
|
+
#
|
53
|
+
# @api private
|
54
|
+
class PublicCall < MethodCall
|
55
|
+
@interfaces = ::Concurrent::Map.new
|
56
|
+
|
57
|
+
# Choose or build the interface
|
58
|
+
#
|
59
|
+
# @return [::Module]
|
60
|
+
def self.call_interface(method, safe)
|
61
|
+
@interfaces.fetch_or_store([method, safe]) do
|
62
|
+
::Module.new do
|
63
|
+
if safe
|
64
|
+
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
65
|
+
def call(input, &block)
|
66
|
+
@target.#{method}(input, &block)
|
67
|
+
end
|
68
|
+
RUBY
|
69
|
+
else
|
70
|
+
module_eval(<<~RUBY, __FILE__, __LINE__ + 1)
|
71
|
+
def call(input, &block)
|
72
|
+
@target.#{method}(input)
|
73
|
+
rescue ::NoMethodError, ::TypeError, ::ArgumentError => error
|
74
|
+
CoercionError.handle(error, &block)
|
75
|
+
end
|
76
|
+
RUBY
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# Coercion via a private method call
|
84
|
+
#
|
85
|
+
# @api private
|
86
|
+
class PrivateCall < MethodCall
|
87
|
+
def call(input, &block)
|
88
|
+
@target.send(@name, input, &block)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Coercion via an unsafe private method call
|
93
|
+
#
|
94
|
+
# @api private
|
95
|
+
class PrivateSafeCall < PrivateCall
|
96
|
+
def call(input, &block)
|
97
|
+
@target.send(@name, input)
|
98
|
+
rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
|
99
|
+
CoercionError.handle(e, &block)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# @api private
|
104
|
+
#
|
105
|
+
# @return [MethodCall]
|
106
|
+
def self.[](fn, safe)
|
107
|
+
public = fn.receiver.respond_to?(fn.name)
|
108
|
+
MethodCall.call_class(fn.name, public, safe).new(fn)
|
109
|
+
end
|
110
|
+
|
111
|
+
attr_reader :target, :name
|
112
|
+
|
113
|
+
def initialize(fn)
|
114
|
+
super
|
115
|
+
@target = fn.receiver
|
116
|
+
@name = fn.name
|
117
|
+
end
|
118
|
+
|
119
|
+
def to_ast
|
120
|
+
[:method, target, name]
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
class Wrapper < Function
|
125
|
+
# @return [Object]
|
126
|
+
def call(input, type, &block)
|
127
|
+
@fn.(input, type, &block)
|
128
|
+
rescue ::NoMethodError, ::TypeError, ::ArgumentError => e
|
129
|
+
CoercionError.handle(e, &block)
|
130
|
+
end
|
131
|
+
alias_method :[], :call
|
132
|
+
|
133
|
+
def arity
|
134
|
+
2
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
# Choose or build specialized invokation code for a callable
|
139
|
+
#
|
140
|
+
# @param [#call] fn
|
141
|
+
# @return [Function]
|
142
|
+
def self.[](fn)
|
143
|
+
raise ::ArgumentError, "Missing constructor block" if fn.nil?
|
144
|
+
|
145
|
+
if fn.is_a?(Function)
|
146
|
+
fn
|
147
|
+
elsif fn.respond_to?(:arity) && fn.arity.equal?(2)
|
148
|
+
Wrapper.new(fn)
|
149
|
+
elsif fn.is_a?(::Method)
|
150
|
+
MethodCall[fn, yields_block?(fn)]
|
151
|
+
elsif yields_block?(fn)
|
152
|
+
new(fn)
|
153
|
+
else
|
154
|
+
Safe.new(fn)
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
# @return [Boolean]
|
159
|
+
def self.yields_block?(fn)
|
160
|
+
*, (last_arg,) =
|
161
|
+
if fn.respond_to?(:parameters)
|
162
|
+
fn.parameters
|
163
|
+
else
|
164
|
+
fn.method(:call).parameters
|
165
|
+
end
|
166
|
+
|
167
|
+
last_arg.equal?(:block)
|
168
|
+
end
|
169
|
+
|
170
|
+
include ::Dry::Equalizer(:fn, immutable: true)
|
171
|
+
|
172
|
+
attr_reader :fn
|
173
|
+
|
174
|
+
def initialize(fn)
|
175
|
+
@fn = fn
|
176
|
+
end
|
177
|
+
|
178
|
+
# @return [Object]
|
179
|
+
def call(input, &block)
|
180
|
+
@fn.(input, &block)
|
181
|
+
end
|
182
|
+
alias_method :[], :call
|
183
|
+
|
184
|
+
# @return [Integer]
|
185
|
+
def arity
|
186
|
+
1
|
187
|
+
end
|
188
|
+
|
189
|
+
def wrapper?
|
190
|
+
arity.equal?(2)
|
191
|
+
end
|
192
|
+
|
193
|
+
# @return [Array]
|
194
|
+
def to_ast
|
195
|
+
if fn.is_a?(::Proc)
|
196
|
+
[:id, FnContainer.register(fn)]
|
197
|
+
else
|
198
|
+
[:callable, fn]
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
# @return [Function]
|
203
|
+
def >>(other)
|
204
|
+
f = Function[other]
|
205
|
+
Function[-> x, &b { f.(self.(x, &b), &b) }]
|
206
|
+
end
|
207
|
+
|
208
|
+
# @return [Function]
|
209
|
+
def <<(other)
|
210
|
+
f = Function[other]
|
211
|
+
Function[-> x, &b { self.(f.(x, &b), &b) }]
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Dry
|
4
|
+
module Types
|
5
|
+
# @api public
|
6
|
+
class Constructor < Nominal
|
7
|
+
module Wrapper
|
8
|
+
# @return [Object]
|
9
|
+
#
|
10
|
+
# @api private
|
11
|
+
def call_safe(input, &block)
|
12
|
+
fn.(input, type, &block)
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Object]
|
16
|
+
#
|
17
|
+
# @api private
|
18
|
+
def call_unsafe(input)
|
19
|
+
fn.(input, type)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @param [Object] input
|
23
|
+
# @param [#call,nil] block
|
24
|
+
#
|
25
|
+
# @return [Logic::Result, Types::Result]
|
26
|
+
# @return [Object] if block given and try fails
|
27
|
+
#
|
28
|
+
# @api public
|
29
|
+
def try(input, &block)
|
30
|
+
value = fn.(input, type)
|
31
|
+
rescue CoercionError => e
|
32
|
+
failure = failure(input, e)
|
33
|
+
block_given? ? yield(failure) : failure
|
34
|
+
else
|
35
|
+
type.try(value, &block)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Define a constructor for the type
|
39
|
+
#
|
40
|
+
# @param [#call,nil] constructor
|
41
|
+
# @param [Hash] options
|
42
|
+
# @param [#call,nil] block
|
43
|
+
#
|
44
|
+
# @return [Constructor]
|
45
|
+
#
|
46
|
+
# @api public
|
47
|
+
define_method(:constructor, Builder.instance_method(:constructor))
|
48
|
+
alias_method :append, :constructor
|
49
|
+
alias_method :>>, :constructor
|
50
|
+
|
51
|
+
# Build a new constructor by prepending a block to the coercion function
|
52
|
+
#
|
53
|
+
# @param [#call, nil] new_fn
|
54
|
+
# @param [Hash] options
|
55
|
+
# @param [#call, nil] block
|
56
|
+
#
|
57
|
+
# @return [Constructor]
|
58
|
+
#
|
59
|
+
# @api public
|
60
|
+
def prepend(new_fn = nil, **options, &block)
|
61
|
+
prep_fn = Function[new_fn || block]
|
62
|
+
|
63
|
+
decorated =
|
64
|
+
if prep_fn.wrapper?
|
65
|
+
type.constructor(prep_fn, **options)
|
66
|
+
else
|
67
|
+
type.prepend(prep_fn, **options)
|
68
|
+
end
|
69
|
+
|
70
|
+
__new__(decorated)
|
71
|
+
end
|
72
|
+
alias_method :<<, :prepend
|
73
|
+
|
74
|
+
# @return [Constructor]
|
75
|
+
#
|
76
|
+
# @api public
|
77
|
+
def lax
|
78
|
+
# return self back because wrapping function
|
79
|
+
# can handle failed type check
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
private
|
84
|
+
|
85
|
+
# Replace underlying type
|
86
|
+
#
|
87
|
+
# @api private
|
88
|
+
def __new__(type)
|
89
|
+
self.class.new(type, *@__args__.drop(1), **@options)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|