method-pipeline 1.0.3

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
+ SHA256:
3
+ metadata.gz: 197ac28d84c9435d6f894925841cd79b2f25510529b1f027b539b6cb181dac17
4
+ data.tar.gz: 6308de9cb33fac936542ca87904530649c45f791cfc67ebc60a09f357d967e7a
5
+ SHA512:
6
+ metadata.gz: 82f756e9b6fa5faf26aff5e832a3c50d7305b9a0ad0a398b8fccbe59f7121a9916ac242329b598df064439db0cc6d8f6c5a479e5d8235d7c4ffa0f40813ee631
7
+ data.tar.gz: b94a698fed672c08d6d939c1bedbd8056b2de306bce1eeda964d67b05a110c76656b23bbe4ef447e3bd90a948a5d3a4dcce47fa5814adcfbb07456f7c45606f3
data/Gemfile ADDED
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+ source 'https://rubygems.org'
3
+ gemspec
4
+
5
+ # Development Apps
6
+ group :development do
7
+ group :type_check do
8
+ gem 'rbs', '~> 3.1.0', require: false
9
+ gem 'steep', '~> 1.5.0', require: false
10
+ end
11
+ group :documentation do
12
+ gem 'yard', '~> 0.9.0', require: false
13
+ gem 'commonmarker', '~> 0.23.0', require: false
14
+ end
15
+ group :test do
16
+ gem 'rake', '~> 13.0.0'
17
+ gem 'minitest', '~> 5.19.0'
18
+ end
19
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,35 @@
1
+ Copyright (c) 2023 ParadoxV5
2
+
3
+ The Universal Permissive License (UPL), Version 1.0
4
+
5
+ Subject to the condition set forth below, permission is hereby granted to any
6
+ person obtaining a copy of this software, associated documentation and/or data
7
+ (collectively the "Software"), free of charge and under any and all copyright
8
+ rights in the Software, and any and all patent rights owned or freely
9
+ licensable by each licensor hereunder covering either (i) the unmodified
10
+ Software as contributed to or provided by such licensor, or (ii) the Larger
11
+ Works (as defined below), to deal in both
12
+
13
+ (a) the Software, and
14
+ (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if
15
+ one is included with the Software (each a "Larger Work" to which the Software
16
+ is contributed by such licensors),
17
+
18
+ without restriction, including without limitation the rights to copy, create
19
+ derivative works of, display, perform, and distribute the Software and make,
20
+ use, sell, offer for sale, import, export, have made, and have sold the
21
+ Software and the Larger Work(s), and to sublicense the foregoing rights on
22
+ either these or other terms.
23
+
24
+ This license is subject to the following condition:
25
+ The above copyright notice and either this complete permission notice or at
26
+ a minimum a reference to the UPL must be included in all copies or
27
+ substantial portions of the Software.
28
+
29
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
30
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
31
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
32
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
33
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
34
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
35
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,148 @@
1
+ The `Pipeline` Refinement module bundles a method duo that builds
2
+ a clean and clever pure-Ruby solution to rightward method piping.
3
+
4
+ A reminder that Refinement modules are activated with `using` and last only
5
+ for that module/class block or file (if top-level). See: `refinements.rdoc`
6
+ ([on docs.ruby-lang.org](https://docs.ruby-lang.org/en/master/syntax/refinements_rdoc.html))
7
+
8
+
9
+ ## Synopsis Example
10
+
11
+ Here’s the example from `Kernel#then` straight off the Ruby 3.2 docs:
12
+ ```ruby
13
+ require 'open-uri'
14
+ require 'json'
15
+
16
+ construct_url(arguments).
17
+ then {|url| URI(url).read }.
18
+ then {|response| JSON.parse(response) }
19
+ ```
20
+
21
+ With the new Refinement active: (Oh gosh, this looks like a brand-new language!)
22
+ ```ruby
23
+ using Pipeline
24
+
25
+ arguments.then_pipe(
26
+ `construct_url`,
27
+ `URI`,
28
+ :read,
29
+ JSON.`:parse
30
+ )
31
+ ```
32
+
33
+
34
+ ## Walkthrough Introduction
35
+
36
+ We traditionally pipe methods leftwards as nesting arguments:
37
+ ```ruby
38
+ def parse(string) = …
39
+ do_stuff = ->(a){ … }
40
+
41
+ # This is even more incomprehensible if omitting round parentheses (poetry mode)
42
+ STDOUT.puts( # different-scope method
43
+ do_stuff.call( # Proc
44
+ parse( # same-scope method
45
+ input
46
+ )
47
+ )
48
+ )
49
+ ```
50
+
51
+ The trending Rightward Operations improve readability –
52
+ especially for wordy expressions like the above –
53
+ by matching English’s left-to-right writing direction.
54
+ We currently accomplish this with `Object#then` and *light-weight Procs*:
55
+ ```ruby
56
+ input
57
+ .then { parse _1 }
58
+ .then { do_stuff.call _1 }
59
+ .then { STDOUT.puts _1 }
60
+ ```
61
+
62
+ We alternatively can deconstruct the corresponding `#to_proc` objects with `&`:
63
+ ```ruby
64
+ # heck, these `method` calls can't even go poetry mode
65
+ input
66
+ .then(&method(:parse))
67
+ .then(&do_stuff)
68
+ .then(&STDOUT.method(:puts))
69
+ ```
70
+
71
+ The Refinement module `Pipeline` introduces `Object#then_pipe` to
72
+ eliminate repeating `then(&…)`. It gives a beautiful vibe similar to
73
+ that of the Pipe Operator `|>` in some other languages (namely Elixir).
74
+ ```ruby
75
+ using Pipeline
76
+
77
+ input.then_pipe(
78
+ method(:parse),
79
+ do_stuff,
80
+ STDOUT.method(:puts)
81
+ )
82
+ ```
83
+
84
+ However, unlike Lisp-1 languages like Python or JavaScript,
85
+ Lisp-2s like Ruby face the additional challenge of namespace disambiguation.
86
+ Ruby solves it with the reflection methods `method` & co., but, as seen above,
87
+ their verbosity makes them eyesores inside otherwise elegant syntax.
88
+ Therefore, `Pipeline` further improves the grammar by replacing the `` `…` ``
89
+ syntax (powered by `` Kernel#` ``) with an `alias` of the bulky `Object#method`:
90
+ ```ruby
91
+ using Pipeline
92
+
93
+ input.then_pipe(
94
+ `parse`,
95
+ do_stuff,
96
+ STDOUT.`:puts
97
+ )
98
+ ```
99
+
100
+
101
+ ## Why `` #` ``?
102
+
103
+ * It is the backend of both `` `…` `` and `%x{…}` syntaxes –
104
+ ``self.`:meth`` can instead be `%x:meth:` or `` `meth` ``.
105
+ * A Ruby script is not (strictly) a Shell script. User system differences aside,
106
+ summoning subshells should be the last resort when there are no Ruby/Gem APIs.
107
+ Dedicating the `` ` `` char to subshells is a waste;
108
+ e.g., you typically see `$(…)` in bash rather than `` `…` ``.
109
+ `Pipeline` assigns `` #` `` a new and recurrent purpose; it also `alias`es
110
+ the original `` Kernel#` `` to `Object#sys` in the event of its preference.
111
+
112
+
113
+ ## Limitations
114
+
115
+ * **The new `` #` `` ignores method visibilities (`private` or `protected`).**
116
+ * This is a Ruby limitation – one can bypass visibilities with
117
+ `Object#method` and `Method#call` regardless of this Refinement.
118
+ A security engineer would love reflection APIs that respect visibilities.
119
+ * The original design for the new `` #` `` was to match `Object#public_method`,
120
+ but it couldn’t retrieve oneself’s own `private` and `protected` methods.
121
+ * **`#then_pipe` cannot pass additional arguments at each step.**
122
+ * The current `Object#then`-based solution is as good as it can get –
123
+ There are no syntactical benefits to avoid a block while calling rightwards.
124
+ Hey, unlike `#then_pipe`, it welcomes (inner) blocks.
125
+ ```ruby
126
+ # a lambda – a Proc with fixed arities, unlike regula-o’ procs (or blocks)
127
+ do_stuff = ->(a, o, z){ … }
128
+
129
+ # This essentially wraps `do_stuff` in a one-arg block (as in F#).
130
+ # The `_1` resembles Hack’s special pipe variable `$$`.
131
+ o.then { do_stuff.(a, _1, z) }
132
+ # The previous, adapted for `then_pipe`
133
+ o.then_pipe ->{ do_stuff.(a, _1, z) }
134
+ # Pure-rightward solution
135
+ [a, o, b].then { do_stuff.(*_1) }
136
+ ```
137
+
138
+
139
+ ## Miniblog: My insights on a Pipeline Operator for Ruby
140
+
141
+ https://gist.github.com/ParadoxV5/4f563e609decbdac07d40e5f2dead751
142
+
143
+
144
+ ## License
145
+
146
+ Copyright (c) 2023 ParadoxV5
147
+
148
+ Licensed under the [Universal Permissive License v 1.0](https://oss.oracle.com/licenses/upl/)
data/Rakefile ADDED
@@ -0,0 +1,8 @@
1
+ require 'minitest/test_task'
2
+ # Create the following tasks:
3
+ # * test : run tests
4
+ # * test:cmd : print the testing command
5
+ # * test:isolated : run tests independently to surface order dependencies
6
+ # * test:deps : (alias of test:isolated)
7
+ # * test:slow : run tests and reports the slowest 25
8
+ Minitest::TestTask.create
data/Steepfile ADDED
@@ -0,0 +1,4 @@
1
+ target :lib do
2
+ check 'lib'
3
+ signature 'sig'
4
+ end
data/lib/pipeline.rb ADDED
@@ -0,0 +1,54 @@
1
+ # The `Pipeline` Refinement module bundles a method duo that builds
2
+ # a clean and clever pure-Ruby solution to rightward method piping.
3
+ # Check out [the README](index.html) for examples.
4
+ #
5
+ # Reminder: activate a Refinement module with `using`:
6
+ # ```ruby
7
+ # using Pipeline
8
+ # my_obj.then_pipe(…)
9
+ # ```
10
+ # Refinements are only active for the module/class block or file
11
+ # (if top-level) that’s `using` them. See: `refinements.rdoc`
12
+ # ([on docs.ruby-lang.org](https://docs.ruby-lang.org/en/master/syntax/refinements_rdoc.html))
13
+ #
14
+ # @!method then_pipe(*procs)
15
+ # Yield `self` to the first `Proc` (or `#to_proc` object) argument,
16
+ # then the result to the second argument, and so forth.
17
+ # ```ruby
18
+ # construct_url(arguments).then_pipe(
19
+ # proc {|url| URI(url).read },
20
+ # JSON.public_method :parse
21
+ # )
22
+ # ```
23
+ # @param procs `Proc` or `#to_proc` objects to call
24
+ # @return The result of the last call, or `self` if no arguments were given.
25
+ #
26
+ # @!method sys(command)
27
+ # Provide a replacement alias for `` Kernel#` ``, the subshell method.
28
+ # @see #`
29
+ #
30
+ # @!method `(name)
31
+ # Alias `Object#method`.
32
+ # ```ruby
33
+ # m = 42.` :to_s
34
+ # m.call #=> "42"
35
+ # ```
36
+ # The `` ` `` method is the backend to the `` `…` `` and `%x{…}` syntaxes.
37
+ # ```ruby
38
+ # class MyArray < Array
39
+ # using Pipeline
40
+ # def fetch_values(*indices)
41
+ # indices.map(&`[]`)
42
+ # end
43
+ # end
44
+ # a = MyArray.new(['A', 'B', 'C'])
45
+ # a.fetch_values 1, 3 #=> ["B", nil]
46
+ # ```
47
+ # @see #sys
48
+
49
+ module Pipeline; refine Object do
50
+ def then_pipe(*procs) = procs.reduce(self) { _1.then(&_2) }
51
+
52
+ alias sys `
53
+ alias ` method
54
+ end end
data/pipeline.gemspec ADDED
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ Gem::Specification.new do |spec|
4
+ spec.name = 'method-pipeline'
5
+ spec.summary = 'Pure-Ruby solution to method pipelines'
6
+ spec.version = '1.0.3'
7
+ spec.author = 'ParadoxV5'
8
+ spec.license = 'UPL-1.0'
9
+
10
+
11
+ github_account = spec.author
12
+ github = File.join 'https://github.com', github_account, spec.name
13
+ spec.homepage = github
14
+ spec.metadata = {
15
+ 'homepage_uri' => spec.homepage,
16
+ 'source_code_uri' => github,
17
+ 'changelog_uri' => File.join(github, 'releases'),
18
+ 'bug_tracker_uri' => File.join(github, 'issues'),
19
+ 'documentation_uri' => File.join('https://rubydoc.info/gems', spec.name)
20
+ }
21
+
22
+ spec.files = Dir['**/*']
23
+ spec.required_ruby_version = '~> 3.2'
24
+ end
@@ -0,0 +1,7 @@
1
+ path: .gem_rbs_collection
2
+
3
+ sources:
4
+ - name: ruby/gem_rbs_collection
5
+ remote: https://github.com/ruby/gem_rbs_collection.git
6
+ revision: main
7
+ repo_dir: gems
data/sig/pipeline.rbs ADDED
@@ -0,0 +1,13 @@
1
+ module Pipeline
2
+ VERSION: String
3
+
4
+ interface _Function
5
+ def to_proc: () -> ^(untyped) -> untyped
6
+ end
7
+ end
8
+ class Object
9
+ def then_pipe: (*Pipeline::_Function procs) -> untyped
10
+
11
+ def sys: (String) -> String
12
+ alias ` method
13
+ end
@@ -0,0 +1,43 @@
1
+ # frozen_string_literal: true
2
+ require 'minitest/autorun'
3
+ require 'pipeline'
4
+ describe Pipeline do
5
+
6
+ it 'is a module that refines the Object class and only that' do
7
+ assert_kind_of Module, Pipeline
8
+ assert_equal [Object], Pipeline.refinements.map(&:refined_class)
9
+ assert_empty Pipeline.instance_methods
10
+ assert_empty Pipeline.private_instance_methods
11
+ end
12
+ using Pipeline
13
+
14
+ describe '`#method` shorthand' do
15
+ it 'overrides `backticks` with a public delegate to #method or equivalent' do
16
+ o = Object.new
17
+ def o.echo = # do nothing for a phony name
18
+ assert_equal o.method(:echo), o.`('echo')
19
+ assert_equal o.method(:__id__), o.`(:__id__)
20
+ pass if o.`(:initialize)
21
+ end
22
+
23
+ it 'renames Object#` to Object#sys' do
24
+ assert_equal :`, Object.instance_method(:sys).original_name
25
+ random_string = Kernel.rand.to_s
26
+ assert_equal random_string, sys("echo #{random_string}").chomp
27
+ end
28
+ end
29
+
30
+ it 'defines Object#then_pipe that chain-calls each of the given _ToProc’s with the receiver as the first arg' do
31
+ receiver, proc_return = Object.new, Object.new
32
+ # It is neither required nor forbidden for `#then_pipe` to also
33
+ # pass their blocks to their _ToProc arg(s) (block-ception Lol)
34
+ a_proc = proc do|arg|
35
+ assert_same receiver, arg
36
+ proc_return
37
+ end
38
+ a_proc_like = Object.new
39
+ a_proc_like.define_singleton_method(:to_proc) { a_proc }
40
+ assert_same proc_return, receiver.then_pipe(a_proc_like)
41
+ receiver.then_pipe(a_proc, ->{ assert_same proc_return, _1 })
42
+ end
43
+ end
metadata ADDED
@@ -0,0 +1,57 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: method-pipeline
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.3
5
+ platform: ruby
6
+ authors:
7
+ - ParadoxV5
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2023-07-26 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description:
14
+ email:
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - Gemfile
20
+ - LICENSE.txt
21
+ - README.md
22
+ - Rakefile
23
+ - Steepfile
24
+ - lib/pipeline.rb
25
+ - pipeline.gemspec
26
+ - rbs_collection.yaml
27
+ - sig/pipeline.rbs
28
+ - test/pipeline_test.rb
29
+ homepage: https://github.com/ParadoxV5/method-pipeline
30
+ licenses:
31
+ - UPL-1.0
32
+ metadata:
33
+ homepage_uri: https://github.com/ParadoxV5/method-pipeline
34
+ source_code_uri: https://github.com/ParadoxV5/method-pipeline
35
+ changelog_uri: https://github.com/ParadoxV5/method-pipeline/releases
36
+ bug_tracker_uri: https://github.com/ParadoxV5/method-pipeline/issues
37
+ documentation_uri: https://rubydoc.info/gems/method-pipeline
38
+ post_install_message:
39
+ rdoc_options: []
40
+ require_paths:
41
+ - lib
42
+ required_ruby_version: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.2'
47
+ required_rubygems_version: !ruby/object:Gem::Requirement
48
+ requirements:
49
+ - - ">="
50
+ - !ruby/object:Gem::Version
51
+ version: '0'
52
+ requirements: []
53
+ rubygems_version: 3.4.10
54
+ signing_key:
55
+ specification_version: 4
56
+ summary: Pure-Ruby solution to method pipelines
57
+ test_files: []