dry-types 1.4.0 → 1.5.1
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 +78 -0
- data/LICENSE +1 -1
- data/README.md +1 -1
- data/dry-types.gemspec +2 -3
- data/lib/dry-types.rb +1 -1
- data/lib/dry/types.rb +55 -31
- data/lib/dry/types/any.rb +2 -2
- data/lib/dry/types/array.rb +2 -2
- data/lib/dry/types/array/constructor.rb +1 -1
- data/lib/dry/types/array/member.rb +1 -1
- data/lib/dry/types/builder.rb +66 -18
- data/lib/dry/types/builder_methods.rb +1 -2
- data/lib/dry/types/coercions/json.rb +5 -5
- data/lib/dry/types/coercions/params.rb +3 -3
- data/lib/dry/types/compiler.rb +10 -10
- data/lib/dry/types/constrained.rb +6 -9
- data/lib/dry/types/constraints.rb +3 -3
- data/lib/dry/types/constructor.rb +40 -6
- data/lib/dry/types/constructor/function.rb +32 -2
- data/lib/dry/types/constructor/wrapper.rb +94 -0
- data/lib/dry/types/container.rb +1 -1
- data/lib/dry/types/core.rb +12 -12
- data/lib/dry/types/decorator.rb +2 -2
- data/lib/dry/types/default.rb +14 -1
- data/lib/dry/types/enum.rb +4 -3
- data/lib/dry/types/errors.rb +1 -1
- data/lib/dry/types/extensions.rb +2 -2
- data/lib/dry/types/extensions/maybe.rb +5 -4
- data/lib/dry/types/extensions/monads.rb +1 -1
- data/lib/dry/types/fn_container.rb +1 -1
- data/lib/dry/types/hash.rb +9 -15
- data/lib/dry/types/hash/constructor.rb +1 -1
- data/lib/dry/types/inflector.rb +1 -1
- data/lib/dry/types/json.rb +15 -15
- data/lib/dry/types/lax.rb +2 -2
- data/lib/dry/types/map.rb +2 -2
- data/lib/dry/types/meta.rb +1 -1
- data/lib/dry/types/module.rb +6 -6
- data/lib/dry/types/nominal.rb +11 -11
- data/lib/dry/types/params.rb +30 -28
- data/lib/dry/types/predicate_inferrer.rb +51 -11
- data/lib/dry/types/predicate_registry.rb +1 -1
- data/lib/dry/types/primitive_inferrer.rb +1 -1
- data/lib/dry/types/printer.rb +25 -25
- data/lib/dry/types/result.rb +1 -1
- data/lib/dry/types/schema.rb +5 -13
- data/lib/dry/types/schema/key.rb +4 -4
- data/lib/dry/types/spec/types.rb +57 -45
- data/lib/dry/types/sum.rb +4 -3
- data/lib/dry/types/type.rb +1 -1
- data/lib/dry/types/version.rb +1 -1
- metadata +9 -22
@@ -1,7 +1,8 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dry/core/cache"
|
4
|
+
require "dry/core/class_attributes"
|
5
|
+
require "dry/types/predicate_registry"
|
5
6
|
|
6
7
|
module Dry
|
7
8
|
module Types
|
@@ -12,13 +13,17 @@ module Dry
|
|
12
13
|
extend Core::Cache
|
13
14
|
|
14
15
|
TYPE_TO_PREDICATE = {
|
15
|
-
DateTime => :date_time?,
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
16
|
+
::DateTime => :date_time?,
|
17
|
+
::Date => :date?,
|
18
|
+
::Time => :time?,
|
19
|
+
::FalseClass => :false?,
|
20
|
+
::Integer => :int?,
|
21
|
+
::Float => :float?,
|
22
|
+
::NilClass => :nil?,
|
23
|
+
::String => :str?,
|
24
|
+
::TrueClass => :true?,
|
25
|
+
::BigDecimal => :decimal?,
|
26
|
+
::Array => :array?
|
22
27
|
}.freeze
|
23
28
|
|
24
29
|
REDUCED_TYPES = {
|
@@ -35,6 +40,11 @@ module Dry
|
|
35
40
|
#
|
36
41
|
# @api private
|
37
42
|
class Compiler
|
43
|
+
extend Core::ClassAttributes
|
44
|
+
|
45
|
+
defines :infer_predicate_by_class_name
|
46
|
+
infer_predicate_by_class_name nil
|
47
|
+
|
38
48
|
# @return [PredicateRegistry]
|
39
49
|
# @api private
|
40
50
|
attr_reader :registry
|
@@ -46,7 +56,37 @@ module Dry
|
|
46
56
|
|
47
57
|
# @api private
|
48
58
|
def infer_predicate(type)
|
49
|
-
|
59
|
+
pred = TYPE_TO_PREDICATE.fetch(type) do
|
60
|
+
if type.name.nil? || self.class.infer_predicate_by_class_name.equal?(false)
|
61
|
+
nil
|
62
|
+
else
|
63
|
+
candidate = :"#{type.name.split("::").last.downcase}?"
|
64
|
+
|
65
|
+
if registry.key?(candidate)
|
66
|
+
if self.class.infer_predicate_by_class_name
|
67
|
+
candidate
|
68
|
+
else
|
69
|
+
raise ::KeyError, <<~MESSAGE
|
70
|
+
Automatic predicate inferring from class names is deprecated
|
71
|
+
and will be removed in dry-types 2.0.
|
72
|
+
Use `Dry::Types::PredicateInferrer::Compiler.infer_predicate_by_class_name true`
|
73
|
+
to restore the previous behavior
|
74
|
+
or `Dry::Types::PredicateInferrer::Compiler.infer_predicate_by_class_name false`
|
75
|
+
to explicitly opt-out (i.e. no exception + no inferring).
|
76
|
+
Note: for dry-schema and dry-validation use Dry::Schema::PredicateInferrer::Compiler.
|
77
|
+
MESSAGE
|
78
|
+
end
|
79
|
+
else
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
if pred.nil?
|
86
|
+
EMPTY_ARRAY
|
87
|
+
else
|
88
|
+
[pred]
|
89
|
+
end
|
50
90
|
end
|
51
91
|
|
52
92
|
# @api private
|
@@ -60,7 +100,7 @@ module Dry
|
|
60
100
|
type = node[0]
|
61
101
|
predicate = infer_predicate(type)
|
62
102
|
|
63
|
-
if registry.key?(predicate[0])
|
103
|
+
if !predicate.empty? && registry.key?(predicate[0])
|
64
104
|
predicate
|
65
105
|
else
|
66
106
|
[type?: type]
|
data/lib/dry/types/printer.rb
CHANGED
@@ -7,8 +7,6 @@ module Dry
|
|
7
7
|
MAPPING = {
|
8
8
|
Nominal => :visit_nominal,
|
9
9
|
Constructor => :visit_constructor,
|
10
|
-
Hash::Constructor => :visit_constructor,
|
11
|
-
Array::Constructor => :visit_constructor,
|
12
10
|
Constrained => :visit_constrained,
|
13
11
|
Constrained::Coercible => :visit_constrained,
|
14
12
|
Hash => :visit_hash,
|
@@ -27,14 +25,16 @@ module Dry
|
|
27
25
|
}
|
28
26
|
|
29
27
|
def call(type)
|
30
|
-
output =
|
28
|
+
output = "".dup
|
31
29
|
visit(type) { |str| output << str }
|
32
30
|
"#<Dry::Types[#{output}]>"
|
33
31
|
end
|
34
32
|
|
35
33
|
def visit(type, &block)
|
36
34
|
print_with = MAPPING.fetch(type.class) do
|
37
|
-
if type.
|
35
|
+
if type.class < Constructor
|
36
|
+
:visit_constructor
|
37
|
+
elsif type.is_a?(Type)
|
38
38
|
return yield type.inspect
|
39
39
|
else
|
40
40
|
raise ArgumentError, "Do not know how to print #{type.class}"
|
@@ -44,7 +44,7 @@ module Dry
|
|
44
44
|
end
|
45
45
|
|
46
46
|
def visit_any(_)
|
47
|
-
yield
|
47
|
+
yield "Any"
|
48
48
|
end
|
49
49
|
|
50
50
|
def visit_array(type)
|
@@ -88,11 +88,11 @@ module Dry
|
|
88
88
|
def visit_schema(schema)
|
89
89
|
options = schema.options.dup
|
90
90
|
size = schema.count
|
91
|
-
key_fn_str =
|
92
|
-
type_fn_str =
|
93
|
-
strict_str =
|
91
|
+
key_fn_str = ""
|
92
|
+
type_fn_str = ""
|
93
|
+
strict_str = ""
|
94
94
|
|
95
|
-
strict_str =
|
95
|
+
strict_str = "strict " if options.delete(:strict)
|
96
96
|
|
97
97
|
if key_fn = options.delete(:key_transform_fn)
|
98
98
|
visit_callable(key_fn) do |fn|
|
@@ -119,7 +119,7 @@ module Dry
|
|
119
119
|
else
|
120
120
|
yield header.dup << keys.map { |key|
|
121
121
|
visit(key) { |type| type }
|
122
|
-
}.join(
|
122
|
+
}.join(" ") << "}>"
|
123
123
|
end
|
124
124
|
end
|
125
125
|
end
|
@@ -194,12 +194,12 @@ module Dry
|
|
194
194
|
|
195
195
|
visit_options(options) do |opts|
|
196
196
|
if mapping == enum.inverted_mapping
|
197
|
-
values = mapping.values.map(&:inspect).join(
|
197
|
+
values = mapping.values.map(&:inspect).join(", ")
|
198
198
|
yield "Enum<#{type} values={#{values}}#{opts}>"
|
199
199
|
else
|
200
200
|
mapping_str = mapping.map { |key, value|
|
201
201
|
"#{key.inspect}=>#{value.inspect}"
|
202
|
-
}.join(
|
202
|
+
}.join(", ")
|
203
203
|
yield "Enum<#{type} mapping={#{mapping_str}}#{opts}>"
|
204
204
|
end
|
205
205
|
end
|
@@ -234,7 +234,7 @@ module Dry
|
|
234
234
|
|
235
235
|
def visit_hash(hash)
|
236
236
|
options = hash.options.dup
|
237
|
-
type_fn_str =
|
237
|
+
type_fn_str = ""
|
238
238
|
|
239
239
|
if type_fn = options.delete(:type_transform_fn)
|
240
240
|
visit_callable(type_fn) do |fn|
|
@@ -244,7 +244,7 @@ module Dry
|
|
244
244
|
|
245
245
|
visit_options(options, hash.meta) do |opts|
|
246
246
|
if opts.empty? && type_fn_str.empty?
|
247
|
-
yield
|
247
|
+
yield "Hash"
|
248
248
|
else
|
249
249
|
yield "Hash<#{type_fn_str}#{opts}>"
|
250
250
|
end
|
@@ -255,24 +255,24 @@ module Dry
|
|
255
255
|
fn = callable.is_a?(String) ? FnContainer[callable] : callable
|
256
256
|
|
257
257
|
case fn
|
258
|
-
when Method
|
258
|
+
when ::Method
|
259
259
|
yield "#{fn.receiver}.#{fn.name}"
|
260
|
-
when Proc
|
260
|
+
when ::Proc
|
261
261
|
path, line = fn.source_location
|
262
262
|
|
263
263
|
if line&.zero?
|
264
264
|
yield ".#{path}"
|
265
265
|
elsif path
|
266
|
-
yield "#{path.sub(Dir.pwd +
|
267
|
-
elsif fn.lambda?
|
268
|
-
yield '(lambda)'
|
266
|
+
yield "#{path.sub(Dir.pwd + "/", EMPTY_STRING)}:#{line}"
|
269
267
|
else
|
270
|
-
match = fn.to_s.match(/\A#<Proc:0x\h+\(&:(
|
268
|
+
match = fn.to_s.match(/\A#<Proc:0x\h+\(&:(?<name>\w+)\)(:? \(lambda\))?>\z/)
|
271
269
|
|
272
270
|
if match
|
273
|
-
yield ".#{match[
|
271
|
+
yield ".#{match[:name]}"
|
272
|
+
elsif fn.lambda?
|
273
|
+
yield "(lambda)"
|
274
274
|
else
|
275
|
-
yield
|
275
|
+
yield "(proc)"
|
276
276
|
end
|
277
277
|
end
|
278
278
|
else
|
@@ -288,9 +288,9 @@ module Dry
|
|
288
288
|
|
289
289
|
def visit_options(options, meta = EMPTY_HASH)
|
290
290
|
if options.empty? && meta.empty?
|
291
|
-
yield
|
291
|
+
yield ""
|
292
292
|
else
|
293
|
-
opts = options.empty? ?
|
293
|
+
opts = options.empty? ? "" : " options=#{options.inspect}"
|
294
294
|
|
295
295
|
if meta.empty?
|
296
296
|
yield opts
|
@@ -304,7 +304,7 @@ module Dry
|
|
304
304
|
end
|
305
305
|
end
|
306
306
|
|
307
|
-
yield "#{opts} meta={#{values.join(
|
307
|
+
yield "#{opts} meta={#{values.join(", ")}}"
|
308
308
|
end
|
309
309
|
end
|
310
310
|
end
|
data/lib/dry/types/result.rb
CHANGED
data/lib/dry/types/schema.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
3
|
+
require "dry/types/fn_container"
|
4
4
|
|
5
5
|
module Dry
|
6
6
|
module Types
|
@@ -83,7 +83,7 @@ module Dry
|
|
83
83
|
call_unsafe(hash, options)
|
84
84
|
end
|
85
85
|
|
86
|
-
# @param [Hash] hash
|
86
|
+
# @param input [Hash] hash
|
87
87
|
#
|
88
88
|
# @yieldparam [Failure] failure
|
89
89
|
# @yieldreturn [Result]
|
@@ -145,18 +145,10 @@ module Dry
|
|
145
145
|
#
|
146
146
|
# @api public
|
147
147
|
def to_ast(meta: true)
|
148
|
-
if RUBY_VERSION >= '2.5'
|
149
|
-
opts = options.slice(:key_transform_fn, :type_transform_fn, :strict)
|
150
|
-
else
|
151
|
-
opts = options.select { |k, _|
|
152
|
-
k == :key_transform_fn || k == :type_transform_fn || k == :strict
|
153
|
-
}
|
154
|
-
end
|
155
|
-
|
156
148
|
[
|
157
149
|
:schema,
|
158
150
|
[keys.map { |key| key.to_ast(meta: meta) },
|
159
|
-
|
151
|
+
options.slice(:key_transform_fn, :type_transform_fn, :strict),
|
160
152
|
meta ? self.meta : EMPTY_HASH]
|
161
153
|
]
|
162
154
|
end
|
@@ -190,7 +182,7 @@ module Dry
|
|
190
182
|
def with_key_transform(proc = nil, &block)
|
191
183
|
fn = proc || block
|
192
184
|
|
193
|
-
raise ArgumentError,
|
185
|
+
raise ArgumentError, "a block or callable argument is required" if fn.nil?
|
194
186
|
|
195
187
|
handle = Dry::Types::FnContainer.register(fn)
|
196
188
|
with(key_transform_fn: handle)
|
@@ -288,7 +280,7 @@ module Dry
|
|
288
280
|
#
|
289
281
|
# A new instance is returned.
|
290
282
|
#
|
291
|
-
# @param
|
283
|
+
# @param other [Schema] schema
|
292
284
|
# @return [Schema]
|
293
285
|
#
|
294
286
|
# @api public
|
data/lib/dry/types/schema/key.rb
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
require
|
4
|
-
require
|
3
|
+
require "dry/core/equalizer"
|
4
|
+
require "dry/core/deprecations"
|
5
5
|
|
6
6
|
module Dry
|
7
7
|
module Types
|
@@ -136,8 +136,8 @@ module Dry
|
|
136
136
|
super
|
137
137
|
else
|
138
138
|
self.class.warn(
|
139
|
-
|
140
|
-
|
139
|
+
"Using meta for making schema keys is deprecated, " \
|
140
|
+
"please use .omittable or .required(false) instead" \
|
141
141
|
"\n" + Core::Deprecations::STACK.()
|
142
142
|
)
|
143
143
|
super.required(!data[:omittable])
|
data/lib/dry/types/spec/types.rb
CHANGED
@@ -1,79 +1,79 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
-
RSpec.shared_examples_for
|
3
|
+
RSpec.shared_examples_for "Dry::Types::Nominal without primitive" do
|
4
4
|
def be_boolean
|
5
5
|
satisfy { |x| x == true || x == false }
|
6
6
|
end
|
7
7
|
|
8
|
-
describe
|
9
|
-
it
|
8
|
+
describe "#constrained?" do
|
9
|
+
it "returns a boolean value" do
|
10
10
|
expect(type.constrained?).to be_boolean
|
11
11
|
end
|
12
12
|
end
|
13
13
|
|
14
|
-
describe
|
15
|
-
it
|
14
|
+
describe "#default?" do
|
15
|
+
it "returns a boolean value" do
|
16
16
|
expect(type.default?).to be_boolean
|
17
17
|
end
|
18
18
|
end
|
19
19
|
|
20
|
-
describe
|
21
|
-
it
|
20
|
+
describe "#valid?" do
|
21
|
+
it "returns a boolean value" do
|
22
22
|
expect(type.valid?(1)).to be_boolean
|
23
23
|
end
|
24
24
|
end
|
25
25
|
|
26
|
-
describe
|
27
|
-
it
|
26
|
+
describe "#eql?" do
|
27
|
+
it "has #eql? defined" do
|
28
28
|
expect(type).to eql(type)
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
describe
|
33
|
-
it
|
32
|
+
describe "#==" do
|
33
|
+
it "has #== defined" do
|
34
34
|
expect(type).to eq(type)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
38
|
-
describe
|
39
|
-
it
|
38
|
+
describe "#optional?" do
|
39
|
+
it "returns a boolean value" do
|
40
40
|
expect(type.optional?).to be_boolean
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
describe
|
45
|
-
it
|
46
|
-
expect(type.to_s).to start_with(
|
44
|
+
describe "#to_s" do
|
45
|
+
it "returns a custom string representation" do
|
46
|
+
expect(type.to_s).to start_with("#<Dry::Types") if type.class.name.start_with?("Dry::Types")
|
47
47
|
end
|
48
48
|
end
|
49
49
|
|
50
|
-
describe
|
50
|
+
describe "#to_proc" do
|
51
51
|
subject(:callable) { type.to_proc }
|
52
52
|
|
53
|
-
it
|
53
|
+
it "converts a type to a proc" do
|
54
54
|
expect(callable).to be_a(Proc)
|
55
55
|
end
|
56
56
|
end
|
57
57
|
end
|
58
58
|
|
59
|
-
RSpec.shared_examples_for
|
60
|
-
describe
|
61
|
-
it
|
62
|
-
with_meta = type.meta(foo: :bar).meta(baz:
|
59
|
+
RSpec.shared_examples_for "Dry::Types::Nominal#meta" do
|
60
|
+
describe "#meta" do
|
61
|
+
it "allows setting meta information" do
|
62
|
+
with_meta = type.meta(foo: :bar).meta(baz: "1")
|
63
63
|
|
64
64
|
expect(with_meta).to be_instance_of(type.class)
|
65
|
-
expect(with_meta.meta).to eql(foo: :bar, baz:
|
65
|
+
expect(with_meta.meta).to eql(foo: :bar, baz: "1")
|
66
66
|
end
|
67
67
|
|
68
|
-
it
|
68
|
+
it "equalizes on empty meta" do
|
69
69
|
expect(type).to eql(type.meta({}))
|
70
70
|
end
|
71
71
|
|
72
|
-
it
|
73
|
-
expect(type).to_not eql(type.meta(i_am:
|
72
|
+
it "equalizes on filled meta" do
|
73
|
+
expect(type).to_not eql(type.meta(i_am: "different"))
|
74
74
|
end
|
75
75
|
|
76
|
-
it
|
76
|
+
it "is locally immutable" do
|
77
77
|
expect(type.meta).to be_a ::Hash
|
78
78
|
expect(type.meta).to be_frozen
|
79
79
|
expect(type.meta).not_to have_key :immutable_test
|
@@ -84,24 +84,24 @@ RSpec.shared_examples_for 'Dry::Types::Nominal#meta' do
|
|
84
84
|
end
|
85
85
|
end
|
86
86
|
|
87
|
-
describe
|
88
|
-
it
|
87
|
+
describe "#pristine" do
|
88
|
+
it "erases meta" do
|
89
89
|
expect(type.meta(foo: :bar).pristine).to eql(type)
|
90
90
|
end
|
91
91
|
end
|
92
92
|
end
|
93
93
|
|
94
94
|
RSpec.shared_examples_for Dry::Types::Nominal do
|
95
|
-
it_behaves_like
|
95
|
+
it_behaves_like "Dry::Types::Nominal without primitive"
|
96
96
|
|
97
|
-
describe
|
98
|
-
it
|
97
|
+
describe "#primitive" do
|
98
|
+
it "returns a class" do
|
99
99
|
expect(type.primitive).to be_instance_of(Class)
|
100
100
|
end
|
101
101
|
end
|
102
102
|
|
103
|
-
describe
|
104
|
-
it
|
103
|
+
describe "#constructor" do
|
104
|
+
it "returns a constructor" do
|
105
105
|
constructor = type.constructor(&:to_s)
|
106
106
|
|
107
107
|
expect(constructor).to be_a(Dry::Types::Type)
|
@@ -109,40 +109,52 @@ RSpec.shared_examples_for Dry::Types::Nominal do
|
|
109
109
|
end
|
110
110
|
end
|
111
111
|
|
112
|
-
RSpec.shared_examples_for
|
112
|
+
RSpec.shared_examples_for "a constrained type" do |options = {inputs: Object.new}|
|
113
113
|
inputs = options[:inputs]
|
114
114
|
|
115
115
|
let(:fallback) { Object.new }
|
116
116
|
|
117
|
-
describe
|
118
|
-
it
|
117
|
+
describe "#call" do
|
118
|
+
it "yields a block on failure" do
|
119
119
|
Array(inputs).each do |input|
|
120
120
|
expect(type.(input) { fallback }).to be(fallback)
|
121
121
|
end
|
122
122
|
end
|
123
123
|
|
124
|
-
it
|
124
|
+
it "throws an error on invalid input" do
|
125
125
|
Array(inputs).each do |input|
|
126
126
|
expect { type.(input) }.to raise_error(Dry::Types::CoercionError)
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
130
|
+
|
131
|
+
describe "#constructor" do
|
132
|
+
let(:wrapping_constructor) do
|
133
|
+
type.constructor { |input, type| type.(input) { fallback } }
|
134
|
+
end
|
135
|
+
|
136
|
+
it "can be wrapped" do
|
137
|
+
Array(inputs).each do |input|
|
138
|
+
expect(wrapping_constructor.(input)).to be(fallback)
|
139
|
+
end
|
140
|
+
end
|
141
|
+
end
|
130
142
|
end
|
131
143
|
|
132
|
-
RSpec.shared_examples_for
|
133
|
-
describe
|
134
|
-
it
|
144
|
+
RSpec.shared_examples_for "a nominal type" do |inputs: Object.new|
|
145
|
+
describe "#call" do
|
146
|
+
it "always returns the input back" do
|
135
147
|
Array(inputs).each do |input|
|
136
|
-
expect(type.(input) {
|
148
|
+
expect(type.(input) { raise }).to be(input)
|
137
149
|
expect(type.(input)).to be(input)
|
138
150
|
end
|
139
151
|
end
|
140
152
|
end
|
141
153
|
end
|
142
154
|
|
143
|
-
RSpec.shared_examples_for
|
144
|
-
describe
|
145
|
-
it
|
155
|
+
RSpec.shared_examples_for "a composable constructor" do
|
156
|
+
describe "#constructor" do
|
157
|
+
it "has aliases for composition" do
|
146
158
|
expect(type.method(:append)).to eql(type.method(:constructor))
|
147
159
|
expect(type.method(:prepend)).to eql(type.method(:constructor))
|
148
160
|
expect(type.method(:<<)).to eql(type.method(:constructor))
|