trailblazer-context 0.1.3 → 0.3.2

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.
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
- class Context
3
- VERSION = "0.1.3"
2
+ module Context
3
+ VERSION = "0.3.2"
4
4
  end
5
5
  end
@@ -1,22 +1,5 @@
1
1
  module Trailblazer
2
- # @note This might go to trailblazer-args along with `Context` at some point.
3
- def self.Option(proc)
4
- Option.build(Option, proc)
5
- end
6
-
7
2
  class Option
8
- # Generic builder for a callable "option".
9
- # @param call_implementation [Class, Module] implements the process of calling the proc
10
- # while passing arguments/options to it in a specific style (e.g. kw args, step interface).
11
- # @return [Proc] when called, this proc will evaluate its option (at run-time).
12
- def self.build(call_implementation, proc)
13
- if proc.is_a? Symbol
14
- ->(*args, &block) { call_implementation.evaluate_method(proc, *args, &block) }
15
- else
16
- ->(*args, &block) { call_implementation.evaluate_callable(proc, *args, &block) }
17
- end
18
- end
19
-
20
3
  # A call implementation invoking `proc.(*args)` and plainly forwarding all arguments.
21
4
  # Override this for your own step strategy (see KW#call!).
22
5
  # @private
@@ -27,16 +10,31 @@ module Trailblazer
27
10
  # Note that both #evaluate_callable and #evaluate_method drop most of the args.
28
11
  # If you need those, override this class.
29
12
  # @private
30
- def self.evaluate_callable(proc, *args, **flow_options, &block)
13
+ def self.evaluate_callable(proc, *args, **, &block)
31
14
  call!(proc, *args, &block)
32
15
  end
33
16
 
34
17
  # Make the context's instance method a "lambda" and reuse #call!.
35
18
  # @private
36
- def self.evaluate_method(proc, *args, exec_context: raise("No :exec_context given."), **flow_options, &block)
19
+ def self.evaluate_method(proc, *args, exec_context: raise("No :exec_context given."), **, &block)
37
20
  call!(exec_context.method(proc), *args, &block)
38
21
  end
39
22
 
23
+ # Generic builder for a callable "option".
24
+ # @param call_implementation [Class, Module] implements the process of calling the proc
25
+ # while passing arguments/options to it in a specific style (e.g. kw args, step interface).
26
+ # @return [Proc] when called, this proc will evaluate its option (at run-time).
27
+ def self.build(call_implementation, proc)
28
+ if proc.is_a? Symbol
29
+ ->(*args, &block) { call_implementation.evaluate_method(proc, *args, &block) }
30
+ else
31
+ ->(*args, &block) {
32
+
33
+ puts "@@@@@ #{proc.inspect}"
34
+ call_implementation.evaluate_callable(proc, *args, &block) }
35
+ end
36
+ end
37
+
40
38
  # Returns a {Proc} that, when called, invokes the `proc` argument with keyword arguments.
41
39
  # This is known as "step (call) interface".
42
40
  #
@@ -75,4 +73,8 @@ module Trailblazer
75
73
  end
76
74
  end
77
75
  end
76
+ # @note This might go to trailblazer-args along with `Context` at some point.
77
+ def self.Option(proc)
78
+ Option.build(Option, proc)
79
+ end
78
80
  end
