trailblazer-context 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: cbed8941980e69f6e3ef14de677024e8c45f4573
4
+ data.tar.gz: 31c11f6e2a59b45868e0247772bf9eafc692f06e
5
+ SHA512:
6
+ metadata.gz: 33020d8f9f10cf5323e3dbe5572da5f8d1fe0b7842d1b2a73199be07a2bba2648ddcc146dea445e06551900152117bbedc19b7a6c5c476c2675d8222fa7fcaac
7
+ data.tar.gz: 6bf38334cd38dce21da64615860eec450bc180c4457c0b09399d179c376b204a7af1360349edc377054923e8f70354a22b943729d22412659eb80155998cddef
data/.gitignore ADDED
@@ -0,0 +1,50 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /spec/examples.txt
9
+ /test/tmp/
10
+ /test/version_tmp/
11
+ /tmp/
12
+
13
+ # Used by dotenv library to load environment variables.
14
+ # .env
15
+
16
+ ## Specific to RubyMotion:
17
+ .dat*
18
+ .repl_history
19
+ build/
20
+ *.bridgesupport
21
+ build-iPhoneOS/
22
+ build-iPhoneSimulator/
23
+
24
+ ## Specific to RubyMotion (use of CocoaPods):
25
+ #
26
+ # We recommend against adding the Pods directory to your .gitignore. However
27
+ # you should judge for yourself, the pros and cons are mentioned at:
28
+ # https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
29
+ #
30
+ # vendor/Pods/
31
+
32
+ ## Documentation cache and generated files:
33
+ /.yardoc/
34
+ /_yardoc/
35
+ /doc/
36
+ /rdoc/
37
+
38
+ ## Environment normalization:
39
+ /.bundle/
40
+ /vendor/bundle
41
+ /lib/bundler/man/
42
+
43
+ # for a library or gem, you might want to ignore these files since the code is
44
+ # intended to run in multiple environments; otherwise, check them in:
45
+ # Gemfile.lock
46
+ # .ruby-version
47
+ # .ruby-gemset
48
+
49
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
50
+ .rvmrc
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+ gemspec
3
+
4
+ gem "minitest-line"
5
+ gem "benchmark-ips"
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017 TRAILBLAZER GmbH
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,9 @@
1
+ # Trailblazer-context
2
+
3
+ _Argument-specific data structures for Trailblazer._
4
+
5
+ This gem provides data structures needed across `Activity`, `Workflow` and `Operation`, such as the following.
6
+
7
+ * `Trailblazer::Context` implements the so-called `options` hash that is passed between steps and implements the keyword arguments.
8
+ * `Trailblazer::Option` is often used to wrap an option at compile-time and `call` it at runtime, which allows to have the common `-> ()`, `:method` or `Callable` pattern used for most options.
9
+ * `Trailblazer::ContainerChain` to implement chained lookups of properties and allow including containers such as `Dry::Container` in this chain. This is experimental.
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList['test/*_test.rb']
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,2 @@
1
+ require "trailblazer/context"
2
+ require "trailblazer/option"
@@ -0,0 +1,45 @@
1
+ # @private
2
+ class Trailblazer::Context::ContainerChain # used to be called Resolver.
3
+ # Keeps a list of containers. When looking up a key/value, containers are traversed in
4
+ # the order they were added until key is found.
5
+ #
6
+ # Required Container interface: `#key?`, `#[]`.
7
+ #
8
+ # @note ContainerChain is an immutable data structure, it does not support writing.
9
+ # @param containers Array of <Container> objects (splatted)
10
+ def initialize(containers, to_hash: nil)
11
+ @containers = containers
12
+ @to_hash = to_hash
13
+ end
14
+
15
+ # @param name Symbol or String to lookup a value stored in one of the containers.
16
+ def [](name)
17
+ self.class.find(@containers, name)
18
+ end
19
+
20
+ # @private
21
+ def key?(name)
22
+ @containers.find { |container| container.key?(name) }
23
+ end
24
+
25
+ def self.find(containers, name)
26
+ containers.find { |container| container.key?(name) && (return container[name]) }
27
+ end
28
+
29
+ def keys
30
+ @containers.collect(&:keys).flatten
31
+ end
32
+
33
+ # @private
34
+ 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
+ @containers.each_with_object({}) { |container, hash| hash.merge!(container.to_hash) }
37
+ end
38
+ end
39
+
40
+ # alternative implementation:
41
+ # containers.reverse.each do |container| @mutable_options.merge!(container) end
42
+ #
43
+ # benchmark, merging in #initialize vs. this resolver.
44
+ # merge 39.678k (± 9.1%) i/s - 198.700k in 5.056653s
45
+ # resolver 68.928k (± 6.4%) i/s - 342.836k in 5.001610s
@@ -0,0 +1,68 @@
1
+ # TODO: mark/make all but mutable_options as frozen.
2
+ # The idea of Skill is to have a generic, ordered read/write interface that
3
+ # collects mutable runtime-computed data while providing access to compile-time
4
+ # information.
5
+ # The runtime-data takes precedence over the class data.
6
+ module Trailblazer
7
+ # Holds local options (aka `mutable_options`) and "original" options from the "outer"
8
+ # activity (aka wrapped_options).
9
+
10
+ # only public creator: Build
11
+ class Context # :data object:
12
+ def initialize(wrapped_options, mutable_options)
13
+ @wrapped_options, @mutable_options = wrapped_options, mutable_options
14
+ end
15
+
16
+ def [](name)
17
+ ContainerChain.find( [@mutable_options, @wrapped_options], name )
18
+ end
19
+
20
+ def key?(name)
21
+ @mutable_options.key?(name) || @wrapped_options.key?(name)
22
+ end
23
+
24
+ def []=(name, value)
25
+ @mutable_options[name] = value
26
+ end
27
+
28
+ def merge(hash)
29
+ original, mutable_options = decompose
30
+
31
+ ctx = Trailblazer::Context( original, mutable_options.merge(hash) )
32
+ end
33
+
34
+ # Return the Context's two components. Used when computing the new output for
35
+ # the next activity.
36
+ def decompose
37
+ [ @wrapped_options, @mutable_options ]
38
+ end
39
+
40
+ def key?(name)
41
+ ContainerChain.find( [@mutable_options, @wrapped_options], name )
42
+ end
43
+
44
+
45
+ def keys
46
+ @mutable_options.keys + @wrapped_options.keys # FIXME.
47
+ end
48
+
49
+
50
+
51
+ # TODO: maybe we shouldn't allow to_hash from context?
52
+ # TODO: massive performance bottleneck. also, we could already "know" here what keys the
53
+ # transformation wants.
54
+ # FIXME: ToKeywordArguments()
55
+ def to_hash
56
+ {}.tap do |hash|
57
+ # the "key" here is to call to_hash on all containers.
58
+ [ @wrapped_options.to_hash, @mutable_options.to_hash ].each do |options|
59
+ options.each { |k, v| hash[k.to_sym] = v }
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ def self.Context(wrapped_options, mutable_options={})
66
+ Context.new(wrapped_options, mutable_options)
67
+ end
68
+ end # Trailblazer
@@ -0,0 +1,5 @@
1
+ module Trailblazer
2
+ class Context
3
+ VERSION = "0.1.0"
4
+ end
5
+ end
@@ -0,0 +1,78 @@
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
+ 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) { call_implementation.evaluate_method(proc, *args) }
15
+ else
16
+ ->(*args) { call_implementation.evaluate_callable(proc, *args) }
17
+ end
18
+ end
19
+
20
+ # A call implementation invoking `proc.(*args)` and plainly forwarding all arguments.
21
+ # Override this for your own step strategy (see KW#call!).
22
+ # @private
23
+ def self.call!(proc, *args)
24
+ proc.(*args)
25
+ end
26
+
27
+ # Note that both #evaluate_callable and #evaluate_method drop most of the args.
28
+ # If you need those, override this class.
29
+ # @private
30
+ def self.evaluate_callable(proc, *args, **flow_options)
31
+ call!(proc, *args)
32
+ end
33
+
34
+ # Make the context's instance method a "lambda" and reuse #call!.
35
+ # @private
36
+ def self.evaluate_method(proc, *args, exec_context:raise, **flow_options)
37
+ call!(exec_context.method(proc), *args)
38
+ end
39
+
40
+ # Returns a {Proc} that, when called, invokes the `proc` argument with keyword arguments.
41
+ # This is known as "step (call) interface".
42
+ #
43
+ # This is commonly used by `Operation::step` to wrap the argument and make it
44
+ # callable in the circuit.
45
+ #
46
+ # my_proc = ->(options, **kws) { options["i got called"] = true }
47
+ # task = Trailblazer::Option::KW(my_proc)
48
+ # task.(options = {})
49
+ # options["i got called"] #=> true
50
+ #
51
+ # Alternatively, you can pass a symbol and an `:exec_context`.
52
+ #
53
+ # my_proc = :some_method
54
+ # task = Trailblazer::Option::KW(my_proc)
55
+ #
56
+ # class A
57
+ # def some_method(options, **kws)
58
+ # options["i got called"] = true
59
+ # end
60
+ # end
61
+ #
62
+ # task.(options = {}, exec_context: A.new)
63
+ # options["i got called"] #=> true
64
+ def self.KW(proc)
65
+ Option.build(KW, proc)
66
+ end
67
+
68
+ # TODO: It would be cool if call! was typed and had `options SymbolizedHash` or something.
69
+ class KW < Option
70
+ # A different call implementation that calls `proc` with a "step interface".
71
+ # your_code.(options, **options)
72
+ # @private
73
+ def self.call!(proc, options, *)
74
+ proc.(options, **options.to_hash) # Step interface: (options, **)
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,28 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'trailblazer/context/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "trailblazer-context"
7
+ spec.version = Trailblazer::Context::VERSION
8
+ spec.authors = ["Nick Sutterer"]
9
+ spec.email = ["apotonick@gmail.com"]
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.}
13
+ spec.homepage = "http://trailblazer.to/gems/workflow"
14
+ spec.licenses = ["MIT"]
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
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"
26
+
27
+ spec.required_ruby_version = '>= 2.0.0'
28
+ end
metadata ADDED
@@ -0,0 +1,98 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trailblazer-context
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Sutterer
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2017-12-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.14'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.14'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description: Argument-specific data structures for Trailblazer such as Context, Option
56
+ and ContainerChain.
57
+ email:
58
+ - apotonick@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".gitignore"
64
+ - Gemfile
65
+ - LICENSE
66
+ - README.md
67
+ - Rakefile
68
+ - lib/trailblazer-context.rb
69
+ - lib/trailblazer/container_chain.rb
70
+ - lib/trailblazer/context.rb
71
+ - lib/trailblazer/context/version.rb
72
+ - lib/trailblazer/option.rb
73
+ - trailblazer-context.gemspec
74
+ homepage: http://trailblazer.to/gems/workflow
75
+ licenses:
76
+ - MIT
77
+ metadata: {}
78
+ post_install_message:
79
+ rdoc_options: []
80
+ require_paths:
81
+ - lib
82
+ required_ruby_version: !ruby/object:Gem::Requirement
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ version: 2.0.0
87
+ required_rubygems_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ requirements: []
93
+ rubyforge_project:
94
+ rubygems_version: 2.6.8
95
+ signing_key:
96
+ specification_version: 4
97
+ summary: Argument-specific data structures for Trailblazer.
98
+ test_files: []