teckel 0.4.0 → 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +71 -0
- data/README.md +3 -3
- data/lib/teckel/chain/config.rb +286 -0
- data/lib/teckel/chain/result.rb +3 -3
- data/lib/teckel/chain/runner.rb +28 -17
- data/lib/teckel/chain.rb +14 -190
- data/lib/teckel/config.rb +13 -7
- data/lib/teckel/operation/config.rb +400 -0
- data/lib/teckel/operation/result.rb +8 -8
- data/lib/teckel/operation/runner.rb +29 -25
- data/lib/teckel/operation.rb +87 -388
- data/lib/teckel/result.rb +8 -6
- data/lib/teckel/version.rb +1 -1
- data/lib/teckel.rb +0 -1
- data/spec/chain/around_hook_spec.rb +100 -0
- data/spec/chain/default_settings_spec.rb +39 -0
- data/spec/chain/inheritance_spec.rb +44 -44
- data/spec/chain/none_input_spec.rb +36 -0
- data/spec/chain/results_spec.rb +28 -28
- data/spec/chain_spec.rb +132 -57
- data/spec/doctest_helper.rb +1 -0
- data/spec/operation/config_spec.rb +227 -0
- data/spec/operation/contract_trace_spec.rb +118 -0
- data/spec/operation/default_settings_spec.rb +120 -0
- data/spec/operation/fail_on_input_spec.rb +103 -0
- data/spec/operation/inheritance_spec.rb +38 -38
- data/spec/operation/result_spec.rb +27 -12
- data/spec/operation/results_spec.rb +67 -67
- data/spec/operation_spec.rb +276 -230
- data/spec/rb27/pattern_matching_spec.rb +2 -2
- data/spec/result_spec.rb +12 -10
- data/spec/spec_helper.rb +10 -0
- metadata +41 -12
- data/spec/chain_around_hook_spec.rb +0 -100
data/lib/teckel/result.rb
CHANGED
@@ -47,16 +47,18 @@ module Teckel
|
|
47
47
|
end
|
48
48
|
|
49
49
|
def deconstruct_keys(keys)
|
50
|
-
{}
|
51
|
-
|
52
|
-
|
53
|
-
|
50
|
+
e = {}
|
51
|
+
e[:success] = successful? if keys.include?(:success)
|
52
|
+
e[:value] = value if keys.include?(:value)
|
53
|
+
e
|
54
54
|
end
|
55
55
|
end
|
56
56
|
|
57
57
|
def self.included(receiver)
|
58
|
-
receiver.
|
59
|
-
|
58
|
+
receiver.class_eval do
|
59
|
+
extend ClassMethods
|
60
|
+
include InstanceMethods
|
61
|
+
end
|
60
62
|
end
|
61
63
|
end
|
62
64
|
end
|
data/lib/teckel/version.rb
CHANGED
data/lib/teckel.rb
CHANGED
@@ -0,0 +1,100 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'support/dry_base'
|
4
|
+
require 'support/fake_db'
|
5
|
+
require 'support/fake_models'
|
6
|
+
|
7
|
+
module TeckelChainAroundHookTest
|
8
|
+
class CreateUser
|
9
|
+
include ::Teckel::Operation
|
10
|
+
|
11
|
+
result!
|
12
|
+
|
13
|
+
input Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer.optional)
|
14
|
+
output Types.Instance(User)
|
15
|
+
error Types::Hash.schema(message: Types::String, errors: Types::Array.of(Types::Hash))
|
16
|
+
|
17
|
+
def call(input)
|
18
|
+
user = User.new(name: input[:name], age: input[:age])
|
19
|
+
if user.save
|
20
|
+
success!(user)
|
21
|
+
else
|
22
|
+
fail!(message: "Could not safe User", errors: user.errors)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class AddFriend
|
28
|
+
include ::Teckel::Operation
|
29
|
+
|
30
|
+
result!
|
31
|
+
|
32
|
+
settings Struct.new(:fail_befriend)
|
33
|
+
|
34
|
+
input Types.Instance(User)
|
35
|
+
output Types::Hash.schema(user: Types.Instance(User), friend: Types.Instance(User))
|
36
|
+
error Types::Hash.schema(message: Types::String)
|
37
|
+
|
38
|
+
def call(user)
|
39
|
+
if settings&.fail_befriend
|
40
|
+
fail!(message: "Did not find a friend.")
|
41
|
+
else
|
42
|
+
success! user: user, friend: User.new(name: "A friend", age: 42)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
@stack = []
|
48
|
+
def self.stack
|
49
|
+
@stack
|
50
|
+
end
|
51
|
+
|
52
|
+
class Chain
|
53
|
+
include Teckel::Chain
|
54
|
+
|
55
|
+
around ->(chain, input) {
|
56
|
+
result = nil
|
57
|
+
begin
|
58
|
+
TeckelChainAroundHookTest.stack << :before
|
59
|
+
|
60
|
+
FakeDB.transaction do
|
61
|
+
result = chain.call(input)
|
62
|
+
raise FakeDB::Rollback if result.failure?
|
63
|
+
end
|
64
|
+
|
65
|
+
TeckelChainAroundHookTest.stack << :after
|
66
|
+
result
|
67
|
+
rescue FakeDB::Rollback
|
68
|
+
result
|
69
|
+
end
|
70
|
+
}
|
71
|
+
|
72
|
+
step :create, CreateUser
|
73
|
+
step :befriend, AddFriend
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
RSpec.describe Teckel::Chain do
|
78
|
+
before { TeckelChainAroundHookTest.stack.clear }
|
79
|
+
|
80
|
+
context "success" do
|
81
|
+
it "result matches" do
|
82
|
+
result = TeckelChainAroundHookTest::Chain.call(name: "Bob", age: 23)
|
83
|
+
expect(result.success).to include(user: kind_of(User), friend: kind_of(User))
|
84
|
+
end
|
85
|
+
|
86
|
+
it "runs around hook" do
|
87
|
+
TeckelChainAroundHookTest::Chain.call(name: "Bob", age: 23)
|
88
|
+
expect(TeckelChainAroundHookTest.stack).to eq([:before, :after])
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
context "failure" do
|
93
|
+
it "runs around hook" do
|
94
|
+
TeckelChainAroundHookTest::Chain.
|
95
|
+
with(befriend: :fail).
|
96
|
+
call(name: "Bob", age: 23)
|
97
|
+
expect(TeckelChainAroundHookTest.stack).to eq([:before])
|
98
|
+
end
|
99
|
+
end
|
100
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TeckelChainDefaultSettingsTest
|
4
|
+
class MyOperation
|
5
|
+
include Teckel::Operation
|
6
|
+
result!
|
7
|
+
|
8
|
+
settings Struct.new(:say, :other)
|
9
|
+
settings_constructor ->(data) { settings.new(*data.values_at(*settings.members)) } # ruby 2.4 way for `keyword_init: true`
|
10
|
+
|
11
|
+
input none
|
12
|
+
output Hash
|
13
|
+
error none
|
14
|
+
|
15
|
+
def call(_)
|
16
|
+
success! settings.to_h
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
class Chain
|
21
|
+
include Teckel::Chain
|
22
|
+
|
23
|
+
default_settings!(a: { say: "Chain Default" })
|
24
|
+
|
25
|
+
step :a, MyOperation
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
RSpec.describe Teckel::Chain do
|
30
|
+
specify "call chain without settings, uses default settings" do
|
31
|
+
result = TeckelChainDefaultSettingsTest::Chain.call
|
32
|
+
expect(result.success).to eq(say: "Chain Default", other: nil)
|
33
|
+
end
|
34
|
+
|
35
|
+
specify "call chain with explicit settings, overwrites defaults" do
|
36
|
+
result = TeckelChainDefaultSettingsTest::Chain.with(a: { other: "What" }).call
|
37
|
+
expect(result.success).to eq(say: nil, other: "What")
|
38
|
+
end
|
39
|
+
end
|
@@ -3,72 +3,72 @@
|
|
3
3
|
require 'support/dry_base'
|
4
4
|
require 'support/fake_models'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
LOG = [] # rubocop:disable Style/MutableConstant
|
9
|
-
|
10
|
-
class LoggingChain
|
11
|
-
include Teckel::Chain
|
6
|
+
module TeckelChainDefaultsViaBaseClass
|
7
|
+
LOG = [] # rubocop:disable Style/MutableConstant
|
12
8
|
|
13
|
-
|
14
|
-
|
15
|
-
result = nil
|
16
|
-
LOG << Benchmark.measure { result = chain.call(input) }
|
17
|
-
result
|
18
|
-
end
|
9
|
+
class LoggingChain
|
10
|
+
include Teckel::Chain
|
19
11
|
|
20
|
-
|
12
|
+
around do |chain, input|
|
13
|
+
require 'benchmark'
|
14
|
+
result = nil
|
15
|
+
LOG << Benchmark.measure { result = chain.call(input) }
|
16
|
+
result
|
21
17
|
end
|
22
18
|
|
23
|
-
|
24
|
-
|
19
|
+
freeze
|
20
|
+
end
|
25
21
|
|
26
|
-
|
22
|
+
class OperationA
|
23
|
+
include Teckel::Operation
|
27
24
|
|
28
|
-
|
29
|
-
output Types::Integer
|
30
|
-
error none
|
25
|
+
result!
|
31
26
|
|
32
|
-
|
33
|
-
|
34
|
-
|
27
|
+
input none
|
28
|
+
output Types::Integer
|
29
|
+
error none
|
35
30
|
|
36
|
-
|
31
|
+
def call(_)
|
32
|
+
success! rand(1000)
|
37
33
|
end
|
38
34
|
|
39
|
-
|
40
|
-
|
35
|
+
finalize!
|
36
|
+
end
|
41
37
|
|
42
|
-
|
38
|
+
class OperationB
|
39
|
+
include Teckel::Operation
|
43
40
|
|
44
|
-
|
45
|
-
output Types::String
|
46
|
-
error none
|
41
|
+
result!
|
47
42
|
|
48
|
-
|
49
|
-
|
50
|
-
|
43
|
+
input none
|
44
|
+
output Types::String
|
45
|
+
error none
|
51
46
|
|
52
|
-
|
47
|
+
def call(_)
|
48
|
+
success! ("a".."z").to_a.sample
|
53
49
|
end
|
54
50
|
|
55
|
-
|
56
|
-
|
51
|
+
finalize!
|
52
|
+
end
|
57
53
|
|
58
|
-
|
59
|
-
|
54
|
+
class ChainA < LoggingChain
|
55
|
+
step :roll, OperationA
|
60
56
|
|
61
|
-
|
62
|
-
|
57
|
+
finalize!
|
58
|
+
end
|
63
59
|
|
64
|
-
|
65
|
-
|
60
|
+
class ChainB < LoggingChain
|
61
|
+
step :say, OperationB
|
66
62
|
|
67
|
-
|
68
|
-
|
69
|
-
|
63
|
+
finalize!
|
64
|
+
end
|
65
|
+
|
66
|
+
class ChainC < ChainB
|
67
|
+
finalize!
|
70
68
|
end
|
69
|
+
end
|
71
70
|
|
71
|
+
RSpec.describe Teckel::Chain do
|
72
72
|
before do
|
73
73
|
TeckelChainDefaultsViaBaseClass::LOG.clear
|
74
74
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module TeckelChainNoneInputTest
|
4
|
+
class MyOperation
|
5
|
+
include Teckel::Operation
|
6
|
+
result!
|
7
|
+
|
8
|
+
settings Struct.new(:say)
|
9
|
+
|
10
|
+
input none
|
11
|
+
output String
|
12
|
+
error none
|
13
|
+
|
14
|
+
def call(_)
|
15
|
+
success!(settings&.say || "Called")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
class Chain
|
20
|
+
include Teckel::Chain
|
21
|
+
|
22
|
+
step :a, MyOperation
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
RSpec.describe Teckel::Chain do
|
27
|
+
specify "call chain without input value" do
|
28
|
+
result = TeckelChainNoneInputTest::Chain.call
|
29
|
+
expect(result.success).to eq("Called")
|
30
|
+
end
|
31
|
+
|
32
|
+
specify "call chain runner without input value" do
|
33
|
+
result = TeckelChainNoneInputTest::Chain.with(a: "What").call
|
34
|
+
expect(result.success).to eq("What")
|
35
|
+
end
|
36
|
+
end
|
data/spec/chain/results_spec.rb
CHANGED
@@ -3,47 +3,47 @@
|
|
3
3
|
require 'support/dry_base'
|
4
4
|
require 'support/fake_models'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
include ::Teckel::Operation
|
6
|
+
module TeckelChainResultTest
|
7
|
+
class Message
|
8
|
+
include ::Teckel::Operation
|
10
9
|
|
11
|
-
|
10
|
+
result!
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
|
12
|
+
input Types::Hash.schema(message: Types::String)
|
13
|
+
error none
|
14
|
+
output Types::String
|
16
15
|
|
17
|
-
|
18
|
-
|
19
|
-
end
|
16
|
+
def call(input)
|
17
|
+
success! input[:message].upcase
|
20
18
|
end
|
19
|
+
end
|
21
20
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
step :message, Message
|
21
|
+
class Chain
|
22
|
+
include Teckel::Chain
|
26
23
|
|
27
|
-
|
28
|
-
def initialize(value, success, step, opts = {})
|
29
|
-
super(value, success)
|
30
|
-
@step = step
|
31
|
-
@opts = opts
|
32
|
-
end
|
24
|
+
step :message, Message
|
33
25
|
|
34
|
-
|
35
|
-
|
36
|
-
|
26
|
+
class Result < Teckel::Operation::Result
|
27
|
+
def initialize(value, success, step, opts = {})
|
28
|
+
super(value, success)
|
29
|
+
@step = step
|
30
|
+
@opts = opts
|
31
|
+
end
|
37
32
|
|
38
|
-
|
33
|
+
class << self
|
34
|
+
alias :[] :new # Alias the default constructor to :new
|
39
35
|
end
|
40
36
|
|
41
|
-
|
42
|
-
result.new(value, success, step, time: Time.now.to_i)
|
43
|
-
}
|
37
|
+
attr_reader :opts, :step
|
44
38
|
end
|
39
|
+
|
40
|
+
result_constructor ->(value, success, step) {
|
41
|
+
result.new(value, success, step, time: Time.now.to_i)
|
42
|
+
}
|
45
43
|
end
|
44
|
+
end
|
46
45
|
|
46
|
+
RSpec.describe Teckel::Chain do
|
47
47
|
specify do
|
48
48
|
result = TeckelChainResultTest::Chain.call(message: "Hello World!")
|
49
49
|
expect(result).to be_successful
|
data/spec/chain_spec.rb
CHANGED
@@ -3,68 +3,73 @@
|
|
3
3
|
require 'support/dry_base'
|
4
4
|
require 'support/fake_models'
|
5
5
|
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
fail!(message: "Could not save User", errors: user.errors)
|
22
|
-
end
|
6
|
+
module TeckelChainTest
|
7
|
+
class CreateUser
|
8
|
+
include ::Teckel::Operation
|
9
|
+
result!
|
10
|
+
|
11
|
+
input Types::Hash.schema(name: Types::String, age: Types::Coercible::Integer.optional)
|
12
|
+
output Types.Instance(User)
|
13
|
+
error Types::Hash.schema(message: Types::String, errors: Types::Array.of(Types::Hash))
|
14
|
+
|
15
|
+
def call(input)
|
16
|
+
user = User.new(name: input[:name], age: input[:age])
|
17
|
+
if user.save
|
18
|
+
success!(user)
|
19
|
+
else
|
20
|
+
fail!(message: "Could not save User", errors: user.errors)
|
23
21
|
end
|
24
22
|
end
|
23
|
+
end
|
25
24
|
|
26
|
-
|
27
|
-
|
25
|
+
class LogUser
|
26
|
+
include ::Teckel::Operation
|
28
27
|
|
29
|
-
|
28
|
+
result!
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
|
30
|
+
input Types.Instance(User)
|
31
|
+
error none
|
32
|
+
output input
|
34
33
|
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
end
|
34
|
+
def call(usr)
|
35
|
+
Logger.new(File::NULL).info("User #{usr.name} created")
|
36
|
+
success! usr
|
39
37
|
end
|
38
|
+
end
|
40
39
|
|
41
|
-
|
42
|
-
|
40
|
+
class AddFriend
|
41
|
+
include ::Teckel::Operation
|
43
42
|
|
44
|
-
|
43
|
+
result!
|
45
44
|
|
46
|
-
|
45
|
+
settings Struct.new(:fail_befriend)
|
47
46
|
|
48
|
-
|
49
|
-
|
50
|
-
|
47
|
+
input Types.Instance(User)
|
48
|
+
output Types::Hash.schema(user: Types.Instance(User), friend: Types.Instance(User))
|
49
|
+
error Types::Hash.schema(message: Types::String)
|
51
50
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
end
|
51
|
+
def call(user)
|
52
|
+
if settings&.fail_befriend
|
53
|
+
fail!(message: "Did not find a friend.")
|
54
|
+
else
|
55
|
+
success! user: user, friend: User.new(name: "A friend", age: 42)
|
58
56
|
end
|
59
57
|
end
|
58
|
+
end
|
60
59
|
|
61
|
-
|
62
|
-
|
60
|
+
class Chain
|
61
|
+
include Teckel::Chain
|
63
62
|
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
63
|
+
step :create, CreateUser
|
64
|
+
step :log, LogUser
|
65
|
+
step :befriend, AddFriend
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
RSpec.describe Teckel::Chain do
|
70
|
+
let(:frozen_error) do
|
71
|
+
# different ruby versions raise different errors
|
72
|
+
defined?(FrozenError) ? FrozenError : RuntimeError
|
68
73
|
end
|
69
74
|
|
70
75
|
it 'Chain input points to first step input' do
|
@@ -85,8 +90,7 @@ RSpec.describe Teckel::Chain do
|
|
85
90
|
|
86
91
|
context "success" do
|
87
92
|
it "result matches" do
|
88
|
-
result =
|
89
|
-
TeckelChainTest::Chain.
|
93
|
+
result = TeckelChainTest::Chain.
|
90
94
|
with(befriend: nil).
|
91
95
|
call(name: "Bob", age: 23)
|
92
96
|
|
@@ -96,8 +100,7 @@ RSpec.describe Teckel::Chain do
|
|
96
100
|
|
97
101
|
context "failure" do
|
98
102
|
it "returns a Result for invalid input" do
|
99
|
-
result =
|
100
|
-
TeckelChainTest::Chain.
|
103
|
+
result = TeckelChainTest::Chain.
|
101
104
|
with(befriend: :fail).
|
102
105
|
call(name: "Bob", age: 0)
|
103
106
|
|
@@ -108,8 +111,7 @@ RSpec.describe Teckel::Chain do
|
|
108
111
|
end
|
109
112
|
|
110
113
|
it "returns a Result for failed step" do
|
111
|
-
result =
|
112
|
-
TeckelChainTest::Chain.
|
114
|
+
result = TeckelChainTest::Chain.
|
113
115
|
with(befriend: :fail).
|
114
116
|
call(name: "Bob", age: 23)
|
115
117
|
|
@@ -121,11 +123,6 @@ RSpec.describe Teckel::Chain do
|
|
121
123
|
end
|
122
124
|
|
123
125
|
describe "#finalize!" do
|
124
|
-
let(:frozen_error) do
|
125
|
-
# different ruby versions raise different errors
|
126
|
-
defined?(FrozenError) ? FrozenError : RuntimeError
|
127
|
-
end
|
128
|
-
|
129
126
|
subject { TeckelChainTest::Chain.dup }
|
130
127
|
|
131
128
|
it "freezes the Chain class and operation classes" do
|
@@ -177,4 +174,82 @@ RSpec.describe Teckel::Chain do
|
|
177
174
|
expect(subject.call).to eq(:mocked)
|
178
175
|
end
|
179
176
|
end
|
177
|
+
|
178
|
+
describe "#clone" do
|
179
|
+
subject { TeckelChainTest::Chain.dup }
|
180
|
+
let(:klone) { subject.clone }
|
181
|
+
|
182
|
+
it 'clones' do
|
183
|
+
expect(klone.object_id).not_to be_eql(subject.object_id)
|
184
|
+
end
|
185
|
+
|
186
|
+
it 'clones config' do
|
187
|
+
orig_config = subject.instance_variable_get(:@config)
|
188
|
+
klone_config = klone.instance_variable_get(:@config)
|
189
|
+
expect(klone_config.object_id).not_to be_eql(orig_config.object_id)
|
190
|
+
end
|
191
|
+
|
192
|
+
it 'clones steps' do
|
193
|
+
orig_settings = subject.instance_variable_get(:@config).instance_variable_get(:@config)[:steps]
|
194
|
+
klone_settings = klone.instance_variable_get(:@config).instance_variable_get(:@config)[:steps]
|
195
|
+
|
196
|
+
expect(orig_settings).to be_a(Array)
|
197
|
+
expect(klone_settings).to be_a(Array)
|
198
|
+
expect(klone_settings.object_id).not_to be_eql(orig_settings.object_id)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe "frozen" do
|
203
|
+
subject { TeckelChainTest::Chain.dup }
|
204
|
+
|
205
|
+
it "also freezes the config" do
|
206
|
+
expect { subject.freeze }.to change {
|
207
|
+
[
|
208
|
+
subject.frozen?,
|
209
|
+
subject.instance_variable_get(:@config).frozen?
|
210
|
+
]
|
211
|
+
}.from([false, false]).to([true, true])
|
212
|
+
end
|
213
|
+
|
214
|
+
it "prevents changes to steps" do
|
215
|
+
subject.freeze
|
216
|
+
expect {
|
217
|
+
subject.class_eval do
|
218
|
+
step :yet_other, TeckelChainTest::AddFriend
|
219
|
+
end
|
220
|
+
}.to raise_error(frozen_error)
|
221
|
+
end
|
222
|
+
|
223
|
+
it "prevents changes to config" do
|
224
|
+
subject.freeze
|
225
|
+
expect {
|
226
|
+
subject.class_eval do
|
227
|
+
default_settings!(a: { say: "Chain Default" })
|
228
|
+
end
|
229
|
+
}.to raise_error(frozen_error)
|
230
|
+
end
|
231
|
+
|
232
|
+
describe '#clone' do
|
233
|
+
subject { TeckelChainTest::Chain.dup }
|
234
|
+
|
235
|
+
it 'clones the class' do
|
236
|
+
subject.freeze
|
237
|
+
klone = subject.clone
|
238
|
+
|
239
|
+
expect(klone).to be_frozen
|
240
|
+
expect(klone.object_id).not_to be_eql(subject.object_id)
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'cloned class uses the same, frozen config' do
|
244
|
+
subject.freeze
|
245
|
+
klone = subject.clone
|
246
|
+
|
247
|
+
orig_config = subject.instance_variable_get(:@config)
|
248
|
+
klone_config = klone.instance_variable_get(:@config)
|
249
|
+
|
250
|
+
expect(klone_config).to be_frozen
|
251
|
+
expect(klone_config.object_id).to be_eql(orig_config.object_id)
|
252
|
+
end
|
253
|
+
end
|
254
|
+
end
|
180
255
|
end
|
data/spec/doctest_helper.rb
CHANGED