@@ -0,0 +1,32 @@
1
+ require 'test_helper'
2
+ require 'benchmark/ips'
3
+
4
+ BenchmarkRepresenter = Struct.new(:benchmark, :times_slower)
5
+
6
+ Minitest::Spec.class_eval do
7
+ def benchmark_ips(records, time: 1, warmup: 1)
8
+ base = records[:base]
9
+ target = records[:target]
10
+
11
+ benchmark = Benchmark.ips do |x|
12
+ x.config(time: time, warmup: warmup)
13
+
14
+ x.report(base[:label], &base[:block])
15
+ x.report(target[:label], &target[:block])
16
+
17
+ x.compare!
18
+ end
19
+
20
+ times_slower = benchmark.data[0][:ips] / benchmark.data[1][:ips]
21
+ BenchmarkRepresenter.new(benchmark, times_slower)
22
+ end
23
+
24
+ def assert_times_slower(result, threshold)
25
+ base = result.benchmark.data[0]
26
+ target = result.benchmark.data[1]
27
+
28
+ msg = "Expected #{target[:name]} to be slower by at most #{threshold} times than #{base[:name]}, but got #{result.times_slower}"
29
+
30
+ assert result.times_slower < threshold, msg
31
+ end
32
+ end
@@ -0,0 +1,89 @@
1
+ require_relative "benchmark_helper"
2
+
3
+ describe "Context::IndifferentAccess Performance" do
4
+ wrapped_options = { model: Object, policy: Hash, representer: String }
5
+ mutable_options = { write: String, read: Integer, delete: Float, merge: Symbol }
6
+ context_options = {
7
+ container_class: Trailblazer::Context::Container,
8
+ replica_class: Trailblazer::Context::Store::IndifferentAccess,
9
+ }
10
+
11
+ default_hash = Hash(**wrapped_options, **mutable_options)
12
+ indifferent_hash = Trailblazer::Context.build(wrapped_options, mutable_options, context_options)
13
+
14
+ it "initialize" do
15
+ result = benchmark_ips(
16
+ base: { label: :initialize_default_hash, block: ->{
17
+ Hash(**wrapped_options, **mutable_options)
18
+ }},
19
+ target: { label: :initialize_indifferent_hash, block: ->{
20
+ Trailblazer::Context.build(wrapped_options, mutable_options, context_options)
21
+ }},
22
+ )
23
+
24
+ assert_times_slower result, 3
25
+ end
26
+
27
+ it "read" do
28
+ result = benchmark_ips(
29
+ base: { label: :read_from_default_hash, block: ->{ default_hash[:read] } },
30
+ target: { label: :read_from_indifferent_hash, block: ->{ indifferent_hash[:read] } },
31
+ )
32
+
33
+ assert_times_slower result, 1.4
34
+ end
35
+
36
+ it "unknown read" do
37
+ result = benchmark_ips(
38
+ base: { label: :unknown_read_from_default_hash, block: ->{ default_hash[:unknown] } },
39
+ target: { label: :unknown_read_from_indifferent_hash, block: ->{ indifferent_hash[:unknown] } },
40
+ )
41
+
42
+ assert_times_slower result, 3.5
43
+ end
44
+
45
+ it "write" do
46
+ result = benchmark_ips(
47
+ base: { label: :write_to_default_hash, block: ->{ default_hash[:write] = "" } },
48
+ target: { label: :write_to_indifferent_hash, block: ->{ indifferent_hash[:write] = "SKU-1" } },
49
+ )
50
+
51
+ assert_times_slower result, 2.3
52
+ end
53
+
54
+ it "delete" do
55
+ result = benchmark_ips(
56
+ base: { label: :delete_from_default_hash, block: ->{ default_hash.delete(:delete) } },
57
+ target: { label: :delete_from_indifferent_hash, block: ->{ indifferent_hash.delete(:delete) } },
58
+ )
59
+
60
+ assert_times_slower result, 2.4
61
+ end
62
+
63
+ it "merge" do
64
+ result = benchmark_ips(
65
+ base: { label: :merge_from_default_hash, block: ->{ default_hash.merge(merge: :object_id) } },
66
+ target: { label: :merge_from_indifferent_hash, block: ->{ indifferent_hash.merge(merge: :object_id) } },
67
+ )
68
+
69
+ assert_times_slower result, 5.55
70
+ end
71
+
72
+ it "to_hash" do
73
+ result = benchmark_ips(
74
+ base: { label: :default_to_hash, block: ->{ default_hash.to_hash } },
75
+ target: { label: :indifferent_to_hash, block: ->{ indifferent_hash.to_hash } },
76
+ )
77
+
78
+ assert_times_slower result, 1.3
79
+ end
80
+
81
+ it "decompose" do
82
+ result = benchmark_ips(
83
+ base: { label: :dup_default_hash, block: ->{ default_hash.to_hash } },
84
+ target: { label: :decompose, block: ->{ indifferent_hash.decompose } },
85
+ )
86
+
87
+ assert_times_slower result, 1.55
88
+ end
89
+ end
@@ -0,0 +1,73 @@
1
+ require_relative "benchmark_helper"
2
+
3
+ describe "Context::Aliasing Performance" do
4
+ wrapped_options = { model: Object, policy: Hash, representer: String }
5
+ mutable_options = { write: String, read: Integer, delete: Float, merge: Symbol }
6
+
7
+ context_options = {
8
+ container_class: Trailblazer::Context::Container::WithAliases,
9
+ replica_class: Trailblazer::Context::Store::IndifferentAccess,
10
+ aliases: { read: :reader }
11
+ }
12
+
13
+ default_hash = Hash(**wrapped_options, **mutable_options)
14
+ aliased_hash = Trailblazer::Context.build(wrapped_options, mutable_options, context_options)
15
+
16
+ it "initialize" do
17
+ result = benchmark_ips(
18
+ base: { label: :initialize_default_hash, block: ->{
19
+ Hash(**wrapped_options, **mutable_options)
20
+ }},
21
+ target: { label: :initialize_aliased_hash, block: ->{
22
+ Trailblazer::Context.build(wrapped_options, mutable_options, context_options)
23
+ }},
24
+ )
25
+
26
+ assert_times_slower result, 8
27
+ end
28
+
29
+ it "write" do
30
+ result = benchmark_ips(
31
+ base: { label: :write_to_default_hash, block: ->{ default_hash[:write] = "" } },
32
+ target: { label: :write_to_aliased_hash, block: ->{ aliased_hash[:write] = "" } },
33
+ )
34
+
35
+ assert_times_slower result, 3.66
36
+ end
37
+
38
+ it "read" do
39
+ result = benchmark_ips(
40
+ base: { label: :read_from_default_hash, block: ->{ default_hash[:read] } },
41
+ target: { label: :read_from_aliased_hash, block: ->{ aliased_hash[:reader] } },
42
+ )
43
+
44
+ assert_times_slower result, 1.5
45
+ end
46
+
47
+ it "delete" do
48
+ result = benchmark_ips(
49
+ base: { label: :delete_from_default_hash, block: ->{ default_hash.delete(:delete) } },
50
+ target: { label: :delete_from_aliased_hash, block: ->{ aliased_hash.delete(:delete) } },
51
+ )
52
+
53
+ assert_times_slower result, 4
54
+ end
55
+
56
+ it "merge" do
57
+ result = benchmark_ips(
58
+ base: { label: :merge_from_default_hash, block: ->{ default_hash.merge(merge: :object_id) } },
59
+ target: { label: :merge_from_aliased_hash, block: ->{ aliased_hash.merge(merge: :object_id) } },
60
+ )
61
+
62
+ assert_times_slower result, 8.5
63
+ end
64
+
65
+ it "to_hash" do
66
+ result = benchmark_ips(
67
+ base: { label: :default_to_hash, block: ->{ default_hash.to_hash } },
68
+ target: { label: :aliased_to_hash, block: ->{ aliased_hash.to_hash } },
69
+ )
70
+
71
+ assert_times_slower result, 1.5
72
+ end
73
+ end
data/test/context_test.rb CHANGED
@@ -2,8 +2,6 @@ require "test_helper"
2
2
  require "trailblazer/container_chain"
