teckel 0.4.0 → 0.8.0
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 +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