trailblazer-context 0.1.1 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: eb81fa6ece2d3615f46a7b72a066a8316c48065a
4
- data.tar.gz: d720c68f9154786ef561e97f67e29ff15e58be6c
2
+ SHA256:
3
+ metadata.gz: 0f6c3c7ff9c8b061c5ac0d5aca954f03cd1b5ff0bf286aa787ede12f1e187890
4
+ data.tar.gz: 0fdc45ffab9316c7799f2ce59f54c6409305cf91fa01349d30d026eac998c188
5
5
  SHA512:
6
- metadata.gz: 720a50aab05ec4de810b88227c7405f5bffaef9e2685419849f09cd56159e5e216c0ae5959b4d6502e2edabc7559bdfcbe5a01b61f9a141bfc01508f407e39a6
7
- data.tar.gz: 2aa161d5f6de1b93210f3e1f5f9c187f0dd8b44cc0bf2d335dbc36c6479d6891d1a4ac002dd729d3b824a5fd04304fe952b0038fa16e4105ac242ec584905020
6
+ metadata.gz: 2c9f349028afca126e1961e50ebb0ff661a5fe5a0e2573108c9c79f762694cdb03c6914beca4eec4c5b3b25aecae03d4706c47c84854d0ebb57b6e8a42ec279f
7
+ data.tar.gz: 1fba9db52f9e3680cccb3b89624c8a159bbd144fbd0b86b3a71972ea1feff96978b82867c976a568aec709b09d0fe859d173a1a813fa5747cb42d179bf6c7520
data/.gitignore CHANGED
@@ -9,6 +9,7 @@
9
9
  /test/tmp/
10
10
  /test/version_tmp/
11
11
  /tmp/
12
+ Gemfile.lock
12
13
 
13
14
  # Used by dotenv library to load environment variables.
14
15
  # .env
@@ -0,0 +1,12 @@
1
+ language: ruby
2
+ before_install: gem install bundler
3
+ cache: bundler
4
+ rvm:
5
+ - ruby-head
6
+ - 2.7
7
+ - 2.6
8
+ - 2.5
9
+ - 2.4
10
+ jobs:
11
+ allow_failures:
12
+ - rvm: ruby-head
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
@@ -1,5 +1,5 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
2
  gemspec
3
3
 
4
- gem "minitest-line"
5
4
  gem "benchmark-ips"
5
+ gem "minitest-line"
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  MIT License
2
2
 
3
- Copyright (c) 2017 TRAILBLAZER GmbH
3
+ Copyright (c) 2017-2020 TRAILBLAZER GmbH
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/Rakefile CHANGED
@@ -4,7 +4,7 @@ require "rake/testtask"
4
4
  Rake::TestTask.new(:test) do |t|
5
5
  t.libs << "test"
6
6
  t.libs << "lib"
7
- t.test_files = FileList['test/*_test.rb']
7
+ t.test_files = FileList["test/*_test.rb"]
8
8
  end
9
9
 
10
- task :default => :test
10
+ task default: %i[test]
@@ -1,2 +1,2 @@
1
+ require "trailblazer/context/version"
1
2
  require "trailblazer/context"
2
- require "trailblazer/option"
@@ -1,5 +1,6 @@
1
1
  # @private
2
- class Trailblazer::Context::ContainerChain # used to be called Resolver.
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
- return @to_hash.(@containers) if @to_hash # FIXME: introduce pattern matching so we can have different "transformers" for each container type.
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
@@ -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
- class Context # :data object:
15
- def initialize(wrapped_options, mutable_options)
16
- @wrapped_options, @mutable_options = wrapped_options, mutable_options
17
- # TODO: wrapped_options should be optimized for lookups here since it could also be a Context instance, but should be a ContainerChain.
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
- ctx = Trailblazer::Context( original, mutable_options.merge(hash) )
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
- [ @wrapped_options, @mutable_options ]
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
- [ @wrapped_options.to_hash, @mutable_options.to_hash ].each do |options|
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 # Trailblazer
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
@@ -1,5 +1,5 @@
1
1
  module Trailblazer
2
2
  class Context
3
- VERSION = "0.1.1"
3
+ VERSION = "0.3.0"
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,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, **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, **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) { 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.
@@ -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
@@ -0,0 +1,3 @@
1
+ require "trailblazer-context"
2
+
3
+ require "minitest/autorun"
@@ -1,6 +1,6 @@
1
- lib = File.expand_path('../lib', __FILE__)
1
+ lib = File.expand_path("lib", __dir__)
2
2
  $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
- require 'trailblazer/context/version'
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 = %q{Argument-specific data structures for Trailblazer.}
12
- spec.description = %q{Argument-specific data structures for Trailblazer such as Context, Option and ContainerChain.}
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{^(test|spec|features)/})
17
+ f.match(%r(^test/))
18
18
  end
19
- spec.bindir = "exe"
20
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
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", "~> 1.14"
24
- spec.add_development_dependency "rake", "~> 10.0"
25
- spec.add_development_dependency "minitest", "~> 5.0"
23
+ spec.add_development_dependency "bundler"
24
+ spec.add_development_dependency "minitest"
25
+ spec.add_development_dependency "rake"
26
26
 
27
- spec.required_ruby_version = '>= 2.0.0'
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.1.1
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nick Sutterer
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2017-12-14 00:00:00.000000000 Z
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: '1.14'
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: '1.14'
26
+ version: '0'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: minitest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
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: '10.0'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: minitest
42
+ name: rake
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - "~>"
45
+ - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '5.0'
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: '5.0'
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.0.0
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
- rubyforge_project:
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