3
3
 
4
4
  class ArgsTest < Minitest::Spec
5
- Context = Trailblazer::Context
6
-
7
5
  let(:immutable) { {repository: "User"} }
8
6
 
9
7
  let(:ctx) { Trailblazer::Context(immutable) }
@@ -16,16 +14,17 @@ class ArgsTest < Minitest::Spec
16
14
  # options[] and options[]=
17
15
  ctx[:model] = Module
18
16
  ctx[:contract] = Integer
19
- ctx[:model] .must_equal Module
20
- ctx[:contract].must_equal Integer
17
+ _(ctx[:model]) .must_equal Module
18
+ _(ctx[:contract]).must_equal Integer
21
19
 
22
20
  # it { }
23
- immutable.inspect.must_equal %({:repository=>\"User\"})
21
+ _(immutable.inspect).must_equal %({:repository=>\"User\"})
22
+ _(ctx.inspect).must_equal %{#<Trailblazer::Context::Container wrapped_options={:repository=>\"User\"} mutable_options={:model=>Module, :contract=>Integer}>}
24
23
  end
25
24
 
26
25
  it "allows false/nil values" do
27
26
  ctx["x"] = false
28
- ctx["x"].must_equal false
27
+ _(ctx["x"]).must_equal false
29
28
 
30
29
  ctx["x"] = nil
31
30
  assert_nil ctx["x"]
@@ -36,7 +35,7 @@ class ArgsTest < Minitest::Spec
36
35
  ctx = Trailblazer::Context(immutable)
37
36
 
38
37
  # it { }
39
- ctx.to_hash.must_equal(repository: "User")
38
+ _(ctx.to_hash).must_equal(repository: "User")
40
39
 
41
40
  # last added has precedence.
42
41
  # only symbol keys.
@@ -44,7 +43,7 @@ class ArgsTest < Minitest::Spec
44
43
  ctx[:a] = Symbol
45
44
  ctx["a"] = String
46
45
 
47
- ctx.to_hash.must_equal(repository: "User", a: String)
46
+ _(ctx.to_hash).must_equal(repository: "User", a: String)
48
47
  end
49
48
 
50
49
  describe "#merge" do
@@ -53,69 +52,271 @@ class ArgsTest < Minitest::Spec
53
52
 
54
53
  merged = ctx.merge(current_user: Module)
55
54
 
56
- merged.to_hash.must_equal(repository: "User", current_user: Module)
57
- ctx.to_hash.must_equal(repository: "User")
55
+ _(merged.class).must_equal(Trailblazer::Context::Container)
56
+ _(merged.to_hash).must_equal(repository: "User", current_user: Module)
57
+ _(ctx.to_hash).must_equal(repository: "User")
58
58
  end
59
59
  end
60
60
 
61
- #-
61
+ describe "Enumerable behaviour" do
62
+ it { _(ctx.each.to_a).must_equal [[:repository, "User"]] }
63
+ it { _(ctx.find{ |k, _| k == :repository }).must_equal [:repository, "User"] }
64
+ it { _(ctx.inject([]){ |r, (k, _)| r << k}).must_equal [:repository] }
65
+ end
66
+
67
+ #- #decompose
62
68
  it do
63
69
  immutable = {repository: "User", model: Module, current_user: Class}
70
+ mutable = {error: RuntimeError}
64
71
 
65
- Trailblazer::Context(immutable) do |_original, mutable|
66
- mutable
67
- end
72
+ _([immutable, mutable]).must_equal Trailblazer::Context(immutable, mutable).decompose
68
73
  end
69
74
  end
70
75
 
71
76
  class ContextWithIndifferentAccessTest < Minitest::Spec
72
77
  it do
73
- flow_options = {}
78
+ flow_options = {
79
+ context_options: {
80
+ container_class: Trailblazer::Context::Container,
81
+ replica_class: Trailblazer::Context::Store::IndifferentAccess
82
+ }
83
+ }
84
+
74
85
  circuit_options = {}
75
86
 
76
- immutable = {model: Object}
87
+ immutable = {model: Object, "policy" => Hash}
77
88
 
78
- ctx = Trailblazer::Context.for(immutable, [immutable, flow_options], circuit_options)
89
+ ctx = Trailblazer::Context.for_circuit(immutable, {}, [immutable, flow_options], **circuit_options)
79
90
 
80
- ctx[:model].must_equal Object
81
- ctx["model"].must_equal Object
91
+ _(ctx[:model]).must_equal Object
92
+ _(ctx["model"]).must_equal Object
93
+ _(ctx[:policy]).must_equal Hash
94
+ _(ctx["policy"]).must_equal Hash
82
95
 
83
96
  ctx["contract.default"] = Module
84
- ctx["contract.default"].must_equal Module
85
- ctx[:"contract.default"].must_equal Module
97
+ _(ctx["contract.default"]).must_equal Module
98
+ _(ctx[:"contract.default"]).must_equal Module
86
99
 
87
100
  # key?
88
- ctx.key?("contract.default").must_equal true
89
- ctx.key?(:"contract.default").must_equal true
101
+ _(ctx.key?("____contract.default")).must_equal false
102
+ _(ctx.key?("contract.default")).must_equal true
103
+ _(ctx.key?(:"contract.default")).must_equal true
90
104
 
91
105
  # context in context
92
- ctx2 = Trailblazer::Context.for(ctx, [ctx, flow_options], circuit_options)
106
+ ctx2 = Trailblazer::Context.for_circuit(ctx, {}, [ctx, flow_options], **circuit_options)
93
107
 
94
- ctx2[:model].must_equal Object
95
- ctx2["model"].must_equal Object
108
+ _(ctx2[:model]).must_equal Object
109
+ _(ctx2["model"]).must_equal Object
96
110
 
97
111
  ctx2["contract.default"] = Class
98
- ctx2["contract.default"].must_equal Class
99
- ctx2[:"contract.default"].must_equal Class
112
+ _(ctx2["contract.default"]).must_equal Class
113
+ _(ctx2[:"contract.default"]).must_equal Class
100
114
 
101
115
  # key?
102
- ctx2.key?("contract.default").must_equal true
103
- ctx2.key?(:"contract.default").must_equal true
104
- ctx2.key?("model").must_equal true
116
+ _(ctx2.key?("contract.default")).must_equal true
117
+ _(ctx2.key?(:"contract.default")).must_equal true
118
+ _(ctx2.key?("model")).must_equal true
105
119
 
106
120
  # wrapped ctx doesn't change
107
- ctx["contract.default"].must_equal Module
108
- ctx[:"contract.default"].must_equal Module
121
+ _(ctx["contract.default"]).must_equal Module
122
+ _(ctx[:"contract.default"]).must_equal Module
123
+
124
+ # delete
125
+ ctx[:model] = Object
126
+ ctx.delete 'model'
109
127
 
128
+ _(ctx.key?(:model)).must_equal false
129
+ _(ctx.key?("model")).must_equal false
110
130
 
111
131
  ctx3 = ctx.merge("result" => false)
112
132
 
113
- ctx3["contract.default"].must_equal Module
114
- ctx3[:"contract.default"].must_equal Module
115
- ctx3["result"].must_equal false
116
- ctx3[:result].must_equal false
117
- ctx3.key?("result").must_equal true
118
- ctx3.key?(:result).must_equal true
133
+ _(ctx3["contract.default"]).must_equal Module
134
+ _(ctx3[:"contract.default"]).must_equal Module
135
+ _(ctx3["result"]).must_equal false
136
+ _(ctx3[:result]).must_equal false
137
+ _(ctx3.key?("result")).must_equal true
138
+ _(ctx3.key?(:result)).must_equal true
139
+ end
140
+
141
+ it "Aliasable" do
142
+ flow_options = {
143
+ context_options: {
144
+ container_class: Trailblazer::Context::Container::WithAliases,
145
+ replica_class: Trailblazer::Context::Store::IndifferentAccess,
146
+ aliases: { "contract.default" => :contract, "result.default"=>:result, "trace.stack" => :stack }
147
+ }
148
+ }
149
+
150
+ circuit_options = {}
151
+
152
+ immutable = {model: Object, "policy" => Hash}
153
+
154
+ ctx = Trailblazer::Context.for_circuit(immutable, {}, [immutable, flow_options], **circuit_options)
155
+ _(ctx.class).must_equal(Trailblazer::Context::Container::WithAliases)
156
+
157
+ _(ctx.inspect).must_equal %{#<Trailblazer::Context::Container::WithAliases wrapped_options={:model=>Object, \"policy\"=>Hash} mutable_options={} aliases={\"contract.default\"=>:contract, \"result.default\"=>:result, \"trace.stack\"=>:stack}>}
158
+
159
+ _(ctx.to_hash).must_equal(:model=>Object, :policy=>Hash)
160
+
161
+ _(ctx[:model]).must_equal Object
162
+ _(ctx["model"]).must_equal Object
163
+ _(ctx[:policy]).must_equal Hash
164
+ _(ctx["policy"]).must_equal Hash
165
+
166
+ ctx["contract.default"] = Module
167
+ _(ctx["contract.default"]).must_equal Module
168
+ _(ctx[:"contract.default"]).must_equal Module
169
+
170
+ # alias
171
+ assert_nil ctx[:result]
172
+ assert_nil ctx["result"]
173
+
174
+ _(ctx[:contract]).must_equal Module
175
+ _(ctx['contract']).must_equal Module
176
+
177
+ assert_nil ctx[:stack]
178
+ assert_nil ctx['stack']
179
+
180
+ # Set an aliased property via setter
181
+ ctx["trace.stack"] = Object
182
+ _(ctx[:stack]).must_equal Object
183
+ _(ctx["stack"]).must_equal Object
184
+ _(ctx["trace.stack"]).must_equal Object
185
+
186
+ # Set an aliased property with merge
187
+ ctx["trace.stack"] = String
188
+ merged = ctx.merge(stack: Integer)
189
+
190
+ _(merged.class).must_equal(Trailblazer::Context::Container::WithAliases)
191
+ _(merged.to_hash).must_equal(:model=>Object, :policy=>Hash, :contract=>Module, :"contract.default"=>Module, :stack=>Integer, :"trace.stack"=>Integer)
192
+
193
+ # key?
194
+ _(ctx.key?("____contract.default")).must_equal false
195
+ _(ctx.key?("contract.default")).must_equal true
196
+ _(ctx.key?(:"contract.default")).must_equal true
197
+ _(ctx.key?(:contract)).must_equal true
198
+ _(ctx.key?(:result)).must_equal false
199
+ _(ctx.key?(:stack)).must_equal true
200
+ _(ctx.key?("trace.stack")).must_equal true
201
+ _(ctx.key?(:"trace.stack")).must_equal true
202
+
203
+ # delete
204
+ ctx[:result] = Object
205
+ ctx.delete :result
206
+
207
+ _(ctx.key?(:result)).must_equal false
208
+ _(ctx.key?("result")).must_equal false
209
+
210
+ _(ctx.key?(:"result.default")).must_equal false
211
+ _(ctx.key?("result.default")).must_equal false
212
+
213
+
214
+ # to_hash
215
+ _(ctx.to_hash).must_equal(:model=>Object, :policy=>Hash, :contract=>Module, :"contract.default"=>Module, :stack=>String, :"trace.stack"=>String)
216
+
217
+ # context in context
218
+ ctx2 = Trailblazer::Context.for_circuit(ctx, {}, [ctx, flow_options], **circuit_options)
219
+
220
+ _(ctx2.key?("____contract.default")).must_equal false
221
+ _(ctx2.key?("contract.default")).must_equal true
222
+ _(ctx2.key?(:"contract.default")).must_equal true
223
+ _(ctx2.key?(:contract)).must_equal true
224
+ _(ctx2.key?(:result)).must_equal false
225
+ _(ctx2.key?("result.default")).must_equal false
226
+ _(ctx2.key?(:stack)).must_equal true
227
+ _(ctx2.key?("trace.stack")).must_equal true
228
+ _(ctx2.key?(:"trace.stack")).must_equal true
229
+
230
+ # Set aliased in new context via setter
231
+ ctx2["result.default"] = Class
232
+
233
+ _(ctx2[:result]).must_equal Class
234
+ _(ctx2[:"result.default"]).must_equal Class
235
+
236
+ _(ctx2.key?("result.default")).must_equal true
237
+ _(ctx2.key?(:"result.default")).must_equal true
238
+ _(ctx2.key?(:result)).must_equal true
239
+
240
+ # todo: TEST flow_options={context_class: SomethingElse}
241
+ end
242
+
243
+ it ".build accepts custom container class" do
244
+ MyContainer = Class.new(Trailblazer::Context::Container) do
245
+ def inspect
246
+ %{#<MyContainer wrapped=#{@wrapped_options} mutable=#{@mutable_options}>}
247
+ end
248
+ end
249
+
250
+ immutable = { model: Object }
251
+ options = { container_class: MyContainer, replica_class: Trailblazer::Context::Store::IndifferentAccess }
252
+
253
+ ctx = Trailblazer::Context.build(immutable, {}, options)
254
+ _(ctx.class).must_equal(MyContainer)
255
+ _(ctx.inspect).must_equal("#<MyContainer wrapped=#{immutable} mutable={}>")
256
+
257
+ _(ctx.to_hash).must_equal({ model: Object })
258
+
259
+ ctx[:integer] = Integer
260
+ _(ctx.to_hash).must_equal({ model: Object, integer: Integer })
261
+
262
+ ctx2 = ctx.merge(float: Float)
263
+ _(ctx2.class).must_equal(MyContainer)
264
+
265
+ _(ctx2.to_hash).must_equal({ model: Object, integer: Integer, float: Float })
266
+ end
267
+
268
+ it ".build accepts custom replica class (For example, To opt out from indifferent access)" do
269
+ MyReplica = Class.new(Hash) do
270
+ def initialize(*containers)
271
+ containers.each do |container|
272
+ container.each{ |key, value| self[key] = value }
273
+ end
274
+ end
275
+ end
276
+
277
+ immutable = { model: Object }
278
+ options = { container_class: Trailblazer::Context::Container, replica_class: MyReplica }
279
+
280
+ ctx = Trailblazer::Context.build(immutable, {}, options)
281
+ ctx[:integer] = Integer
282
+
283
+ _(ctx[:integer]).must_equal(Integer)
284
+ _(ctx['integer']).must_be_nil
285
+ end
286
+
287
+ it "Context() provides default args" do
288
+ immutable = {model: Object, "policy.default" => Hash}
289
+ options = {
290
+ container_class: Trailblazer::Context::Container::WithAliases,
291
+ aliases: { "policy.default" => :policy }
292
+ }
293
+
294
+ ctx = Trailblazer::Context(immutable, {}, options)
295
+
296
+ _(ctx[:model]).must_equal Object
297
+ _(ctx["model"]).must_equal Object
298
+ _(ctx[:policy]).must_equal Hash
299
+
300
+ ctx2 = ctx.merge(result: :success)
301
+
302
+
303
+ _(ctx2[:model]).must_equal Object
304
+ _(ctx2["model"]).must_equal Object
305
+ _(ctx2[:policy]).must_equal Hash
306
+ _(ctx2[:result]).must_equal :success
307
+ end
308
+
309
+ it "Context() throws RuntimeError if aliases are passed but container_class doesn't support it" do
310
+ immutable = {model: Object, "policy.default" => Hash}
311
+ options = {
312
+ aliases: { "policy.default" => :policy }
313
+ }
314
+
315
+ exception = assert_raises Trailblazer::Context::Container::UseWithAliases do
316
+ Trailblazer::Context(immutable, {}, options)
317
+ end
318
+
319
+ _(exception.message).must_equal %{Pass `Trailblazer::Context::Container::WithAliases` as `container_class` while defining `aliases`}
119
320
  end
120
321
  end
121
322