trailblazer-option 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
+ SHA256:
3
+ metadata.gz: 0c5d6eb7b91e88296224c9a75b28fae91a4c91d58b36f1193ed8c13068e77261
4
+ data.tar.gz: 4fb0d17ce2fbb30a2ac25c358c4a7b082e056aa406d646ac026991ba43ffe263
5
+ SHA512:
6
+ metadata.gz: 709e99594cbfb5810f6861a20294552ace31a364025e83a14709dee74e0339d8ed5e1e7ce9ac35bb18018864994a1ee81b1c8eea6115edf25f3d3d2b9febe22a
7
+ data.tar.gz: 8ac22945f47ead2bc30d143f608dbaf257605fe95bad012c7328bde2aff2acf278bd3410854fe7787c0d0d3450f8cd8a28d4edc1f0d01c7f0bcc455f12b4ff17
@@ -0,0 +1,17 @@
1
+ name: CI
2
+ on: [push, pull_request]
3
+ jobs:
4
+ test:
5
+ strategy:
6
+ fail-fast: false
7
+ matrix:
8
+ # Due to https://github.com/actions/runner/issues/849, we have to use quotes for '3.0'
9
+ ruby: [2.5, 2.6, 2.7, '3.0', head, jruby, jruby-head]
10
+ runs-on: ubuntu-latest
11
+ steps:
12
+ - uses: actions/checkout@v2
13
+ - uses: ruby/setup-ruby@v1
14
+ with:
15
+ ruby-version: ${{ matrix.ruby }}
16
+ bundler-cache: true # runs 'bundle install' and caches installed gems automatically
17
+ - run: bundle exec rake
data/.gitignore ADDED
@@ -0,0 +1,10 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.swp
data/CHANGES.md ADDED
@@ -0,0 +1,3 @@
1
+ # 0.1.0
2
+
3
+ * Separated from `trailblazer-context`, beginning its own world.
data/Gemfile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ source "https://rubygems.org"
4
+
5
+ gemspec
6
+
7
+ gem "rake"
8
+ gem "minitest"
9
+ gem "minitest-line"
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2017-2021 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 @@
1
+ # trailblazer-option
data/Rakefile ADDED
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rake/testtask"
5
+
6
+ Rake::TestTask.new(:test) do |t|
7
+ t.libs << "test"
8
+ t.libs << "lib"
9
+ t.test_files = FileList["test/**/*_test.rb"]
10
+ end
11
+
12
+ task default: %i[test]
data/bin/console ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "trailblazer/option"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ # (If you use this, don't forget to add pry to your Gemfile!)
11
+ # require "pry"
12
+ # Pry.start
13
+
14
+ require "irb"
15
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,2 @@
1
+ require "trailblazer/option/version"
2
+ require "trailblazer/option"
@@ -0,0 +1,56 @@
1
+ module Trailblazer
2
+ class Option
3
+ # A call implementation invoking `value.(*args, **keyword_arguments)` and plainly forwarding all arguments.
4
+ # Override this for your own step strategy.
5
+ # @private
6
+ def self.call!(value, *args, signal: :call, keyword_arguments: {}, **, &block)
7
+ # {**keyword_arguments} gets removed automatically if it's an empty hash.
8
+ # DISCUSS: is this a good practice?
9
+ value.public_send(signal, *args, **keyword_arguments, &block)
10
+ end
11
+
12
+ # Note that #evaluate_callable, #evaluate_proc and #evaluate_method drop most of the args.
13
+ # If you need those, override this class.
14
+ #
15
+ # @private
16
+ def self.evaluate_callable(value, *args, **options, &block)
17
+ call!(value, *args, **options, &block)
18
+ end
19
+
20
+ # Pass given `value` as a block and evaluate it within `exec_context` binding.
21
+ # @private
22
+ def self.evaluate_proc(value, *args, signal: :instance_exec, exec_context: raise("No :exec_context given."), **options)
23
+ call!(exec_context, *args, signal: signal, **options, &value)
24
+ end
25
+
26
+ # Make the exec_context's instance method a "lambda" and reuse #call!.
27
+ # @private
28
+ def self.evaluate_method(value, *args, exec_context: raise("No :exec_context given."), **options, &block)
29
+ call!(exec_context.method(value), *args, **options, &block)
30
+ end
31
+
32
+ # Choose appropriate evaluator and forward all arguments.
33
+ # @private
34
+ def self.evaluator(value, *args, **options, &block)
35
+ evaluate = case value
36
+ when Symbol then method(:evaluate_method)
37
+ when Proc then method(:evaluate_proc)
38
+ else method(:evaluate_callable)
39
+ end
40
+
41
+ evaluate.(value, *args, **options, &block)
42
+ end
43
+
44
+ # Generic builder for a callable "option".
45
+ # @param call_implementation [Class, Module] implements the process of calling the proc
46
+ # while passing arguments/options to it in a specific style (e.g. kw args, step interface).
47
+ # @return [Proc] when called, this proc will evaluate its option (at run-time).
48
+ def self.build(value)
49
+ ->(*args, **options, &block) { evaluator(value, *args, **options, &block) }
50
+ end
51
+ end
52
+
53
+ def self.Option(value)
54
+ Option.build(value)
55
+ end
56
+ end
@@ -0,0 +1,5 @@
1
+ module Trailblazer
2
+ class Option
3
+ VERSION = '0.1.0'
4
+ end
5
+ end
@@ -0,0 +1,170 @@
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, keyword_arguments: 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, keyword_arguments: 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
+ step = Step.new
56
+
57
+ assert_result option.(positional, **{keyword_arguments: keywords, exec_context: step})
58
+ end
59
+ end
60
+
61
+ describe "Callable" do
62
+ let(:option) { Trailblazer::Option(WithPositionalAndKeywords) }
63
+
64
+ it "passes through all args" do
65
+ assert_result option.(positional, keyword_arguments: keywords, exec_context: nil)
66
+ end
67
+
68
+ it "allows passing a block, too" do
69
+ assert_result option.(positional, keyword_arguments: keywords, exec_context: nil, &block), block
70
+ end
71
+ end
72
+ end
73
+
74
+ describe "positionals" do
75
+ def assert_result_pos(result)
76
+ if Gem::Version.new(RUBY_VERSION) >= Gem::Version.new("2.7.0")
77
+ _(result).must_equal([1, 2, [3, 4]])
78
+ _(positionals).must_equal [1, 2, 3, 4]
79
+ else
80
+ _(result).must_equal([1, 2, [3, 4, {}]])
81
+ _(positionals).must_equal [1, 2, 3, 4]
82
+ end
83
+ end
84
+
85
+ # In Ruby < 3.0, {*args} will grab both positionals and keyword arguments.
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 "keywords" do
126
+ def assert_result_kws(result)
127
+ _(keywords).must_equal({ a: 1, b: 2, c: 3, d: 4 })
128
+ _(result).must_equal([1, 2, { c: 3, d: 4 }])
129
+ end
130
+
131
+ # In Ruby < 3.0, {*args} will grab both positionals and keyword arguments.
132
+ class Step
133
+ def with_keywords(a:, b:, **rest)
134
+ [a, b, rest]
135
+ end
136
+ end
137
+
138
+ WITH_KEYWORDS = ->(a:, b:, **rest) do
139
+ [a, b, rest]
140
+ end
141
+
142
+ class WithKeywords
143
+ def self.call(a:, b:, **rest)
144
+ [a, b, rest]
145
+ end
146
+ end
147
+
148
+ let(:keywords) { { a: 1, b: 2, c: 3, d: 4 } }
149
+
150
+ it ":method" do
151
+ step = Step.new
152
+
153
+ option = Trailblazer::Option(:with_keywords)
154
+
155
+ assert_result_kws option.(keyword_arguments: keywords, exec_context: step)
156
+ end
157
+
158
+ it "-> {} lambda" do
159
+ option = Trailblazer::Option(WITH_KEYWORDS)
160
+
161
+ assert_result_kws option.(keyword_arguments: keywords, exec_context: "something")
162
+ end
163
+
164
+ it "callable" do
165
+ option = Trailblazer::Option(WithKeywords)
166
+
167
+ assert_result_kws option.(keyword_arguments: keywords, exec_context: "something")
168
+ end
169
+ end
170
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ $LOAD_PATH.unshift File.expand_path("../lib", __dir__)
4
+ require "trailblazer/option"
5
+
6
+ require "minitest/autorun"
@@ -0,0 +1,28 @@
1
+ lib = File.expand_path("lib", __dir__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require "trailblazer/option/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "trailblazer-option"
7
+ spec.version = Trailblazer::Option::VERSION
8
+ spec.authors = ["Nick Sutterer"]
9
+ spec.email = ["apotonick@gmail.com"]
10
+
11
+ spec.summary = "Callable patterns for options in Trailblazer"
12
+ spec.description = "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."
13
+ spec.homepage = "https://trailblazer.to/"
14
+ spec.licenses = ["MIT"]
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r(^test/))
18
+ end
19
+ spec.test_files = `git ls-files -z test`.split("\x0")
20
+
21
+ spec.require_paths = ["lib"]
22
+
23
+ spec.add_development_dependency "minitest", "~> 5.0"
24
+ spec.add_development_dependency "minitest-line", "~> 0.6.5"
25
+ spec.add_development_dependency "rake", "~> 13.0"
26
+
27
+ spec.required_ruby_version = ">= 2.1.0"
28
+ end
metadata ADDED
@@ -0,0 +1,103 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: trailblazer-option
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nick Sutterer
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-04-05 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: minitest
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '5.0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '5.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: minitest-line
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 0.6.5
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 0.6.5
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '13.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '13.0'
55
+ description: Wrap an option at compile-time and `call` it at runtime, which allows
56
+ to have the common `-> ()`, `:method` or `Callable` pattern used for most options.
57
+ email:
58
+ - apotonick@gmail.com
59
+ executables: []
60
+ extensions: []
61
+ extra_rdoc_files: []
62
+ files:
63
+ - ".github/workflows/ci.yml"
64
+ - ".gitignore"
65
+ - CHANGES.md
66
+ - Gemfile
67
+ - LICENSE
68
+ - README.md
69
+ - Rakefile
70
+ - bin/console
71
+ - bin/setup
72
+ - lib/trailblazer-option.rb
73
+ - lib/trailblazer/option.rb
74
+ - lib/trailblazer/option/version.rb
75
+ - test/option_test.rb
76
+ - test/test_helper.rb
77
+ - trailblazer-option.gemspec
78
+ homepage: https://trailblazer.to/
79
+ licenses:
80
+ - MIT
81
+ metadata: {}
82
+ post_install_message:
83
+ rdoc_options: []
84
+ require_paths:
85
+ - lib
86
+ required_ruby_version: !ruby/object:Gem::Requirement
87
+ requirements:
88
+ - - ">="
89
+ - !ruby/object:Gem::Version
90
+ version: 2.1.0
91
+ required_rubygems_version: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - ">="
94
+ - !ruby/object:Gem::Version
95
+ version: '0'
96
+ requirements: []
97
+ rubygems_version: 3.0.8
98
+ signing_key:
99
+ specification_version: 4
100
+ summary: Callable patterns for options in Trailblazer
101
+ test_files:
102
+ - test/option_test.rb
103
+ - test/test_helper.rb