invocations 0.2.6
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 +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/.gitignore +57 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +22 -0
- data/README.org +128 -0
- data/invocations.gemspec +23 -0
- data/lib/invocations.rb +17 -0
- data/lib/invocations/invocation.rb +253 -0
- data/tests/invocations.rb +24 -0
- data/trust/certificates/colstrom.pem +25 -0
- metadata +78 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 212878a412f9d0078de6d2a13192a8d4867d72a3ec64c1a5680dd1e4bfd03acf
|
4
|
+
data.tar.gz: e9bf132d50b08da294099cb995ede79dadd9fc7304d2ab6fb8ab11839dfd49d4
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: da1fbe8a35ece0b67dbaebb87e300c970f4319f52532b922e3813ffb917f996f353632d43244e8278c4ff3894a8148bd855c80242a97f424945acc681117d99c
|
7
|
+
data.tar.gz: dae0dcfe98e1653f5e068ace7dd986ceb43eaaf61857f8e4139e7b7c878f47c0ad8e5e2faf0ff85faa01b8d1c561af204aeca3c701868928791d2735f28857a4
|
checksums.yaml.gz.sig
ADDED
Binary file
|
data.tar.gz.sig
ADDED
Binary file
|
data/.gitignore
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
|
2
|
+
# Created by https://www.gitignore.io/api/ruby
|
3
|
+
|
4
|
+
### Ruby ###
|
5
|
+
*.gem
|
6
|
+
*.rbc
|
7
|
+
/.config
|
8
|
+
/coverage/
|
9
|
+
/InstalledFiles
|
10
|
+
/pkg/
|
11
|
+
/spec/reports/
|
12
|
+
/spec/examples.txt
|
13
|
+
/test/tmp/
|
14
|
+
/test/version_tmp/
|
15
|
+
/tmp/
|
16
|
+
|
17
|
+
# Used by dotenv library to load environment variables.
|
18
|
+
# .env
|
19
|
+
|
20
|
+
## Specific to RubyMotion:
|
21
|
+
.dat*
|
22
|
+
.repl_history
|
23
|
+
build/
|
24
|
+
*.bridgesupport
|
25
|
+
build-iPhoneOS/
|
26
|
+
build-iPhoneSimulator/
|
27
|
+
|
28
|
+
## Specific to RubyMotion (use of CocoaPods):
|
29
|
+
#
|
30
|
+
# We recommend against adding the Pods directory to your .gitignore. However
|
31
|
+
# you should judge for yourself, the pros and cons are mentioned at:
|
32
|
+
# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control
|
33
|
+
#
|
34
|
+
# vendor/Pods/
|
35
|
+
|
36
|
+
## Documentation cache and generated files:
|
37
|
+
/.yardoc/
|
38
|
+
/_yardoc/
|
39
|
+
/doc/
|
40
|
+
/rdoc/
|
41
|
+
|
42
|
+
## Environment normalization:
|
43
|
+
/.bundle/
|
44
|
+
/vendor/bundle
|
45
|
+
/lib/bundler/man/
|
46
|
+
|
47
|
+
# for a library or gem, you might want to ignore these files since the code is
|
48
|
+
# intended to run in multiple environments; otherwise, check them in:
|
49
|
+
# Gemfile.lock
|
50
|
+
# .ruby-version
|
51
|
+
# .ruby-gemset
|
52
|
+
|
53
|
+
# unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
|
54
|
+
.rvmrc
|
55
|
+
|
56
|
+
|
57
|
+
# End of https://www.gitignore.io/api/ruby
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
|
2
|
+
The MIT License (MIT)
|
3
|
+
Copyright © 2018 Chris Olstrom <chris@olstrom.com>
|
4
|
+
Copyright © 2018 SUSE LLC
|
5
|
+
|
6
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
7
|
+
of this software and associated documentation files (the “Software”), to deal
|
8
|
+
in the Software without restriction, including without limitation the rights
|
9
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
10
|
+
copies of the Software, and to permit persons to whom the Software is
|
11
|
+
furnished to do so, subject to the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be included in
|
14
|
+
all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
17
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
18
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
19
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
20
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
21
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
22
|
+
THE SOFTWARE.
|
data/README.org
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
#+TITLE: Invocations
|
2
|
+
#+LATEX: \pagebreak
|
3
|
+
|
4
|
+
* Overview
|
5
|
+
|
6
|
+
~Invocations~ are better partial functions for Ruby, because sometimes
|
7
|
+
=Proc#curry= just isn't enough.
|
8
|
+
|
9
|
+
* What problem does this solve?
|
10
|
+
|
11
|
+
Partial function evaluation is a very useful tool, but understanding the state
|
12
|
+
carried by a curried =Proc= is often cumbersome and unintuitive.
|
13
|
+
|
14
|
+
* How does Invocations address this problem?
|
15
|
+
|
16
|
+
~Invocations~ provides a single class: the =Invocation=. It functions as a
|
17
|
+
drop-in alternative to a =Proc=, with a few notable improvements (none of
|
18
|
+
which break compatibility):
|
19
|
+
|
20
|
+
- An =Invocation= is implicitly self-currying. Which is to say that if you
|
21
|
+
call it without all the parameters it requires, it simply returns an
|
22
|
+
=Invocation= that requires the missing parameters.
|
23
|
+
- An =Invocation= allows non-keyword arguments to be given as keywords.
|
24
|
+
- An =Invocation= allows arguments to be given out of order, as keyword
|
25
|
+
arguments.
|
26
|
+
- An =Invocation= has several methods for inspection, including ones for
|
27
|
+
listing the missing arguments, identifying keyword inferences, explaining
|
28
|
+
how the underlying function will be called, and reporting internal state.
|
29
|
+
|
30
|
+
* Installation
|
31
|
+
|
32
|
+
#+BEGIN_SRC shell
|
33
|
+
gem install invocations
|
34
|
+
#+END_SRC
|
35
|
+
|
36
|
+
* How can I start using this majestic tool?
|
37
|
+
|
38
|
+
An =Invocation= is a drop-in alternative to =Proc=, or =lambda=. You can use
|
39
|
+
it as an explicit =&block=, etc. As a result, it's very easy to adapt existing
|
40
|
+
code to use it.
|
41
|
+
|
42
|
+
Let's lay out a simple function that will serve as our example, going forward.
|
43
|
+
|
44
|
+
This =power= function takes two arguments, =n= (a number) and =e= (an
|
45
|
+
exponent) and returns the result of raising =n= to =e=:
|
46
|
+
|
47
|
+
#+BEGIN_SRC ruby
|
48
|
+
lambda_power = lambda { |n, e| n ** e }
|
49
|
+
#+END_SRC
|
50
|
+
|
51
|
+
Now, this is somewhat contrived, because I've deliberately defined the
|
52
|
+
arguments in the least convenient order, for illustrative purposes.
|
53
|
+
|
54
|
+
The equivalent =Invocation= would be:
|
55
|
+
|
56
|
+
#+BEGIN_SRC ruby
|
57
|
+
invoke_power = Invocation.new { |n, e| n ** e }
|
58
|
+
#+END_SRC
|
59
|
+
|
60
|
+
~Invocations~ includes an optional =Refinement= for that brings the syntax
|
61
|
+
more in line with =proc= and =lambda=:
|
62
|
+
|
63
|
+
#+BEGIN_SRC ruby
|
64
|
+
using Invocations
|
65
|
+
invoke_power = invocation { |n, e| n ** e }
|
66
|
+
#+END_SRC
|
67
|
+
|
68
|
+
Calling either of these is the same:
|
69
|
+
|
70
|
+
#+BEGIN_SRC ruby
|
71
|
+
lambda_power.(5, 2) #=> 25
|
72
|
+
invoke_power.(5, 2) #=> 25
|
73
|
+
#+END_SRC
|
74
|
+
|
75
|
+
Let's say we wanted to define =lambda_square= and =lambda_cube= functions,
|
76
|
+
that do what their names imply:
|
77
|
+
|
78
|
+
#+BEGIN_SRC ruby
|
79
|
+
lambda_square = lambda { |n| lambda_power.(n, 2) }
|
80
|
+
lambda_cube = lambda { |n| lambda_power.(n, 3) }
|
81
|
+
#+END_SRC
|
82
|
+
|
83
|
+
The order of the arguments to =lambda_power= makes these definitions more
|
84
|
+
awkward. If instead, we had defined it like so:
|
85
|
+
|
86
|
+
#+BEGIN_SRC ruby
|
87
|
+
lambda_power = lambda { |e, n| n ** e }.curry
|
88
|
+
#+END_SRC
|
89
|
+
|
90
|
+
Then we could have done this:
|
91
|
+
|
92
|
+
#+BEGIN_SRC ruby
|
93
|
+
lambda_square = lambda_power.(2)
|
94
|
+
lambda_cube = lambda_power.(3)
|
95
|
+
#+END_SRC
|
96
|
+
|
97
|
+
That said, we don't define every function we use. Often we use the functions
|
98
|
+
provided by a library, and if those have inconvenient argument ordering, too
|
99
|
+
bad.
|
100
|
+
|
101
|
+
If we had been using an =Invocation=, we could have done this:
|
102
|
+
|
103
|
+
#+BEGIN_SRC ruby
|
104
|
+
invoke_square = invoke_power.(e: 2)
|
105
|
+
invoke_cube = invoke_power.(e: 3)
|
106
|
+
#+END_SRC
|
107
|
+
|
108
|
+
** Wait what? Those weren't keyword arguments.
|
109
|
+
|
110
|
+
True, but the block parameters have names. Since it is a =SyntaxError= for a
|
111
|
+
block to have two parameters with the same name, an =Invocation= can Do The
|
112
|
+
Right Thing.
|
113
|
+
|
114
|
+
* Explore It!
|
115
|
+
|
116
|
+
~Invocations~ really shines when used with a great REPL like [[https://github.com/pry/pry][pry]].
|
117
|
+
|
118
|
+
I've uploaded a short screencast [[https://asciinema.org/a/DW4ctct8Nkx1qdwjmOF9Eyw4O][here]] that demonstrates the sort of
|
119
|
+
information an =Invocation= provides (using the example scenario above).
|
120
|
+
|
121
|
+
* License
|
122
|
+
|
123
|
+
~Invocations~ is available under the [[https://tldrlegal.com/license/mit-license][MIT License]]. See ~LICENSE.txt~ for the
|
124
|
+
full text.
|
125
|
+
|
126
|
+
* Contributors
|
127
|
+
|
128
|
+
- [[https://colstrom.github.io/][Chris Olstrom]] | [[mailto:chris@olstrom.com][e-mail]] | [[https://twitter.com/ChrisOlstrom][Twitter]]
|
data/invocations.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
Gem::Specification.new do |gem|
|
2
|
+
tag = `git describe --tags --abbrev=0`.chomp
|
3
|
+
|
4
|
+
gem.name = 'invocations'
|
5
|
+
gem.homepage = 'https://github.com/colstrom/invocations'
|
6
|
+
gem.summary = 'Drop-in alternative to procs and lambdas.'
|
7
|
+
|
8
|
+
gem.version = "#{tag}"
|
9
|
+
gem.licenses = ['MIT']
|
10
|
+
gem.authors = ['Chris Olstrom']
|
11
|
+
gem.email = 'chris@olstrom.com'
|
12
|
+
|
13
|
+
gem.cert_chain = ['trust/certificates/colstrom.pem']
|
14
|
+
gem.signing_key = File.expand_path ENV.fetch 'GEM_SIGNING_KEY'
|
15
|
+
|
16
|
+
gem.files = `git ls-files -z`.split("\x0")
|
17
|
+
gem.test_files = `git ls-files -z -- {test,spec,features}/*`.split("\x0")
|
18
|
+
gem.executables = `git ls-files -z -- bin/*`.split("\x0").map { |f| File.basename(f) }
|
19
|
+
|
20
|
+
gem.require_paths = ['lib']
|
21
|
+
|
22
|
+
gem.required_ruby_version = '~> 2.3.0' # Explicit Non-Support of Unsupported Ruby Versions
|
23
|
+
end
|
data/lib/invocations.rb
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require_relative 'invocations/invocation'
|
5
|
+
|
6
|
+
# This module refines Kernel, adding "invocation" and "Invocation" as methods.
|
7
|
+
module Invocations
|
8
|
+
refine Kernel do
|
9
|
+
# Creates a new Invocation with the provided parameters
|
10
|
+
def invocation(*rest, **keyrest, &block)
|
11
|
+
::Invocation.new(*rest, **keyrest, &block)
|
12
|
+
end
|
13
|
+
|
14
|
+
alias_method :Invocation, :invocation
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,253 @@
|
|
1
|
+
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
# Invocations are drop-in replacements for other functions (such as
|
5
|
+
# procs/blocks, lambdas, methods, etc).
|
6
|
+
#
|
7
|
+
# They differ in that they are self-currying, allow partial evaluation with
|
8
|
+
# arbitrary argument ordering, and allow non-keyword arguments to be given as
|
9
|
+
# keywords.
|
10
|
+
#
|
11
|
+
class Invocation
|
12
|
+
%i(call yield []).each { |name| singleton_class.alias_method name, :new }
|
13
|
+
|
14
|
+
# Creates a new Invocation.
|
15
|
+
#
|
16
|
+
# @param callable [#call] a proc-like object
|
17
|
+
#
|
18
|
+
# @raise [ArgumentError] if neither a callable object nor a block is provided.
|
19
|
+
#
|
20
|
+
# @return [Invocation] a new instance of Invocation
|
21
|
+
#
|
22
|
+
def initialize(callable = nil, *rest, **state, &block)
|
23
|
+
raise ::ArgumentError, "#{self.class}##{__callee__} requires a callable object or a block" unless (callable.respond_to?(:call) || block)
|
24
|
+
|
25
|
+
if callable.respond_to? :call
|
26
|
+
@function = callable
|
27
|
+
@block = block
|
28
|
+
else
|
29
|
+
@function = block
|
30
|
+
end
|
31
|
+
|
32
|
+
@state = state
|
33
|
+
@rest = rest
|
34
|
+
|
35
|
+
%i(required optional inferences unassigned arguments keywords).each { |method| send method }
|
36
|
+
|
37
|
+
self
|
38
|
+
end
|
39
|
+
|
40
|
+
attr_reader :function, :state
|
41
|
+
|
42
|
+
# Which parameters are required?
|
43
|
+
#
|
44
|
+
# @return [Array<Symbol>] the required parameters of the function
|
45
|
+
#
|
46
|
+
def required
|
47
|
+
@required ||= (
|
48
|
+
required_arguments = function_parameters(:req)
|
49
|
+
required_keywords = function_parameters(:keyreq)
|
50
|
+
needed = function_arity - (required_arguments.length + (required_keywords.length > 0 ? 1 : 0))
|
51
|
+
|
52
|
+
@required = function_parameters(:opt, :key).take(needed) + required_arguments + required_keywords
|
53
|
+
)
|
54
|
+
end
|
55
|
+
|
56
|
+
# Which parameters are optional?
|
57
|
+
#
|
58
|
+
# @return [Array<Symbol>] the optional parameters of the function
|
59
|
+
#
|
60
|
+
def optional
|
61
|
+
@optional ||= function_parameters(:opt, :key)
|
62
|
+
.drop(function_arity - function_parameters(:req).length)
|
63
|
+
end
|
64
|
+
|
65
|
+
# Which parameters can be inferred from arguments?
|
66
|
+
#
|
67
|
+
# @return [Hash<Symbol, Object>] the inferred association of non-keyword
|
68
|
+
# arguments to parameter names.
|
69
|
+
#
|
70
|
+
def inferences
|
71
|
+
@inferences ||= if @rest.empty?
|
72
|
+
{}
|
73
|
+
else
|
74
|
+
(function_parameters(:req, :opt) - @state.keys)
|
75
|
+
.map
|
76
|
+
.with_index { |argument, index| [argument, @rest[index]] if index < @rest.length }
|
77
|
+
.compact
|
78
|
+
.to_h
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
# Which arguments are not assigned to known parameters?
|
83
|
+
#
|
84
|
+
# @return [Array] the arguments that are not assigned to parameters.
|
85
|
+
#
|
86
|
+
def unassigned
|
87
|
+
@unassigned ||= @rest.drop(inferences.length)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Which parameters have been provided?
|
91
|
+
#
|
92
|
+
# @return [Hash<Symbol, Object>] all known parameters provided for the function.
|
93
|
+
#
|
94
|
+
def known
|
95
|
+
@known ||= @state.merge(inferences)
|
96
|
+
end
|
97
|
+
|
98
|
+
# Which parameters are missing?
|
99
|
+
#
|
100
|
+
# @return [Array<Symbol>] the names of any missing parameters.
|
101
|
+
def missing
|
102
|
+
@missing ||= required - known.keys
|
103
|
+
end
|
104
|
+
|
105
|
+
# What are the arguments?
|
106
|
+
#
|
107
|
+
# @return [Array] the provided arguments for the function.
|
108
|
+
def arguments
|
109
|
+
@arguments ||= function_parameters(:req, :opt, :rest)
|
110
|
+
.map { |argument| known[argument] }
|
111
|
+
end
|
112
|
+
|
113
|
+
# What are the keywords?
|
114
|
+
#
|
115
|
+
# @return [Hash<Symbol, Object>] the provided keywords for the functino.
|
116
|
+
#
|
117
|
+
def keywords
|
118
|
+
@keywords ||= function_parameters(:keyreq, :key, :keyrest)
|
119
|
+
.map { |keyword| [keyword, known[keyword]] if known.key?(keyword) }
|
120
|
+
.compact
|
121
|
+
.to_h
|
122
|
+
end
|
123
|
+
|
124
|
+
# How will the function be called?
|
125
|
+
#
|
126
|
+
# @return Array the list of arguments that will be used to call the function.
|
127
|
+
#
|
128
|
+
def invocation(*rest, **keyrest)
|
129
|
+
@invocation ||= [*[*arguments, *unassigned].compact, *rest, *[**keywords.merge(keyrest)].reject(&:empty?)]
|
130
|
+
end
|
131
|
+
|
132
|
+
# Is the function ready to be called?
|
133
|
+
#
|
134
|
+
# @return [Boolean] if this Invocation has all required parameters specified.
|
135
|
+
#
|
136
|
+
def prepared?
|
137
|
+
missing.empty?
|
138
|
+
end
|
139
|
+
|
140
|
+
# Prepares an Invocation, without calling it.
|
141
|
+
#
|
142
|
+
# @return [Invocation] a new Invocation populated with the parameters given.
|
143
|
+
#
|
144
|
+
def prepare(*rest, **keyrest, &block)
|
145
|
+
self.class.new(@function, *[*unassigned, *rest], **known.merge(keyrest), &(block || @block))
|
146
|
+
end
|
147
|
+
|
148
|
+
# Prepares an Invocation, and invokes it if able.
|
149
|
+
#
|
150
|
+
# If the provided parameters produce a properly prepared Invocation, it will
|
151
|
+
# be invoked. Otherwise, it will be returned.
|
152
|
+
#
|
153
|
+
# @note This is very similar to how #call works with a curried Proc.
|
154
|
+
#
|
155
|
+
def call(*rest, **keyrest, &block)
|
156
|
+
if block || [rest, keyrest].all?(&:empty?)
|
157
|
+
prepared? ? invoke : self
|
158
|
+
else
|
159
|
+
function = prepare(*rest, **keyrest, &block)
|
160
|
+
function.prepared? ? function.send(:invoke) : function
|
161
|
+
end
|
162
|
+
end
|
163
|
+
|
164
|
+
alias [] call
|
165
|
+
alias yield call
|
166
|
+
|
167
|
+
# How many additional parameters are needed?
|
168
|
+
#
|
169
|
+
# @return [Integer] the number of required parameters.
|
170
|
+
#
|
171
|
+
# @note when multiple keyword parameters are required, they count as one
|
172
|
+
# parameter. This is consistent with other Ruby functions (Proc, etc).
|
173
|
+
#
|
174
|
+
def arity
|
175
|
+
@arity ||= (
|
176
|
+
required_keywords = function_parameters(:keyreq)
|
177
|
+
missing_keywords = required_keywords - known.keys
|
178
|
+
(missing - required_keywords).length + (missing_keywords.length > 0 ? 1 : 0)
|
179
|
+
)
|
180
|
+
end
|
181
|
+
|
182
|
+
# Converts the Invocation into a Proc
|
183
|
+
#
|
184
|
+
# @return [Proc] a Proc that calls the Invocation with any parameters given.
|
185
|
+
#
|
186
|
+
def to_proc
|
187
|
+
proc { |*rest, **keyrest, &block| self.(*rest, **keyrest, &block) }
|
188
|
+
end
|
189
|
+
|
190
|
+
# Converts the Invocation into a curried Proc.
|
191
|
+
#
|
192
|
+
# @return [Proc] a Proc, curried with the arity of the Invocation.
|
193
|
+
#
|
194
|
+
def curry(n = arity)
|
195
|
+
to_proc.curry(n)
|
196
|
+
end
|
197
|
+
|
198
|
+
# Is the Invocation a lambda?
|
199
|
+
#
|
200
|
+
# @return [false] false, always.
|
201
|
+
#
|
202
|
+
# @note An Invocation is not a lambda, as it does not handle arguments
|
203
|
+
# strictly. It is semantically much more proc-like.
|
204
|
+
#
|
205
|
+
def lambda?
|
206
|
+
false
|
207
|
+
end
|
208
|
+
|
209
|
+
# Which parameters have not been provided?
|
210
|
+
#
|
211
|
+
# @return [Array<Symbol, Symbol>] the remaining parameters of the function.
|
212
|
+
#
|
213
|
+
def parameters
|
214
|
+
@function.parameters.reject { |_, name| known.keys.include? name }
|
215
|
+
end
|
216
|
+
|
217
|
+
####################
|
218
|
+
# Internal Methods #
|
219
|
+
####################
|
220
|
+
|
221
|
+
private
|
222
|
+
|
223
|
+
# Invokes the function.
|
224
|
+
#
|
225
|
+
# @note This is used internally to call the function. A return cannot be
|
226
|
+
# specified, because it depends entirely on the function this Invocation was
|
227
|
+
# created with.
|
228
|
+
#
|
229
|
+
def invoke(*rest, **keyrest, &block)
|
230
|
+
@function.(*invocation(*rest, **keyrest), &(block || @block))
|
231
|
+
end
|
232
|
+
|
233
|
+
# How many parameters does the function require?
|
234
|
+
#
|
235
|
+
# @return [Integer] The arity of the initial function.
|
236
|
+
#
|
237
|
+
# @note this is for internal use, and is *not* strictly equivalent to
|
238
|
+
# function.arity. For example, it always returns a non-negative value.
|
239
|
+
#
|
240
|
+
def function_arity
|
241
|
+
@function.arity.positive? ? @function.arity : @function.arity.succ.abs
|
242
|
+
end
|
243
|
+
|
244
|
+
# What are the names of the parameters of certain types?
|
245
|
+
#
|
246
|
+
# @return [Array<Symbol>] the names of parameters matching the given types.
|
247
|
+
#
|
248
|
+
def function_parameters(*types)
|
249
|
+
@function.parameters
|
250
|
+
.select { |type, _| types.include? type }
|
251
|
+
.flat_map(&:last)
|
252
|
+
end
|
253
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
#! /usr/bin/env ruby
|
2
|
+
# -*- ruby -*-
|
3
|
+
|
4
|
+
require_relative '../lib/invocations'
|
5
|
+
|
6
|
+
class Invocation
|
7
|
+
def self.test
|
8
|
+
{
|
9
|
+
'&lambda' => (self.new(&lambda { |am1, am2, ao = nil, *ar, km1:, km2:, ko: nil, **kr| [am1, am2, ao, ar, km1, km2, ko, kr] })),
|
10
|
+
'lambda' => (self.new(lambda { |am1, am2, ao = nil, *ar, km1:, km2:, ko: nil, **kr| [am1, am2, ao, ar, km1, km2, ko, kr] })),
|
11
|
+
'&proc' => (self.new(&proc { |am1, am2, ao = nil, *ar, km1:, km2:, ko: nil, **kr| [am1, am2, ao, ar, km1, km2, ko, kr] })),
|
12
|
+
'proc' => (self.new(proc { |am1, am2, ao = nil, *ar, km1:, km2:, ko: nil, **kr| [am1, am2, ao, ar, km1, km2, ko, kr] })),
|
13
|
+
'&block' => (self.new { |am1, am2, ao = nil, *ar, km1:, km2:, ko: nil, **kr| [am1, am2, ao, ar, km1, km2, ko, kr] }),
|
14
|
+
}
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
abort unless 1 == Invocation
|
19
|
+
.test
|
20
|
+
.map { |style, function| [style, function.(1).(2).(3).(4, 5).(km1: 6).(km2: 7)] }
|
21
|
+
.to_h
|
22
|
+
.values
|
23
|
+
.uniq
|
24
|
+
.length
|
@@ -0,0 +1,25 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIENDCCApygAwIBAgIBATANBgkqhkiG9w0BAQsFADAiMSAwHgYDVQQDDBdjaHJp
|
3
|
+
cy9EQz1vbHN0cm9tL0RDPWNvbTAeFw0xODAzMTUxODMxMTdaFw0xOTAzMTUxODMx
|
4
|
+
MTdaMCIxIDAeBgNVBAMMF2NocmlzL0RDPW9sc3Ryb20vREM9Y29tMIIBojANBgkq
|
5
|
+
hkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAp31YmQvpMuQSlgX22B++/rxl4p4LYyaq
|
6
|
+
cDDbi8DBro9cm2H60lbpCuAUALiED2CagZEK0vel5W8AnZhhns0OEYAlpQtBSJtN
|
7
|
+
8P/jlNYruuY26aVhfHfyA5j1n7tVecJz3i/awEPGC3zuTfvUq7Ahn5czOy+hIm4M
|
8
|
+
epee881dqnJlXjzTX/TKFYQa9tYj4bhsjfJOV+EDMcao/DE3vmNcBKH8XFVv/wQe
|
9
|
+
MGC7VY5zBwow00AkCicNmIr0Psy5hLvqphJ/E3Eiu4UpXhiBfM0z7xiBPoPMBqOx
|
10
|
+
r1RzgfKm/JbDO7leFmrEi8hLofyMmbuGvrSTE274vS4EnKaW6OtK7QM5R+jOJZbd
|
11
|
+
67KUgSw+LdHNwu8xCuuQOdKPeSfWdNz94KAdczjzHdXUl/SpfmTuN/D+BCZjTxSo
|
12
|
+
F1kACSU6uGTBFKZy35XK+yqeny/1l6xRs6j+cON+LSRMKYSt7jdLcKQVk5wH2xLo
|
13
|
+
83njwnumFxKhiWu0oaT5dlDCtyYM85j9AgMBAAGjdTBzMAkGA1UdEwQCMAAwCwYD
|
14
|
+
VR0PBAQDAgSwMB0GA1UdDgQWBBQEd3/D0MMj9FHhMZk0QJDlrUtKaTAcBgNVHREE
|
15
|
+
FTATgRFjaHJpc0BvbHN0cm9tLmNvbTAcBgNVHRIEFTATgRFjaHJpc0BvbHN0cm9t
|
16
|
+
LmNvbTANBgkqhkiG9w0BAQsFAAOCAYEALritM5RkGNZ7cs8hlljSEyuwJrbJYOSX
|
17
|
+
6p1S0D83GlfGZ/5XABy1p4EGQjxiAYuDrnnIw6GLHpgxFEtUNvyNYVfAa6u6yz4Y
|
18
|
+
nEjbEF76zAAxoRfivDApGJ3G9wuZ14cHZswFJppf2N4RG14F8bfLtU1OMYDLw8eK
|
19
|
+
QJOpynqHtrSj+FfsyNb6d93K8rlNCEd4UHkdRH1m7VnG6M18HvkbQCRMJtOFg/3j
|
20
|
+
c66TgdClDMJJXXiktVinfsmpTwxe2IzjGvwo2CZ/S53WPU/jb4uQMUzY0tMw48rl
|
21
|
+
S07/1DQNogstTnLYueqkUS1PYEwtavKVnpAtnaOdf0rJ/Rk4hA36BRgAVyQrp0uu
|
22
|
+
mSbo3NCvepJNYsTOUM+Df421VuPq713JV0aJDqltyfPptTM7fmNMcukbRh0aRuMT
|
23
|
+
EIKh6yDoB+oCRuiTV0uw/lKE2PtbONhJb7uN1qhZqla/iBpmUjiEu8+skI+ygv9n
|
24
|
+
7Krw8FJrV3+VRCiZTPKHeshAfL9yeIZh
|
25
|
+
-----END CERTIFICATE-----
|
metadata
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: invocations
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Chris Olstrom
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIENDCCApygAwIBAgIBATANBgkqhkiG9w0BAQsFADAiMSAwHgYDVQQDDBdjaHJp
|
14
|
+
cy9EQz1vbHN0cm9tL0RDPWNvbTAeFw0xODAzMTUxODMxMTdaFw0xOTAzMTUxODMx
|
15
|
+
MTdaMCIxIDAeBgNVBAMMF2NocmlzL0RDPW9sc3Ryb20vREM9Y29tMIIBojANBgkq
|
16
|
+
hkiG9w0BAQEFAAOCAY8AMIIBigKCAYEAp31YmQvpMuQSlgX22B++/rxl4p4LYyaq
|
17
|
+
cDDbi8DBro9cm2H60lbpCuAUALiED2CagZEK0vel5W8AnZhhns0OEYAlpQtBSJtN
|
18
|
+
8P/jlNYruuY26aVhfHfyA5j1n7tVecJz3i/awEPGC3zuTfvUq7Ahn5czOy+hIm4M
|
19
|
+
epee881dqnJlXjzTX/TKFYQa9tYj4bhsjfJOV+EDMcao/DE3vmNcBKH8XFVv/wQe
|
20
|
+
MGC7VY5zBwow00AkCicNmIr0Psy5hLvqphJ/E3Eiu4UpXhiBfM0z7xiBPoPMBqOx
|
21
|
+
r1RzgfKm/JbDO7leFmrEi8hLofyMmbuGvrSTE274vS4EnKaW6OtK7QM5R+jOJZbd
|
22
|
+
67KUgSw+LdHNwu8xCuuQOdKPeSfWdNz94KAdczjzHdXUl/SpfmTuN/D+BCZjTxSo
|
23
|
+
F1kACSU6uGTBFKZy35XK+yqeny/1l6xRs6j+cON+LSRMKYSt7jdLcKQVk5wH2xLo
|
24
|
+
83njwnumFxKhiWu0oaT5dlDCtyYM85j9AgMBAAGjdTBzMAkGA1UdEwQCMAAwCwYD
|
25
|
+
VR0PBAQDAgSwMB0GA1UdDgQWBBQEd3/D0MMj9FHhMZk0QJDlrUtKaTAcBgNVHREE
|
26
|
+
FTATgRFjaHJpc0BvbHN0cm9tLmNvbTAcBgNVHRIEFTATgRFjaHJpc0BvbHN0cm9t
|
27
|
+
LmNvbTANBgkqhkiG9w0BAQsFAAOCAYEALritM5RkGNZ7cs8hlljSEyuwJrbJYOSX
|
28
|
+
6p1S0D83GlfGZ/5XABy1p4EGQjxiAYuDrnnIw6GLHpgxFEtUNvyNYVfAa6u6yz4Y
|
29
|
+
nEjbEF76zAAxoRfivDApGJ3G9wuZ14cHZswFJppf2N4RG14F8bfLtU1OMYDLw8eK
|
30
|
+
QJOpynqHtrSj+FfsyNb6d93K8rlNCEd4UHkdRH1m7VnG6M18HvkbQCRMJtOFg/3j
|
31
|
+
c66TgdClDMJJXXiktVinfsmpTwxe2IzjGvwo2CZ/S53WPU/jb4uQMUzY0tMw48rl
|
32
|
+
S07/1DQNogstTnLYueqkUS1PYEwtavKVnpAtnaOdf0rJ/Rk4hA36BRgAVyQrp0uu
|
33
|
+
mSbo3NCvepJNYsTOUM+Df421VuPq713JV0aJDqltyfPptTM7fmNMcukbRh0aRuMT
|
34
|
+
EIKh6yDoB+oCRuiTV0uw/lKE2PtbONhJb7uN1qhZqla/iBpmUjiEu8+skI+ygv9n
|
35
|
+
7Krw8FJrV3+VRCiZTPKHeshAfL9yeIZh
|
36
|
+
-----END CERTIFICATE-----
|
37
|
+
date: 2018-05-10 00:00:00.000000000 Z
|
38
|
+
dependencies: []
|
39
|
+
description:
|
40
|
+
email: chris@olstrom.com
|
41
|
+
executables: []
|
42
|
+
extensions: []
|
43
|
+
extra_rdoc_files: []
|
44
|
+
files:
|
45
|
+
- ".gitignore"
|
46
|
+
- Gemfile
|
47
|
+
- LICENSE.txt
|
48
|
+
- README.org
|
49
|
+
- invocations.gemspec
|
50
|
+
- lib/invocations.rb
|
51
|
+
- lib/invocations/invocation.rb
|
52
|
+
- tests/invocations.rb
|
53
|
+
- trust/certificates/colstrom.pem
|
54
|
+
homepage: https://github.com/colstrom/invocations
|
55
|
+
licenses:
|
56
|
+
- MIT
|
57
|
+
metadata: {}
|
58
|
+
post_install_message:
|
59
|
+
rdoc_options: []
|
60
|
+
require_paths:
|
61
|
+
- lib
|
62
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
63
|
+
requirements:
|
64
|
+
- - "~>"
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 2.3.0
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: '0'
|
72
|
+
requirements: []
|
73
|
+
rubyforge_project:
|
74
|
+
rubygems_version: 2.7.6
|
75
|
+
signing_key:
|
76
|
+
specification_version: 4
|
77
|
+
summary: Drop-in alternative to procs and lambdas.
|
78
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|