trailblazer-context 0.1.1 → 0.3.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 +5 -5
- data/.gitignore +1 -0
- data/.travis.yml +12 -0
- data/CHANGES.md +26 -0
- data/Gemfile +2 -2
- data/LICENSE +1 -1
- data/Rakefile +2 -2
- data/lib/trailblazer-context.rb +1 -1
- data/lib/trailblazer/container_chain.rb +5 -3
- data/lib/trailblazer/context.rb +38 -13
- data/lib/trailblazer/context/aliasing.rb +38 -0
- data/lib/trailblazer/context/indifferent_access.rb +38 -0
- data/lib/trailblazer/context/version.rb +1 -1
- data/lib/trailblazer/option.rb +18 -19
- data/test/context_test.rb +211 -0
- data/test/option_test.rb +186 -0
- data/test/test_helper.rb +3 -0
- data/trailblazer-context.gemspec +12 -11
- metadata +29 -21
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 0f6c3c7ff9c8b061c5ac0d5aca954f03cd1b5ff0bf286aa787ede12f1e187890
|
4
|
+
data.tar.gz: 0fdc45ffab9316c7799f2ce59f54c6409305cf91fa01349d30d026eac998c188
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2c9f349028afca126e1961e50ebb0ff661a5fe5a0e2573108c9c79f762694cdb03c6914beca4eec4c5b3b25aecae03d4706c47c84854d0ebb57b6e8a42ec279f
|
7
|
+
data.tar.gz: 1fba9db52f9e3680cccb3b89624c8a159bbd144fbd0b86b3a71972ea1feff96978b82867c976a568aec709b09d0fe859d173a1a813fa5747cb42d179bf6c7520
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
data/CHANGES.md
CHANGED
@@ -1,3 +1,29 @@
|
|
1
|
+
# 0.3.0
|
2
|
+
* Add support for ruby 2.7
|
3
|
+
* Drop support for ruby 2.0
|
4
|
+
|
5
|
+
# 0.2.0
|
6
|
+
|
7
|
+
* Added `Context::IndifferentAccess`.
|
8
|
+
* Added `Context::Aliasing`.
|
9
|
+
* `Context.for_circuit` is not the authorative builder for creating a context.
|
10
|
+
|
11
|
+
# 0.1.5
|
12
|
+
|
13
|
+
* `Context.build` allows quickly building a Context without requiring the circuit interface.
|
14
|
+
|
15
|
+
# 0.1.4
|
16
|
+
|
17
|
+
* Fix the `IndifferentAccess` name lookup. Since we can't convert all keys to symbols internally (not every options structure has `collect`) we need to have a lookup chain.
|
18
|
+
|
19
|
+
# 0.1.3
|
20
|
+
|
21
|
+
* Introduce `Context::IndifferentAccess` which converts all keys to symbol. This, in turn, allows to use both string and symbol keys everywhere. Currently, the implementation is set via the global method `Context.implementation` and defaults to the new `IndifferentAccess`.
|
22
|
+
|
23
|
+
# 0.1.2
|
24
|
+
|
25
|
+
* More meaningful error message: "No :exec_context given.".
|
26
|
+
|
1
27
|
# 0.1.1
|
2
28
|
|
3
29
|
* `Option.( *args, &block )` now passes through the block to the option call.
|
data/Gemfile
CHANGED
data/LICENSE
CHANGED
data/Rakefile
CHANGED
data/lib/trailblazer-context.rb
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# @private
|
2
|
-
|
2
|
+
# used to be called Resolver.
|
3
|
+
class Trailblazer::Context::ContainerChain
|
3
4
|
# Keeps a list of containers. When looking up a key/value, containers are traversed in
|
4
5
|
# the order they were added until key is found.
|
5
6
|
#
|
@@ -28,11 +29,12 @@ class Trailblazer::Context::ContainerChain # used to be called Resolver.
|
|
28
29
|
|
29
30
|
def keys
|
30
31
|
@containers.collect(&:keys).flatten
|
31
|
-
end
|
32
|
+
end
|
32
33
|
|
33
34
|
# @private
|
34
35
|
def to_hash
|
35
|
-
|
36
|
+
# FIXME: introduce pattern matching so we can have different "transformers" for each container type.
|
37
|
+
return @to_hash.(@containers) if @to_hash
|
36
38
|
@containers.each_with_object({}) { |container, hash| hash.merge!(container.to_hash) }
|
37
39
|
end
|
38
40
|
end
|
data/lib/trailblazer/context.rb
CHANGED
@@ -1,3 +1,4 @@
|
|
1
|
+
require "trailblazer/option"
|
1
2
|
# TODO: mark/make all but mutable_options as frozen.
|
2
3
|
# The idea of Skill is to have a generic, ordered read/write interface that
|
3
4
|
# collects mutable runtime-computed data while providing access to compile-time
|
@@ -11,10 +12,36 @@ module Trailblazer
|
|
11
12
|
# activity (aka wrapped_options).
|
12
13
|
|
13
14
|
# only public creator: Build
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
15
|
+
# :data object:
|
16
|
+
class Context
|
17
|
+
# NOTE: In the future, we might look up the Context to use in the ctx.
|
18
|
+
# The demanding signature is for forward-compat.
|
19
|
+
# @private
|
20
|
+
def self.for(wrapped_options, (ctx, flow_options), **circuit_options) # TODO: remove
|
21
|
+
implementation.build(wrapped_options, {}, [ctx, flow_options], circuit_options)
|
22
|
+
end
|
23
|
+
|
24
|
+
def self.for_circuit(wrapped_options, mutable_options, (ctx, flow_options), **circuit_options)
|
25
|
+
context_class = flow_options[:context_class] || implementation # Context::IndifferentAccess
|
26
|
+
|
27
|
+
context_class.build(wrapped_options, mutable_options, [ctx, flow_options], circuit_options)
|
28
|
+
end
|
29
|
+
|
30
|
+
# @public
|
31
|
+
def self.build(wrapped_options, *)
|
32
|
+
new(wrapped_options)
|
33
|
+
end
|
34
|
+
|
35
|
+
# I hate globals, but currently this is the only easy way for setting the implementation.
|
36
|
+
def self.implementation
|
37
|
+
IndifferentAccess
|
38
|
+
end
|
39
|
+
|
40
|
+
def initialize(wrapped_options, mutable_options, *)
|
41
|
+
@wrapped_options = wrapped_options
|
42
|
+
@mutable_options = mutable_options
|
43
|
+
# TODO: wrapped_options should be optimized for lookups here since
|
44
|
+
# it could also be a Context instance, but should be a ContainerChain.
|
18
45
|
end
|
19
46
|
|
20
47
|
def [](name)
|
@@ -39,26 +66,22 @@ module Trailblazer
|
|
39
66
|
end
|
40
67
|
|
41
68
|
# @private
|
42
|
-
#
|
43
|
-
# This method might be removed.
|
44
69
|
def merge(hash)
|
45
70
|
original, mutable_options = decompose
|
46
71
|
|
47
|
-
|
72
|
+
self.class.new(original, mutable_options.merge(hash))
|
48
73
|
end
|
49
74
|
|
50
75
|
# Return the Context's two components. Used when computing the new output for
|
51
76
|
# the next activity.
|
52
77
|
def decompose
|
53
|
-
[
|
78
|
+
[@wrapped_options, @mutable_options]
|
54
79
|
end
|
55
80
|
|
56
81
|
def keys
|
57
82
|
@mutable_options.keys + @wrapped_options.keys # FIXME.
|
58
83
|
end
|
59
84
|
|
60
|
-
|
61
|
-
|
62
85
|
# TODO: maybe we shouldn't allow to_hash from context?
|
63
86
|
# TODO: massive performance bottleneck. also, we could already "know" here what keys the
|
64
87
|
# transformation wants.
|
@@ -66,14 +89,16 @@ module Trailblazer
|
|
66
89
|
def to_hash
|
67
90
|
{}.tap do |hash|
|
68
91
|
# the "key" here is to call to_hash on all containers.
|
69
|
-
[
|
92
|
+
[@wrapped_options.to_hash, @mutable_options.to_hash].each do |options|
|
70
93
|
options.each { |k, v| hash[k.to_sym] = v }
|
71
94
|
end
|
72
95
|
end
|
73
96
|
end
|
74
97
|
end
|
75
98
|
|
76
|
-
def self.Context(wrapped_options, mutable_options={})
|
99
|
+
def self.Context(wrapped_options, mutable_options = {})
|
77
100
|
Context.new(wrapped_options, mutable_options)
|
78
101
|
end
|
79
|
-
end
|
102
|
+
end
|
103
|
+
|
104
|
+
require "trailblazer/context/indifferent_access"
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Trailblazer
|
2
|
+
class Context
|
3
|
+
module Aliasing
|
4
|
+
def initialize(wrapped_options, mutable_options, context_alias: {}, **)
|
5
|
+
super(wrapped_options, mutable_options)
|
6
|
+
|
7
|
+
@aliases = context_alias.invert
|
8
|
+
end
|
9
|
+
|
10
|
+
def [](key)
|
11
|
+
return super unless (aka = @aliases[key]) # yepp, nil/false won't work
|
12
|
+
|
13
|
+
super(aka)
|
14
|
+
end
|
15
|
+
|
16
|
+
def key?(key)
|
17
|
+
return super unless (aka = @aliases[key]) # yepp, nil/false won't work
|
18
|
+
|
19
|
+
super(aka)
|
20
|
+
end
|
21
|
+
|
22
|
+
# @private ?
|
23
|
+
def merge(hash)
|
24
|
+
original, mutable_options = decompose
|
25
|
+
|
26
|
+
self.class.new(
|
27
|
+
original,
|
28
|
+
mutable_options.merge(hash),
|
29
|
+
context_alias: @aliases.invert # DISCUSS: maybe we can speed up by remembering the original options?
|
30
|
+
)
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_hash
|
34
|
+
super.merge(Hash[@aliases.collect { |aka, k| key?(k) ? [aka, self[k]] : nil }.compact]) # FIXME: performance!
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
require "trailblazer/context/aliasing"
|
2
|
+
|
3
|
+
module Trailblazer
|
4
|
+
class Context
|
5
|
+
class IndifferentAccess < Context
|
6
|
+
module InstanceMethods
|
7
|
+
def [](name)
|
8
|
+
# TODO: well...
|
9
|
+
@mutable_options.key?(name.to_sym) and return @mutable_options[name.to_sym]
|
10
|
+
@mutable_options.key?(name.to_s) and return @mutable_options[name.to_s]
|
11
|
+
@wrapped_options.key?(name.to_sym) and return @wrapped_options[name.to_sym]
|
12
|
+
@wrapped_options[name.to_s]
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.build(wrapped_options, mutable_options, (_ctx, flow_options), **)
|
16
|
+
new(wrapped_options, mutable_options, flow_options)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
include InstanceMethods
|
20
|
+
|
21
|
+
def key?(name)
|
22
|
+
super(name.to_sym) || super(name.to_s)
|
23
|
+
end
|
24
|
+
|
25
|
+
include Aliasing # FIXME
|
26
|
+
|
27
|
+
|
28
|
+
class << self
|
29
|
+
# This also builds IndifferentAccess::Aliasing.
|
30
|
+
# The {#build} method is designed to take all args from {for_circuit} and then
|
31
|
+
# translate that to the constructor.
|
32
|
+
def build(wrapped_options, mutable_options, (_ctx, flow_options), **)
|
33
|
+
new(wrapped_options, mutable_options, **flow_options)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/trailblazer/option.rb
CHANGED
@@ -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,28 @@ 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,
|
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
|
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) { call_implementation.evaluate_callable(proc, *args, &block) }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
40
35
|
# Returns a {Proc} that, when called, invokes the `proc` argument with keyword arguments.
|
41
36
|
# This is known as "step (call) interface".
|
42
37
|
#
|
@@ -75,4 +70,8 @@ module Trailblazer
|
|
75
70
|
end
|
76
71
|
end
|
77
72
|
end
|
73
|
+
# @note This might go to trailblazer-args along with `Context` at some point.
|
74
|
+
def self.Option(proc)
|
75
|
+
Option.build(Option, proc)
|
76
|
+
end
|
78
77
|
end
|
@@ -0,0 +1,211 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
require "trailblazer/container_chain"
|
3
|
+
|
4
|
+
class ArgsTest < Minitest::Spec
|
5
|
+
let(:immutable) { {repository: "User"} }
|
6
|
+
|
7
|
+
let(:ctx) { Trailblazer::Context(immutable) }
|
8
|
+
|
9
|
+
it do
|
10
|
+
ctx = Trailblazer::Context(immutable)
|
11
|
+
|
12
|
+
# it { }
|
13
|
+
#-
|
14
|
+
# options[] and options[]=
|
15
|
+
ctx[:model] = Module
|
16
|
+
ctx[:contract] = Integer
|
17
|
+
_(ctx[:model]) .must_equal Module
|
18
|
+
_(ctx[:contract]).must_equal Integer
|
19
|
+
|
20
|
+
# it { }
|
21
|
+
_(immutable.inspect).must_equal %({:repository=>\"User\"})
|
22
|
+
end
|
23
|
+
|
24
|
+
it "allows false/nil values" do
|
25
|
+
ctx["x"] = false
|
26
|
+
_(ctx["x"]).must_equal false
|
27
|
+
|
28
|
+
ctx["x"] = nil
|
29
|
+
assert_nil ctx["x"]
|
30
|
+
end
|
31
|
+
|
32
|
+
#- #to_hash
|
33
|
+
it do
|
34
|
+
ctx = Trailblazer::Context(immutable)
|
35
|
+
|
36
|
+
# it { }
|
37
|
+
_(ctx.to_hash).must_equal(repository: "User")
|
38
|
+
|
39
|
+
# last added has precedence.
|
40
|
+
# only symbol keys.
|
41
|
+
# it { }
|
42
|
+
ctx[:a] = Symbol
|
43
|
+
ctx["a"] = String
|
44
|
+
|
45
|
+
_(ctx.to_hash).must_equal(repository: "User", a: String)
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#merge" do
|
49
|
+
it do
|
50
|
+
ctx = Trailblazer::Context(immutable)
|
51
|
+
|
52
|
+
merged = ctx.merge(current_user: Module)
|
53
|
+
|
54
|
+
_(merged.to_hash).must_equal(repository: "User", current_user: Module)
|
55
|
+
_(ctx.to_hash).must_equal(repository: "User")
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
#-
|
60
|
+
it do
|
61
|
+
immutable = {repository: "User", model: Module, current_user: Class}
|
62
|
+
|
63
|
+
Trailblazer::Context(immutable) do |_original, mutable|
|
64
|
+
mutable
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
class ContextWithIndifferentAccessTest < Minitest::Spec
|
70
|
+
it do
|
71
|
+
flow_options = {}
|
72
|
+
circuit_options = {}
|
73
|
+
|
74
|
+
immutable = {model: Object, "policy" => Hash}
|
75
|
+
|
76
|
+
ctx = Trailblazer::Context.for_circuit(immutable, {}, [immutable, flow_options], **circuit_options)
|
77
|
+
|
78
|
+
_(ctx[:model]).must_equal Object
|
79
|
+
_(ctx["model"]).must_equal Object
|
80
|
+
_(ctx[:policy]).must_equal Hash
|
81
|
+
_(ctx["policy"]).must_equal Hash
|
82
|
+
|
83
|
+
ctx["contract.default"] = Module
|
84
|
+
_(ctx["contract.default"]).must_equal Module
|
85
|
+
_(ctx[:"contract.default"]).must_equal Module
|
86
|
+
|
87
|
+
# key?
|
88
|
+
_(ctx.key?("____contract.default")).must_equal false
|
89
|
+
_(ctx.key?("contract.default")).must_equal true
|
90
|
+
_(ctx.key?(:"contract.default")).must_equal true
|
91
|
+
|
92
|
+
# context in context
|
93
|
+
ctx2 = Trailblazer::Context.for_circuit(ctx, {}, [ctx, flow_options], **circuit_options)
|
94
|
+
|
95
|
+
_(ctx2[:model]).must_equal Object
|
96
|
+
_(ctx2["model"]).must_equal Object
|
97
|
+
|
98
|
+
ctx2["contract.default"] = Class
|
99
|
+
_(ctx2["contract.default"]).must_equal Class
|
100
|
+
_(ctx2[:"contract.default"]).must_equal Class
|
101
|
+
|
102
|
+
# key?
|
103
|
+
_(ctx2.key?("contract.default")).must_equal true
|
104
|
+
_(ctx2.key?(:"contract.default")).must_equal true
|
105
|
+
_(ctx2.key?("model")).must_equal true
|
106
|
+
|
107
|
+
# wrapped ctx doesn't change
|
108
|
+
_(ctx["contract.default"]).must_equal Module
|
109
|
+
_(ctx[:"contract.default"]).must_equal Module
|
110
|
+
|
111
|
+
|
112
|
+
ctx3 = ctx.merge("result" => false)
|
113
|
+
|
114
|
+
_(ctx3["contract.default"]).must_equal Module
|
115
|
+
_(ctx3[:"contract.default"]).must_equal Module
|
116
|
+
_(ctx3["result"]).must_equal false
|
117
|
+
_(ctx3[:result]).must_equal false
|
118
|
+
_(ctx3.key?("result")).must_equal true
|
119
|
+
_(ctx3.key?(:result)).must_equal true
|
120
|
+
end
|
121
|
+
|
122
|
+
it "Aliasable" do
|
123
|
+
flow_options = {context_alias: {"contract.default" => :contract, "result.default"=>:result, "trace.stack" => :stack}}
|
124
|
+
circuit_options = {}
|
125
|
+
|
126
|
+
immutable = {model: Object, "policy" => Hash}
|
127
|
+
|
128
|
+
ctx = Trailblazer::Context.for_circuit(immutable, {}, [immutable, flow_options], **circuit_options)
|
129
|
+
|
130
|
+
_(ctx[:model]).must_equal Object
|
131
|
+
_(ctx["model"]).must_equal Object
|
132
|
+
_(ctx[:policy]).must_equal Hash
|
133
|
+
_(ctx["policy"]).must_equal Hash
|
134
|
+
|
135
|
+
ctx["contract.default"] = Module
|
136
|
+
_(ctx["contract.default"]).must_equal Module
|
137
|
+
_(ctx[:"contract.default"]).must_equal Module
|
138
|
+
|
139
|
+
# alias
|
140
|
+
assert_nil ctx[:result]
|
141
|
+
assert_nil ctx["result"]
|
142
|
+
|
143
|
+
_(ctx[:contract]).must_equal Module
|
144
|
+
|
145
|
+
assert_nil ctx[:stack]
|
146
|
+
|
147
|
+
# Set an aliased property via setter
|
148
|
+
ctx["trace.stack"] = Object
|
149
|
+
_(ctx[:stack]).must_equal Object
|
150
|
+
_(ctx["trace.stack"]).must_equal Object
|
151
|
+
|
152
|
+
# key?
|
153
|
+
_(ctx.key?("____contract.default")).must_equal false
|
154
|
+
_(ctx.key?("contract.default")).must_equal true
|
155
|
+
_(ctx.key?(:"contract.default")).must_equal true
|
156
|
+
_(ctx.key?(:contract)).must_equal true
|
157
|
+
_(ctx.key?(:result)).must_equal false
|
158
|
+
_(ctx.key?(:stack)).must_equal true
|
159
|
+
_(ctx.key?("trace.stack")).must_equal true
|
160
|
+
_(ctx.key?(:"trace.stack")).must_equal true
|
161
|
+
|
162
|
+
# to_hash
|
163
|
+
_(ctx.to_hash).must_equal(:model=>Object, :policy=>Hash, :"contract.default"=>Module, :"trace.stack"=>Object, :contract=>Module, :stack=>Object)
|
164
|
+
|
165
|
+
# context in context
|
166
|
+
ctx2 = Trailblazer::Context.for_circuit(ctx, {}, [ctx, flow_options], **circuit_options)
|
167
|
+
|
168
|
+
_(ctx2.key?("____contract.default")).must_equal false
|
169
|
+
_(ctx2.key?("contract.default")).must_equal true
|
170
|
+
_(ctx2.key?(:"contract.default")).must_equal true
|
171
|
+
_(ctx2.key?(:contract)).must_equal true
|
172
|
+
_(ctx2.key?(:result)).must_equal false
|
173
|
+
_(ctx2.key?("result.default")).must_equal false
|
174
|
+
_(ctx2.key?(:stack)).must_equal true
|
175
|
+
_(ctx2.key?("trace.stack")).must_equal true
|
176
|
+
_(ctx2.key?(:"trace.stack")).must_equal true
|
177
|
+
|
178
|
+
# Set aliased in new context via setter
|
179
|
+
ctx2["result.default"] = Class
|
180
|
+
|
181
|
+
_(ctx2[:result]).must_equal Class
|
182
|
+
_(ctx2[:"result.default"]).must_equal Class
|
183
|
+
|
184
|
+
_(ctx2.key?("result.default")).must_equal true
|
185
|
+
_(ctx2.key?(:"result.default")).must_equal true
|
186
|
+
_(ctx2.key?(:result)).must_equal true
|
187
|
+
|
188
|
+
# todo: TEST flow_options={context_class: SomethingElse}
|
189
|
+
end
|
190
|
+
|
191
|
+
it ".build provides default args" do
|
192
|
+
immutable = {model: Object, "policy.default" => Hash}
|
193
|
+
|
194
|
+
# {Aliasing#initialize}
|
195
|
+
ctx = Trailblazer::Context::IndifferentAccess.new(immutable, {}, context_alias: {"policy.default" => :policy})
|
196
|
+
|
197
|
+
_(ctx[:model]).must_equal Object
|
198
|
+
_(ctx["model"]).must_equal Object
|
199
|
+
_(ctx[:policy]).must_equal Hash
|
200
|
+
|
201
|
+
ctx2 = ctx.merge(result: :success)
|
202
|
+
|
203
|
+
|
204
|
+
_(ctx2[:model]).must_equal Object
|
205
|
+
_(ctx2["model"]).must_equal Object
|
206
|
+
_(ctx2[:policy]).must_equal Hash
|
207
|
+
_(ctx2[:result]).must_equal :success
|
208
|
+
end
|
209
|
+
end
|
210
|
+
|
211
|
+
# TODO: test overriding Context.implementation.
|
data/test/option_test.rb
ADDED
@@ -0,0 +1,186 @@
|
|
1
|
+
require "test_helper"
|
2
|
+
|
3
|
+
class OptionTest < Minitest::Spec
|
4
|
+
def assert_result(result, block = nil)
|
5
|
+
_(result).must_equal([{a: 1}, 2, {b: 3}, block])
|
6
|
+
|
7
|
+
_(positional.inspect).must_equal %({:a=>1})
|
8
|
+
_(keywords.inspect).must_equal %({:a=>2, :b=>3})
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "positional and kws" do
|
12
|
+
class Step
|
13
|
+
def with_positional_and_keywords(options, a: nil, **more_options, &block)
|
14
|
+
[options, a, more_options, block]
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
WITH_POSITIONAL_AND_KEYWORDS = ->(options, a: nil, **more_options, &block) do
|
19
|
+
[options, a, more_options, block]
|
20
|
+
end
|
21
|
+
|
22
|
+
class WithPositionalAndKeywords
|
23
|
+
def self.call(options, a: nil, **more_options, &block)
|
24
|
+
[options, a, more_options, block]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
let(:positional) { {a: 1} }
|
29
|
+
let(:keywords) { {a: 2, b: 3} }
|
30
|
+
|
31
|
+
let(:block) { ->(*) { snippet } }
|
32
|
+
|
33
|
+
describe ":method" do
|
34
|
+
let(:option) { Trailblazer::Option(:with_positional_and_keywords) }
|
35
|
+
|
36
|
+
it "passes through all args" do
|
37
|
+
step = Step.new
|
38
|
+
|
39
|
+
# positional = { a: 1 }
|
40
|
+
# keywords = { a: 2, b: 3 }
|
41
|
+
assert_result option.(positional, keywords, exec_context: step)
|
42
|
+
end
|
43
|
+
|
44
|
+
it "allows passing a block, too" do
|
45
|
+
step = Step.new
|
46
|
+
|
47
|
+
assert_result option.(positional, keywords, {exec_context: step}, &block), block
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
describe "lambda" do
|
52
|
+
let(:option) { Trailblazer::Option(WITH_POSITIONAL_AND_KEYWORDS) }
|
53
|
+
|
54
|
+
it "-> {} lambda" do
|
55
|
+
assert_result option.(positional, keywords, {})
|
56
|
+
end
|
57
|
+
|
58
|
+
it "allows passing a block, too" do
|
59
|
+
assert_result option.(positional, keywords, {}, &block), block
|
60
|
+
end
|
61
|
+
|
62
|
+
it "doesn't mind :exec_context" do
|
63
|
+
assert_result option.(positional, keywords, exec_context: "bogus")
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe "Callable" do
|
68
|
+
let(:option) { Trailblazer::Option(WithPositionalAndKeywords) }
|
69
|
+
|
70
|
+
it "passes through all args" do
|
71
|
+
assert_result option.(positional, keywords, exec_context: nil)
|
72
|
+
end
|
73
|
+
|
74
|
+
it "allows passing a block, too" do
|
75
|
+
assert_result option.(positional, keywords, {exec_context: nil}, &block), block
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
describe "positionals" do
|
81
|
+
def assert_result_pos(result)
|
82
|
+
_(result).must_equal([1, 2, [3, 4]])
|
83
|
+
_(positionals).must_equal [1, 2, 3, 4]
|
84
|
+
end
|
85
|
+
|
86
|
+
class Step
|
87
|
+
def with_positionals(a, b, *args)
|
88
|
+
[a, b, args]
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
WITH_POSITIONALS = ->(a, b, *args) do
|
93
|
+
[a, b, args]
|
94
|
+
end
|
95
|
+
|
96
|
+
class WithPositionals
|
97
|
+
def self.call(a, b, *args)
|
98
|
+
[a, b, args]
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
let(:positionals) { [1, 2, 3, 4] }
|
103
|
+
|
104
|
+
it ":method" do
|
105
|
+
step = Step.new
|
106
|
+
|
107
|
+
option = Trailblazer::Option(:with_positionals)
|
108
|
+
|
109
|
+
assert_result_pos option.(*positionals, exec_context: step)
|
110
|
+
end
|
111
|
+
|
112
|
+
it "-> {} lambda" do
|
113
|
+
option = Trailblazer::Option(WITH_POSITIONALS)
|
114
|
+
|
115
|
+
assert_result_pos option.(*positionals, exec_context: "something")
|
116
|
+
end
|
117
|
+
|
118
|
+
it "callable" do
|
119
|
+
option = Trailblazer::Option(WithPositionals)
|
120
|
+
|
121
|
+
assert_result_pos option.(*positionals, exec_context: "something")
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
describe "Option::KW" do
|
126
|
+
def assert_result_kws(result)
|
127
|
+
_(result).must_equal([{a: 1, b: 2, c: 3}, 1, 2, {c: 3}])
|
128
|
+
end
|
129
|
+
|
130
|
+
class Step
|
131
|
+
def with_kws(options, a: nil, b: nil, **rest)
|
132
|
+
[options, a, b, rest]
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
module Task
|
137
|
+
def self.with_kws(options, a: nil, b: nil, **rest)
|
138
|
+
[options, a, b, rest]
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
WITH_KWS = ->(options, a: nil, b: nil, **rest) do
|
143
|
+
[options, a, b, rest]
|
144
|
+
end
|
145
|
+
|
146
|
+
class WithKWs
|
147
|
+
def self.call(options, a: nil, b: nil, **rest)
|
148
|
+
[options, a, b, rest]
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
let(:options) { {a: 1, b: 2, c: 3} }
|
153
|
+
|
154
|
+
it ":method" do
|
155
|
+
step = Step.new
|
156
|
+
|
157
|
+
option = Trailblazer::Option::KW(:with_kws)
|
158
|
+
|
159
|
+
assert_result_kws option.(options, exec_context: step)
|
160
|
+
end
|
161
|
+
|
162
|
+
it "Method instance" do
|
163
|
+
option = Trailblazer::Option::KW(Task.method(:with_kws))
|
164
|
+
|
165
|
+
assert_result_kws option.(options, {})
|
166
|
+
end
|
167
|
+
|
168
|
+
it "-> {} lambda" do
|
169
|
+
option = Trailblazer::Option::KW(WITH_KWS)
|
170
|
+
|
171
|
+
assert_result_kws option.(options, {})
|
172
|
+
end
|
173
|
+
|
174
|
+
it "lambda ignores :exec_context" do
|
175
|
+
option = Trailblazer::Option::KW(WITH_KWS)
|
176
|
+
|
177
|
+
assert_result_kws option.(options, exec_context: "something")
|
178
|
+
end
|
179
|
+
|
180
|
+
it "callable" do
|
181
|
+
option = Trailblazer::Option::KW(WithKWs)
|
182
|
+
|
183
|
+
assert_result_kws option.(options, {})
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
data/test/test_helper.rb
ADDED
data/trailblazer-context.gemspec
CHANGED
@@ -1,6 +1,6 @@
|
|
1
|
-
lib = File.expand_path(
|
1
|
+
lib = File.expand_path("lib", __dir__)
|
2
2
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
3
|
-
require
|
3
|
+
require "trailblazer/context/version"
|
4
4
|
|
5
5
|
Gem::Specification.new do |spec|
|
6
6
|
spec.name = "trailblazer-context"
|
@@ -8,21 +8,22 @@ Gem::Specification.new do |spec|
|
|
8
8
|
spec.authors = ["Nick Sutterer"]
|
9
9
|
spec.email = ["apotonick@gmail.com"]
|
10
10
|
|
11
|
-
spec.summary =
|
12
|
-
spec.description =
|
11
|
+
spec.summary = "Argument-specific data structures for Trailblazer."
|
12
|
+
spec.description = "Argument-specific data structures for Trailblazer such as Context, Option and ContainerChain."
|
13
13
|
spec.homepage = "http://trailblazer.to/gems/workflow"
|
14
14
|
spec.licenses = ["MIT"]
|
15
15
|
|
16
16
|
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
17
|
-
f.match(%r
|
17
|
+
f.match(%r(^test/))
|
18
18
|
end
|
19
|
-
spec.
|
20
|
-
|
19
|
+
spec.test_files = `git ls-files -z test`.split("\x0")
|
20
|
+
|
21
21
|
spec.require_paths = ["lib"]
|
22
22
|
|
23
|
-
spec.add_development_dependency "bundler"
|
24
|
-
spec.add_development_dependency "
|
25
|
-
spec.add_development_dependency "
|
23
|
+
spec.add_development_dependency "bundler"
|
24
|
+
spec.add_development_dependency "minitest"
|
25
|
+
spec.add_development_dependency "rake"
|
26
26
|
|
27
|
-
|
27
|
+
# maybe we could remove this?
|
28
|
+
spec.required_ruby_version = ">= 2.1.0"
|
28
29
|
end
|
metadata
CHANGED
@@ -1,57 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trailblazer-context
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Nick Sutterer
|
8
8
|
autorequire:
|
9
|
-
bindir:
|
9
|
+
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-29 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- - "
|
17
|
+
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
|
-
version: '
|
19
|
+
version: '0'
|
20
20
|
type: :development
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- - "
|
24
|
+
- - ">="
|
25
25
|
- !ruby/object:Gem::Version
|
26
|
-
version: '
|
26
|
+
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: minitest
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
33
|
+
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: rake
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
|
-
- - "
|
45
|
+
- - ">="
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '0'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- - "
|
52
|
+
- - ">="
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '0'
|
55
55
|
description: Argument-specific data structures for Trailblazer such as Context, Option
|
56
56
|
and ContainerChain.
|
57
57
|
email:
|
@@ -61,6 +61,7 @@ extensions: []
|
|
61
61
|
extra_rdoc_files: []
|
62
62
|
files:
|
63
63
|
- ".gitignore"
|
64
|
+
- ".travis.yml"
|
64
65
|
- CHANGES.md
|
65
66
|
- Gemfile
|
66
67
|
- LICENSE
|
@@ -69,8 +70,13 @@ files:
|
|
69
70
|
- lib/trailblazer-context.rb
|
70
71
|
- lib/trailblazer/container_chain.rb
|
71
72
|
- lib/trailblazer/context.rb
|
73
|
+
- lib/trailblazer/context/aliasing.rb
|
74
|
+
- lib/trailblazer/context/indifferent_access.rb
|
72
75
|
- lib/trailblazer/context/version.rb
|
73
76
|
- lib/trailblazer/option.rb
|
77
|
+
- test/context_test.rb
|
78
|
+
- test/option_test.rb
|
79
|
+
- test/test_helper.rb
|
74
80
|
- trailblazer-context.gemspec
|
75
81
|
homepage: http://trailblazer.to/gems/workflow
|
76
82
|
licenses:
|
@@ -84,16 +90,18 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
84
90
|
requirements:
|
85
91
|
- - ">="
|
86
92
|
- !ruby/object:Gem::Version
|
87
|
-
version: 2.
|
93
|
+
version: 2.1.0
|
88
94
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
95
|
requirements:
|
90
96
|
- - ">="
|
91
97
|
- !ruby/object:Gem::Version
|
92
98
|
version: '0'
|
93
99
|
requirements: []
|
94
|
-
|
95
|
-
rubygems_version: 2.6.8
|
100
|
+
rubygems_version: 3.0.3
|
96
101
|
signing_key:
|
97
102
|
specification_version: 4
|
98
103
|
summary: Argument-specific data structures for Trailblazer.
|
99
|
-
test_files:
|
104
|
+
test_files:
|
105
|
+
- test/context_test.rb
|
106
|
+
- test/option_test.rb
|
107
|
+
- test/test_helper.rb
|