invokable 0.4.1 → 0.6.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 +4 -4
- data/CHANGELOG.md +19 -0
- data/Gemfile.lock +1 -1
- data/README.md +48 -19
- data/lib/invokable.rb +42 -0
- data/lib/invokable/closure.rb +115 -0
- data/lib/invokable/command.rb +18 -0
- data/lib/invokable/core.rb +9 -0
- data/lib/invokable/data.rb +0 -2
- data/lib/invokable/version.rb +1 -1
- metadata +8 -5
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: b1c89f44b80135945cce3300f6f9b8e5b1210282d8c3d10256c2593ccf281948
|
4
|
+
data.tar.gz: 2a0e9ce82c10b9f57b1767087d56b5d53a67bdfeb139e5a642fb071a9bc6a8e6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 20d8a431d84d973709f122211c8e606d4ac43e162c54a7bdf8e7507873c0235dede63a8e73a69d296152b6020ebd93a3001be8c73916dee7a7cfbcc6c600b0df
|
7
|
+
data.tar.gz: c1df682d8c7eed2525d7a2eaadce3e5dedb8114b18d64df3f7e323995b7c30c31827d2c6f2da94738094c06614a74bf4309dc20a1d3a2e9b66152c6e265b7035
|
data/CHANGELOG.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# Change Log
|
2
|
+
|
3
|
+
## 0.4.2
|
4
|
+
|
5
|
+
- `invokable/array` is no longer loaded with `invokable/data`.
|
6
|
+
This created a bit of havok in a few places. Including breaking
|
7
|
+
puma bootup in Rails 5.2.4.1.
|
8
|
+
|
9
|
+
## 0.5.0
|
10
|
+
|
11
|
+
- Added `Invokable::Command` and `Invokable::Core#arity`
|
12
|
+
|
13
|
+
## 0.5.2
|
14
|
+
|
15
|
+
- `Invokable::Command` deprecated in favor of `Invokable::Closure`.
|
16
|
+
|
17
|
+
## 0.6.0
|
18
|
+
|
19
|
+
- `Invokable::Closure` deprecated comparable behavior has been added to `Invokable` itself.
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -8,40 +8,68 @@ Objects are functions! Treat any Object, Hashes, Arrays, and Sets as Procs (like
|
|
8
8
|
## Synopsis
|
9
9
|
|
10
10
|
```ruby
|
11
|
-
|
11
|
+
require 'invokable'
|
12
|
+
require 'invokable/hash'
|
12
13
|
|
13
|
-
|
14
|
-
|
14
|
+
number_names = { 1 => "One", 2 => "Two", 3 => "Three" }
|
15
|
+
[1, 2, 3, 4].map(&number_names) # => ["One", "Two", "Three", nil]
|
15
16
|
```
|
16
17
|
|
17
18
|
```ruby
|
18
|
-
|
19
|
+
require 'invokable'
|
20
|
+
require 'invokable/array'
|
19
21
|
|
20
|
-
|
21
|
-
|
22
|
+
alpha = ('a'..'z').to_a
|
23
|
+
[1, 2, 3, 4].map(&alpha) # => ["b", "c", "d", "e"]
|
22
24
|
```
|
23
25
|
|
24
26
|
```ruby
|
25
|
-
|
27
|
+
require 'invokable'
|
28
|
+
require 'invokable/set'
|
26
29
|
|
27
|
-
|
28
|
-
|
30
|
+
favorite_numbers = Set[3, Math::PI]
|
31
|
+
[1, 2, 3, 4].select(&favorite_numbers) # => [3]
|
29
32
|
```
|
30
33
|
|
31
34
|
```ruby
|
32
|
-
|
35
|
+
# service objects
|
36
|
+
require 'invokable'
|
33
37
|
|
34
|
-
|
35
|
-
|
36
|
-
include Invokable
|
38
|
+
class GetDataFromSomeService
|
39
|
+
include Invokable
|
37
40
|
|
38
|
-
|
39
|
-
|
40
|
-
end
|
41
|
+
def call(user)
|
42
|
+
# do the dirt
|
41
43
|
end
|
44
|
+
end
|
42
45
|
|
43
|
-
|
44
|
-
|
46
|
+
data_for_user = GetDataFromSomeService.new.memoize # 'memoize' makes a proc that caches results
|
47
|
+
User.all.map(&data_for_user)
|
48
|
+
```
|
49
|
+
```ruby
|
50
|
+
# command objects that enclose state, can be treated as automatically curried functions.
|
51
|
+
require 'invokable'
|
52
|
+
|
53
|
+
class TwitterPoster
|
54
|
+
include Invokable
|
55
|
+
|
56
|
+
def initialize(model)
|
57
|
+
@model = model
|
58
|
+
end
|
59
|
+
|
60
|
+
def call(user)
|
61
|
+
# do the dirt
|
62
|
+
...
|
63
|
+
TwitterStatus.new(user, data)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
TwitterPoster.call(Model.find(1)) # => #<TwitterPoster ...>
|
68
|
+
TwitterPoster.call(Model.find(1), current_user) # => #<TwitterStatus ...>
|
69
|
+
|
70
|
+
# both the class and it's instances can be used anywhere Procs are.
|
71
|
+
|
72
|
+
Model.where(created_at: Date.today).map(&TwitterPoster) # => [#<TwitterPoster ...>, ...]
|
45
73
|
```
|
46
74
|
|
47
75
|
Use as much or a little as you need:
|
@@ -51,7 +79,7 @@ require 'invokable' # loads Invokable module
|
|
51
79
|
require 'invokable/hash' # loads hash patch
|
52
80
|
require 'invokable/array' # loads array patch
|
53
81
|
require 'invokable/set' # loads set patch
|
54
|
-
require 'invokable/data' # loads hash
|
82
|
+
require 'invokable/data' # loads hash and set patches
|
55
83
|
```
|
56
84
|
|
57
85
|
## Why?
|
@@ -111,6 +139,7 @@ Returns a proc that is a composition of this invokable and the given invokable.
|
|
111
139
|
|
112
140
|
## See Also
|
113
141
|
|
142
|
+
- [Closures and Objects are Equivalent](http://wiki.c2.com/?ClosuresAndObjectsAreEquivalent)
|
114
143
|
- [Clojure](https://clojure.org)
|
115
144
|
- [Arc](http://www.arclanguage.org)
|
116
145
|
|
data/lib/invokable.rb
CHANGED
@@ -2,6 +2,7 @@ require 'invokable/version'
|
|
2
2
|
require 'invokable/core'
|
3
3
|
require 'invokable/compose'
|
4
4
|
|
5
|
+
# TODO: make use of Gem::Version
|
5
6
|
if RUBY_VERSION.split('.').take(2).join('.').to_f < 2.6
|
6
7
|
require 'invokable/proc'
|
7
8
|
require 'invokable/method'
|
@@ -11,5 +12,46 @@ module Invokable
|
|
11
12
|
def self.included(base)
|
12
13
|
base.include(Invokable::Core)
|
13
14
|
base.include(Invokable::Compose)
|
15
|
+
base.extend(Invokable::Core)
|
16
|
+
base.extend(Invokable::Compose)
|
17
|
+
base.extend(ClassMethods)
|
18
|
+
end
|
19
|
+
|
20
|
+
module ClassMethods
|
21
|
+
# Return the "total" arity of the class (i.e. the arity of the initializer and the arity of the call method)
|
22
|
+
#
|
23
|
+
# @version 0.6.0
|
24
|
+
# @see https://ruby-doc.org/core-2.7.1/Proc.html#method-i-arity Proc#arity
|
25
|
+
# @see initializer_arity
|
26
|
+
#
|
27
|
+
# @return [Integer]
|
28
|
+
def arity
|
29
|
+
initializer_arity + instance_method(:call).arity
|
30
|
+
end
|
31
|
+
|
32
|
+
# Handle automatic currying--will accept either the initializer arity or the total arity of the class. If
|
33
|
+
# the initializer arity is used return a class instance. If the total arity is used instantiate the class
|
34
|
+
# and return the results of the `call` method.
|
35
|
+
#
|
36
|
+
# @version 0.6.0
|
37
|
+
# @see arity
|
38
|
+
# @see initializer_arity
|
39
|
+
def call(*args)
|
40
|
+
if args.length == initializer_arity
|
41
|
+
new(*args)
|
42
|
+
elsif args.length == arity
|
43
|
+
init_args = args.slice(0, initializer_arity)
|
44
|
+
call_args = args.slice(initializer_arity, args.length)
|
45
|
+
new(*init_args).call(*call_args)
|
46
|
+
else
|
47
|
+
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected #{initializer_arity} or #{arity})"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
def initializer_arity
|
54
|
+
instance_method(:initialize).arity
|
55
|
+
end
|
14
56
|
end
|
15
57
|
end
|
@@ -0,0 +1,115 @@
|
|
1
|
+
module Invokable
|
2
|
+
# Treat classes as curried functions
|
3
|
+
#
|
4
|
+
# @see https://ruby-doc.org/core-2.7.0/Proc.html#method-i-curry Proc#curry
|
5
|
+
#
|
6
|
+
# @version 0.5.2
|
7
|
+
# @deprecated These features are included in the Invokable by default now.
|
8
|
+
module Closure
|
9
|
+
def self.included(klass)
|
10
|
+
klass.include(Invokable)
|
11
|
+
klass.extend(Invokable::Core)
|
12
|
+
klass.extend(Invokable::Compose)
|
13
|
+
klass.extend(ClassMethods)
|
14
|
+
end
|
15
|
+
|
16
|
+
module ClassMethods
|
17
|
+
# Return the "total" arity of the class (i.e. the arity of the initializer and the arity of the call method)
|
18
|
+
#
|
19
|
+
# @version 0.5.0
|
20
|
+
# @see https://ruby-doc.org/core-2.7.1/Proc.html#method-i-arity Proc#arity
|
21
|
+
# @see initializer_arity
|
22
|
+
#
|
23
|
+
# @return [Integer]
|
24
|
+
def arity
|
25
|
+
initializer_arity + instance_method(:call).arity
|
26
|
+
end
|
27
|
+
|
28
|
+
# Return the arity of the initializer
|
29
|
+
#
|
30
|
+
# @version 0.5.0
|
31
|
+
# @see arity
|
32
|
+
#
|
33
|
+
# @return [Integer]
|
34
|
+
def initializer_arity
|
35
|
+
instance_method(:initialize).arity
|
36
|
+
end
|
37
|
+
|
38
|
+
# Handle automatic currying--will accept either the initializer arity or the total arity of the class. If
|
39
|
+
# the initializer arity is used return a class instance. If the total arity is used instantiate the class
|
40
|
+
# and return the results of the `call` method.
|
41
|
+
#
|
42
|
+
# @version 0.5.0
|
43
|
+
# @see arity
|
44
|
+
# @see initializer_arity
|
45
|
+
def call(*args)
|
46
|
+
if args.length == initializer_arity
|
47
|
+
new(*args)
|
48
|
+
elsif args.length == arity
|
49
|
+
init_args = args.slice(0, initializer_arity)
|
50
|
+
call_args = args.slice(initializer_arity, args.length)
|
51
|
+
new(*init_args).call(*call_args)
|
52
|
+
else
|
53
|
+
raise ArgumentError, "wrong number of arguments (given #{args.length}, expected #{initializer_arity} or #{arity})"
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
# Specify any enclosed state with a block or named attributes
|
58
|
+
#
|
59
|
+
# @example
|
60
|
+
# class TwitterPater
|
61
|
+
# include Invokable::Command
|
62
|
+
#
|
63
|
+
# enclose :api_key
|
64
|
+
#
|
65
|
+
# def call(user)
|
66
|
+
# # interact with twitter, return results
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# TwitterPater.new(API_KEY).call(User.find(1))
|
71
|
+
# TwitterPater.new(API_KEY).api_key == API_KEY # => true
|
72
|
+
#
|
73
|
+
# class TwitterPater
|
74
|
+
# include Invokable::Command
|
75
|
+
#
|
76
|
+
# enclose do |api_key|
|
77
|
+
# @api_key = api_key
|
78
|
+
# end
|
79
|
+
#
|
80
|
+
# def call(user)
|
81
|
+
# # interact with twitter, return results
|
82
|
+
# end
|
83
|
+
# end
|
84
|
+
#
|
85
|
+
# TwitterPater.new(API_KEY).call(User.find(1))
|
86
|
+
# TwitterPater.new(API_KEY).api_key # error 'method' missing
|
87
|
+
def enclose(*names, &block)
|
88
|
+
return define_initializer_with_block(block) unless block.nil?
|
89
|
+
|
90
|
+
define_initializer_with_names(names)
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
|
95
|
+
def define_initializer_with_block(block)
|
96
|
+
@initializer = block
|
97
|
+
define_method :initialize, &block
|
98
|
+
end
|
99
|
+
|
100
|
+
def define_initializer_with_names(names)
|
101
|
+
@initializer_arity = names.length
|
102
|
+
|
103
|
+
names.each do |name|
|
104
|
+
attr_reader name
|
105
|
+
end
|
106
|
+
|
107
|
+
define_method :initialize do |*args|
|
108
|
+
names.each_with_index do |name, i|
|
109
|
+
instance_variable_set(:"@#{name}", args[i])
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
require_relative 'closure'
|
2
|
+
|
3
|
+
module Invokable
|
4
|
+
# Treat "Command Objects" as curried functions
|
5
|
+
#
|
6
|
+
# @see https://ruby-doc.org/core-2.7.0/Proc.html#method-i-curry Proc#curry
|
7
|
+
#
|
8
|
+
# @version 0.5.0
|
9
|
+
# @deprecated These features are included in the Invokable by default now.
|
10
|
+
module Command
|
11
|
+
def self.included(klass)
|
12
|
+
klass.include(Invokable)
|
13
|
+
klass.extend(Invokable::Core)
|
14
|
+
klass.extend(Invokable::Compose)
|
15
|
+
klass.extend(Invokable::Closure::ClassMethods)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
data/lib/invokable/core.rb
CHANGED
@@ -36,5 +36,14 @@ module Invokable
|
|
36
36
|
@memo[args.hash] ||= call(*args)
|
37
37
|
end
|
38
38
|
end
|
39
|
+
|
40
|
+
# Return the arity (i.e. the number of arguments) of the `call` method.
|
41
|
+
#
|
42
|
+
# @version 0.5.0
|
43
|
+
# @see https://ruby-doc.org/core-2.7.1/Proc.html#method-i-arity Proc#arity
|
44
|
+
# @return [Integer]
|
45
|
+
def arity
|
46
|
+
method(:call).arity
|
47
|
+
end
|
39
48
|
end
|
40
49
|
end
|
data/lib/invokable/data.rb
CHANGED
data/lib/invokable/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: invokable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.6.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Delon Newman
|
8
|
-
autorequire:
|
8
|
+
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2020-
|
11
|
+
date: 2020-08-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -63,6 +63,7 @@ files:
|
|
63
63
|
- ".github/workflows/ruby.yml"
|
64
64
|
- ".gitignore"
|
65
65
|
- ".rspec"
|
66
|
+
- CHANGELOG.md
|
66
67
|
- Gemfile
|
67
68
|
- Gemfile.lock
|
68
69
|
- LICENSE.txt
|
@@ -71,6 +72,8 @@ files:
|
|
71
72
|
- invokable.gemspec
|
72
73
|
- lib/invokable.rb
|
73
74
|
- lib/invokable/array.rb
|
75
|
+
- lib/invokable/closure.rb
|
76
|
+
- lib/invokable/command.rb
|
74
77
|
- lib/invokable/compose.rb
|
75
78
|
- lib/invokable/core.rb
|
76
79
|
- lib/invokable/data.rb
|
@@ -88,7 +91,7 @@ metadata:
|
|
88
91
|
source_code_uri: https://github.com/delonnewman/invokable
|
89
92
|
changelog_uri: https://github.com/delonnewman/invokable#changelog
|
90
93
|
documentation_uri: https://www.rubydoc.info/gems/invokable
|
91
|
-
post_install_message:
|
94
|
+
post_install_message:
|
92
95
|
rdoc_options: []
|
93
96
|
require_paths:
|
94
97
|
- lib
|
@@ -104,7 +107,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
107
|
version: '0'
|
105
108
|
requirements: []
|
106
109
|
rubygems_version: 3.0.6
|
107
|
-
signing_key:
|
110
|
+
signing_key:
|
108
111
|
specification_version: 4
|
109
112
|
summary: Objects are functions! Treat any Object, Hashes, Arrays and Sets as Procs
|
110
113
|
(like Enumerable but for Proc-like objects)
|