trailblazer-context 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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: []