amenable 0.1.0 → 0.1.1
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 +25 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +21 -0
- data/README.md +98 -0
- data/amenable.gemspec +28 -0
- data/lib/amenable/version.rb +1 -1
- data/lib/amenable.rb +55 -39
- metadata +19 -32
- data/spec/amenable_spec.rb +0 -182
- data/spec/readme_spec.rb +0 -21
- data/spec/refinement_spec.rb +0 -159
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 0b63301072a15feda94f5c8a68ca8be4ed71ed08a4f33591c8bd50e0e70913d6
|
|
4
|
+
data.tar.gz: d066ce9fc0f6e20510a17715e68bd36ebf333b3e268775dff8d7a38fe8349892
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 8ac3cb8c04e8aab1072cd30c0116d4ae6a86784f827b10c515bec6e6c515069c62559604647668b41159b542fe7bb3f4c3ad2b483657b16924baa54c7e07faaa
|
|
7
|
+
data.tar.gz: 2e97dd44eca60f8ea2c16b59176ea9a5861e95de54a01814337fb0cded49f39b160ca6921f04d802825b4bf612d1c2065cf5bd8d4a8c51311ec8dc99730251d0
|
data/CHANGELOG.md
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
### v0.1.1 (2026-11-03)
|
|
2
|
+
- drop Ruby 3.0 and 3.1 (end of life); requires Ruby >= 3.2
|
|
3
|
+
- fix: amenable methods no longer run with the first caller's `self` (instances, classes sharing a module, subclasses calling amenable class methods, concurrent first calls)
|
|
4
|
+
- fix: amenable methods pass keyword arguments through instead of dropping them (`opt(1, b: 3)` returned the default `b`, `**opts` came back empty)
|
|
5
|
+
- fix: keywords sent to a method with no keyword params are passed through as Ruby would bind them (to `*rest` or an open positional slot, e.g. `def m(opts = {})`, and to C methods like `String#encode`) instead of being dropped; they're only stripped when Ruby would reject them
|
|
6
|
+
- fix: in a class or module body, `amenable` wraps the instance method even when a class method of the same name exists; `amenable def initialize` now works; module_function methods are wrapped on both the module and its includers
|
|
7
|
+
- fix: `amenable` at the top level, and on a plain object's private singleton method, no longer crashes
|
|
8
|
+
- fix: `amenable` with an unknown or inherited method raises NoMethodError naming the method and receiver
|
|
9
|
+
- fix: a non-lambda proc given a lone Array now auto-splats it as a direct call does (`Amenable.call(proc { |a, b = 2, k: 1| [a, b] }, [5, 1])` returned `[[5, 1], 2]`)
|
|
10
|
+
- fix: extra positionals and keywords sent to a non-lambda proc are left for the proc to drop, as a direct call does (`proc { |a,| a }` given `([5, 1], :x)` returned 5 instead of `[5, 1]`)
|
|
11
|
+
|
|
12
|
+
### v0.1.0 (2021-07-05)
|
|
13
|
+
- readme spec
|
|
14
|
+
- test coverage
|
|
15
|
+
- simplify ci
|
|
16
|
+
- remove wrapper and simplify usage around refinement or .call
|
|
17
|
+
- refinement support
|
|
18
|
+
- kwarg bugfix
|
|
19
|
+
|
|
20
|
+
### v0.0.2 (2021-06-26)
|
|
21
|
+
- upgrade to .call / .wrap
|
|
22
|
+
|
|
23
|
+
### v0.0.1 (2021-04-07)
|
|
24
|
+
- init
|
|
25
|
+
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Daniel Pepper
|
|
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,98 @@
|
|
|
1
|
+
Amenable
|
|
2
|
+
======
|
|
3
|
+
Removes excessive parameters from function calls.
|
|
4
|
+
|
|
5
|
+
The rule: amenable drops only the arguments Ruby would reject with `ArgumentError`. Any call Ruby already accepts behaves exactly as it would without amenable.
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
```ruby
|
|
9
|
+
require 'amenable'
|
|
10
|
+
|
|
11
|
+
class Dog
|
|
12
|
+
using Amenable
|
|
13
|
+
|
|
14
|
+
amenable def bark(n = 1)
|
|
15
|
+
([ :woof ] * n).join(" ")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
Dog.new.bark
|
|
21
|
+
> "woof"
|
|
22
|
+
|
|
23
|
+
Dog.new.bark(2)
|
|
24
|
+
> "woof woof"
|
|
25
|
+
|
|
26
|
+
Dog.new.bark(2, 3, 4, foo: 5)
|
|
27
|
+
> "woof woof"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
```ruby
|
|
34
|
+
gem "amenable" # requires Ruby >= 3.2
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Activate the refinement with `using Amenable`, then wrap a method as you define it (`amenable def bark ...`) or afterwards by name (`amenable :bark`). It works on instance, private, protected, and class methods (`amenable def self.build`), on `module_function` methods, and at the top level.
|
|
38
|
+
|
|
39
|
+
For a Method or Proc you already have, skip the refinement and use `Amenable.call(fn, *args, **kwargs, &block)`:
|
|
40
|
+
|
|
41
|
+
```ruby
|
|
42
|
+
Amenable.call(->(a) { a * 2 }, 3, 4, x: 1)
|
|
43
|
+
> 6
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
## Keywords
|
|
48
|
+
|
|
49
|
+
Extra keywords are dropped when the method can't take them. If it has an open positional slot, they land there as a Hash, as in plain Ruby:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
class Greeter
|
|
53
|
+
using Amenable
|
|
54
|
+
|
|
55
|
+
amenable def hello(name, greeting: "hi")
|
|
56
|
+
"#{greeting} #{name}"
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
amenable def configure(opts = {})
|
|
60
|
+
opts
|
|
61
|
+
end
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
Greeter.new.hello("bob", greeting: "yo", volume: 11)
|
|
66
|
+
> "yo bob"
|
|
67
|
+
|
|
68
|
+
Greeter.new.configure(verbose: true)
|
|
69
|
+
> {verbose: true}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Missing arguments still raise. Amenable removes; it never fills in.
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
## Limits
|
|
76
|
+
|
|
77
|
+
- Only wraps methods defined directly on the receiver, not inherited ones (raises `NoMethodError`).
|
|
78
|
+
- When a class method and an instance method share a name, `amenable def foo` in the class body wraps the instance method. Use `class << self` to wrap the class one.
|
|
79
|
+
- Wrapped methods report the wrapper's `parameters`, `arity`, and `source_location`.
|
|
80
|
+
- Curried lambdas and `Symbol#to_proc` can't be stripped: their real signature isn't visible.
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
----
|
|
84
|
+
## Contributing
|
|
85
|
+
|
|
86
|
+
Yes please :)
|
|
87
|
+
|
|
88
|
+
1. Fork it
|
|
89
|
+
1. Create your feature branch (`git checkout -b my-feature`)
|
|
90
|
+
1. Ensure the tests pass (`bundle exec rspec`)
|
|
91
|
+
1. Commit your changes (`git commit -am 'awesome new feature'`)
|
|
92
|
+
1. Push your branch (`git push origin my-feature`)
|
|
93
|
+
1. Create a Pull Request
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
----
|
|
97
|
+

|
|
98
|
+
[](https://codecov.io/gh/dpep/amenable_rb)
|
data/amenable.gemspec
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require_relative "lib/amenable/version"
|
|
2
|
+
|
|
3
|
+
Gem::Specification.new do |s|
|
|
4
|
+
s.name = 'amenable'
|
|
5
|
+
s.version = Amenable::VERSION
|
|
6
|
+
s.authors = ['Daniel Pepper']
|
|
7
|
+
s.summary = 'Amenable'
|
|
8
|
+
s.description = <<~DESCRIPTION
|
|
9
|
+
A refinement that strips excess positional and keyword arguments
|
|
10
|
+
from method calls, so callers can pass extras without raising
|
|
11
|
+
ArgumentError.
|
|
12
|
+
DESCRIPTION
|
|
13
|
+
s.homepage = "https://github.com/dpep/amenable_rb"
|
|
14
|
+
s.license = 'MIT'
|
|
15
|
+
s.files = `git ls-files * ':!:spec' ':!:CLAUDE.md'`.split("\n")
|
|
16
|
+
|
|
17
|
+
s.metadata = {
|
|
18
|
+
"changelog_uri" => "#{s.homepage}/blob/main/CHANGELOG.md",
|
|
19
|
+
"rubygems_mfa_required" => "true",
|
|
20
|
+
"source_code_uri" => s.homepage,
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
s.required_ruby_version = ">= 3.2"
|
|
24
|
+
|
|
25
|
+
s.add_development_dependency 'debug'
|
|
26
|
+
s.add_development_dependency 'rspec'
|
|
27
|
+
s.add_development_dependency 'simplecov'
|
|
28
|
+
end
|
data/lib/amenable/version.rb
CHANGED
data/lib/amenable.rb
CHANGED
|
@@ -8,7 +8,10 @@ module Amenable
|
|
|
8
8
|
raise ArgumentError, "expecting type Method or Proc, found: #{fn.class}"
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
-
rest = keyrest = false
|
|
11
|
+
rest = keyrest = nokey = false
|
|
12
|
+
# a non-lambda proc drops extra positionals itself, and trimming them can
|
|
13
|
+
# make it auto-splat a lone Array
|
|
14
|
+
loose = fn.is_a?(Proc) && !fn.lambda?
|
|
12
15
|
params = []
|
|
13
16
|
keys = []
|
|
14
17
|
|
|
@@ -22,60 +25,73 @@ module Amenable
|
|
|
22
25
|
rest = true
|
|
23
26
|
when :keyrest
|
|
24
27
|
keyrest = true
|
|
28
|
+
when :nokey
|
|
29
|
+
nokey = true
|
|
25
30
|
end
|
|
26
31
|
end
|
|
27
32
|
|
|
28
33
|
# remove excessive args
|
|
29
|
-
args = args.slice(0, params.count) unless rest
|
|
34
|
+
args = args.slice(0, params.count) unless rest || loose
|
|
30
35
|
|
|
31
|
-
if
|
|
36
|
+
if nokey
|
|
37
|
+
kwargs = {}
|
|
38
|
+
elsif !keys.empty? || keyrest
|
|
32
39
|
# remove excessive keywords
|
|
33
40
|
kwargs = kwargs.slice(*keys) unless keyrest
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
41
|
+
elsif !(rest || loose || args.size < params.count)
|
|
42
|
+
# no keyword params: Ruby binds keywords to *rest or an open positional
|
|
43
|
+
# slot as a Hash, so strip them only when there's nowhere for them to go
|
|
44
|
+
kwargs = {}
|
|
37
45
|
end
|
|
46
|
+
|
|
47
|
+
# `**{}` is not a no-op: it stops a non-lambda proc auto-splatting
|
|
48
|
+
kwargs.empty? ? fn.call(*args, &block) : fn.call(*args, **kwargs, &block)
|
|
38
49
|
end
|
|
39
50
|
|
|
40
51
|
refine Object do
|
|
41
52
|
def amenable(name)
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
fn = method(name).unbind # rebind later to support subclassing
|
|
45
|
-
definer = method(:define_singleton_method)
|
|
46
|
-
elsif private_methods(false).include?(name)
|
|
47
|
-
fn = method(name).unbind
|
|
48
|
-
definer = ->(name, &block) do
|
|
49
|
-
define_singleton_method(name, &block)
|
|
50
|
-
private_class_method(name)
|
|
51
|
-
end
|
|
52
|
-
elsif private_instance_methods(false).include?(name)
|
|
53
|
-
fn = instance_method(name)
|
|
54
|
-
definer = ->(name, &block) do
|
|
55
|
-
define_method(name, &block)
|
|
56
|
-
private(name)
|
|
57
|
-
end
|
|
58
|
-
elsif protected_instance_methods(false).include?(name)
|
|
59
|
-
fn = instance_method(name)
|
|
60
|
-
definer = ->(name, &block) do
|
|
61
|
-
define_method(name, &block)
|
|
62
|
-
protected(name)
|
|
63
|
-
end
|
|
64
|
-
elsif instance_methods(false).include?(name)
|
|
65
|
-
fn = instance_method(name)
|
|
66
|
-
definer = method(:define_method)
|
|
67
|
-
else
|
|
68
|
-
raise NoMethodError
|
|
53
|
+
defines = ->(owner) do
|
|
54
|
+
owner.method_defined?(name, false) || owner.private_method_defined?(name, false)
|
|
69
55
|
end
|
|
70
56
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
57
|
+
wrap = ->(owner) do
|
|
58
|
+
fn = owner.instance_method(name)
|
|
59
|
+
visibility =
|
|
60
|
+
if owner.private_method_defined?(name, false) then :private
|
|
61
|
+
elsif owner.protected_method_defined?(name, false) then :protected
|
|
62
|
+
else :public
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
owner.send(:define_method, name) do |*args, **kwargs, &block|
|
|
66
|
+
# bind per call: `fn` is shared by every receiver, so never reassign it
|
|
67
|
+
Amenable.call(fn.bind(self), *args, **kwargs, &block)
|
|
76
68
|
end
|
|
69
|
+
owner.send(visibility, name)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
# a top-level `def` lands on Object, not on main's singleton class
|
|
73
|
+
mod = TOPLEVEL_BINDING.receiver.equal?(self) ? Object : self
|
|
77
74
|
|
|
78
|
-
|
|
75
|
+
# in a class body, `def foo` means the instance method; only fall back
|
|
76
|
+
# to the singleton side when there isn't one
|
|
77
|
+
if mod.is_a?(Module) && defines.call(mod)
|
|
78
|
+
# module_function leaves a public copy on the singleton and makes the
|
|
79
|
+
# instance method private: wrap both. Location alone can't tell a copy
|
|
80
|
+
# from another def on the same line.
|
|
81
|
+
copy = mod.private_method_defined?(name, false) &&
|
|
82
|
+
mod.singleton_class.public_method_defined?(name, false) &&
|
|
83
|
+
mod.singleton_class.instance_method(name).source_location == mod.instance_method(name).source_location
|
|
84
|
+
|
|
85
|
+
wrap.call(mod)
|
|
86
|
+
wrap.call(mod.singleton_class) if copy
|
|
87
|
+
elsif defines.call(singleton_class)
|
|
88
|
+
wrap.call(singleton_class)
|
|
89
|
+
else
|
|
90
|
+
raise NoMethodError.new(
|
|
91
|
+
"amenable: no method '#{name}' defined directly on #{inspect}",
|
|
92
|
+
name,
|
|
93
|
+
receiver: self,
|
|
94
|
+
)
|
|
79
95
|
end
|
|
80
96
|
|
|
81
97
|
name
|
metadata
CHANGED
|
@@ -1,31 +1,16 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: amenable
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.1.
|
|
4
|
+
version: 0.1.1
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Daniel Pepper
|
|
8
|
-
autorequire:
|
|
9
8
|
bindir: bin
|
|
10
9
|
cert_chain: []
|
|
11
|
-
date:
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
12
11
|
dependencies:
|
|
13
12
|
- !ruby/object:Gem::Dependency
|
|
14
|
-
name:
|
|
15
|
-
requirement: !ruby/object:Gem::Requirement
|
|
16
|
-
requirements:
|
|
17
|
-
- - ">="
|
|
18
|
-
- !ruby/object:Gem::Version
|
|
19
|
-
version: '0'
|
|
20
|
-
type: :development
|
|
21
|
-
prerelease: false
|
|
22
|
-
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
-
requirements:
|
|
24
|
-
- - ">="
|
|
25
|
-
- !ruby/object:Gem::Version
|
|
26
|
-
version: '0'
|
|
27
|
-
- !ruby/object:Gem::Dependency
|
|
28
|
-
name: codecov
|
|
13
|
+
name: debug
|
|
29
14
|
requirement: !ruby/object:Gem::Requirement
|
|
30
15
|
requirements:
|
|
31
16
|
- - ">="
|
|
@@ -66,22 +51,28 @@ dependencies:
|
|
|
66
51
|
- - ">="
|
|
67
52
|
- !ruby/object:Gem::Version
|
|
68
53
|
version: '0'
|
|
69
|
-
description:
|
|
70
|
-
|
|
54
|
+
description: |
|
|
55
|
+
A refinement that strips excess positional and keyword arguments
|
|
56
|
+
from method calls, so callers can pass extras without raising
|
|
57
|
+
ArgumentError.
|
|
71
58
|
executables: []
|
|
72
59
|
extensions: []
|
|
73
60
|
extra_rdoc_files: []
|
|
74
61
|
files:
|
|
62
|
+
- CHANGELOG.md
|
|
63
|
+
- Gemfile
|
|
64
|
+
- LICENSE.txt
|
|
65
|
+
- README.md
|
|
66
|
+
- amenable.gemspec
|
|
75
67
|
- lib/amenable.rb
|
|
76
68
|
- lib/amenable/version.rb
|
|
77
|
-
- spec/amenable_spec.rb
|
|
78
|
-
- spec/readme_spec.rb
|
|
79
|
-
- spec/refinement_spec.rb
|
|
80
69
|
homepage: https://github.com/dpep/amenable_rb
|
|
81
70
|
licenses:
|
|
82
71
|
- MIT
|
|
83
|
-
metadata:
|
|
84
|
-
|
|
72
|
+
metadata:
|
|
73
|
+
changelog_uri: https://github.com/dpep/amenable_rb/blob/main/CHANGELOG.md
|
|
74
|
+
rubygems_mfa_required: 'true'
|
|
75
|
+
source_code_uri: https://github.com/dpep/amenable_rb
|
|
85
76
|
rdoc_options: []
|
|
86
77
|
require_paths:
|
|
87
78
|
- lib
|
|
@@ -89,18 +80,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
|
89
80
|
requirements:
|
|
90
81
|
- - ">="
|
|
91
82
|
- !ruby/object:Gem::Version
|
|
92
|
-
version: '
|
|
83
|
+
version: '3.2'
|
|
93
84
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
94
85
|
requirements:
|
|
95
86
|
- - ">="
|
|
96
87
|
- !ruby/object:Gem::Version
|
|
97
88
|
version: '0'
|
|
98
89
|
requirements: []
|
|
99
|
-
rubygems_version:
|
|
100
|
-
signing_key:
|
|
90
|
+
rubygems_version: 4.0.16
|
|
101
91
|
specification_version: 4
|
|
102
92
|
summary: Amenable
|
|
103
|
-
test_files:
|
|
104
|
-
- spec/amenable_spec.rb
|
|
105
|
-
- spec/refinement_spec.rb
|
|
106
|
-
- spec/readme_spec.rb
|
|
93
|
+
test_files: []
|
data/spec/amenable_spec.rb
DELETED
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
describe Amenable do
|
|
2
|
-
describe '.call' do
|
|
3
|
-
let(:fn) { proc {|x, y = :y, a:, b: 2| [ [ x, y ], { a: a, b: b } ] } }
|
|
4
|
-
|
|
5
|
-
it 'takes args and kwargs as expected' do
|
|
6
|
-
expect(fn).to receive(:call).with(:x, :y, a: 1, b: 2)
|
|
7
|
-
Amenable.call(fn, :x, :y, a: 1, b: 2)
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
it 'removes excessive parameters' do
|
|
11
|
-
expect(fn).to receive(:call).with(:x, :y, a: 1, b: 2)
|
|
12
|
-
Amenable.call(fn, :x, :y, :z, a: 1, b: 2, c: 3)
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
it 'works with splat operator' do
|
|
16
|
-
args = [ :x, :y, :z ]
|
|
17
|
-
kwargs = { a: 1, b: 2, c: 3 }
|
|
18
|
-
|
|
19
|
-
expect(fn).to receive(:call).with(:x, :y, a: 1, b: 2)
|
|
20
|
-
Amenable.call(fn, *args, **kwargs)
|
|
21
|
-
end
|
|
22
|
-
|
|
23
|
-
it 'works with default args' do
|
|
24
|
-
expect(fn).to receive(:call).with(:x, a: 1).and_call_original
|
|
25
|
-
|
|
26
|
-
expect(Amenable.call(fn, :x, a: 1)).to eq([
|
|
27
|
-
[ :x, :y ],
|
|
28
|
-
{ a: 1, b: 2 },
|
|
29
|
-
])
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
it 'raises if required arguments are not passed' do
|
|
33
|
-
expect {
|
|
34
|
-
Amenable.call(fn, :x)
|
|
35
|
-
}.to raise_error(ArgumentError)
|
|
36
|
-
|
|
37
|
-
expect {
|
|
38
|
-
Amenable.call(fn, :x, :y)
|
|
39
|
-
}.to raise_error(ArgumentError)
|
|
40
|
-
|
|
41
|
-
expect {
|
|
42
|
-
Amenable.call(fn, :x, :y, b: 2)
|
|
43
|
-
}.to raise_error(ArgumentError)
|
|
44
|
-
|
|
45
|
-
expect {
|
|
46
|
-
Amenable.call(fn, a: 1, b: 2)
|
|
47
|
-
}.to raise_error(ArgumentError)
|
|
48
|
-
end
|
|
49
|
-
|
|
50
|
-
context 'with var args' do
|
|
51
|
-
let(:fn) { proc {|x, *z, a:, **c| [ x, z, a, c ] } }
|
|
52
|
-
|
|
53
|
-
it 'works with just enough args' do
|
|
54
|
-
expect(fn).to receive(:call).with(:x, a: 1).and_call_original
|
|
55
|
-
|
|
56
|
-
expect(Amenable.call(fn, :x, a: 1)).to eq([
|
|
57
|
-
:x, [], 1, {},
|
|
58
|
-
])
|
|
59
|
-
end
|
|
60
|
-
|
|
61
|
-
it 'fails without enough args' do
|
|
62
|
-
expect {
|
|
63
|
-
Amenable.call(fn, :x)
|
|
64
|
-
}.to raise_error(ArgumentError)
|
|
65
|
-
|
|
66
|
-
expect {
|
|
67
|
-
Amenable.call(fn, a: 1)
|
|
68
|
-
}.to raise_error(ArgumentError)
|
|
69
|
-
end
|
|
70
|
-
|
|
71
|
-
it 'passes through all args' do
|
|
72
|
-
expect(Amenable.call(fn, :x, :y, :z, a: 1, b: 2, c: 3)).to eq([
|
|
73
|
-
:x, [ :y, :z ], 1, { b: 2, c: 3 },
|
|
74
|
-
])
|
|
75
|
-
end
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
context 'with a method' do
|
|
79
|
-
before do
|
|
80
|
-
def test_fn(x, a:, &block)
|
|
81
|
-
[ x, a, block ]
|
|
82
|
-
end
|
|
83
|
-
end
|
|
84
|
-
|
|
85
|
-
it 'works all the same' do
|
|
86
|
-
test_block = proc {}
|
|
87
|
-
|
|
88
|
-
expect(Amenable.call(method(:test_fn), :x, a: 1, &test_block)).to eq([
|
|
89
|
-
:x, 1, test_block
|
|
90
|
-
])
|
|
91
|
-
end
|
|
92
|
-
end
|
|
93
|
-
|
|
94
|
-
context 'with a method that does not take params' do
|
|
95
|
-
before do
|
|
96
|
-
def test_fn; end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
it 'works without args' do
|
|
100
|
-
expect(Amenable.call(method(:test_fn))).to be_nil
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
it 'works with args' do
|
|
104
|
-
expect(Amenable.call(method(:test_fn), :x, :y)).to be_nil
|
|
105
|
-
end
|
|
106
|
-
|
|
107
|
-
it 'works with kwargs' do
|
|
108
|
-
expect(Amenable.call(method(:test_fn), a: 1, b: 2)).to be_nil
|
|
109
|
-
end
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
context 'with a method that only takes args' do
|
|
113
|
-
before do
|
|
114
|
-
def test_fn(arg)
|
|
115
|
-
arg
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
|
-
|
|
119
|
-
it 'fails without args' do
|
|
120
|
-
expect {
|
|
121
|
-
Amenable.call(method(:test_fn))
|
|
122
|
-
}.to raise_error(ArgumentError)
|
|
123
|
-
end
|
|
124
|
-
|
|
125
|
-
it 'works with args' do
|
|
126
|
-
expect(Amenable.call(method(:test_fn), :x, :y)).to be :x
|
|
127
|
-
end
|
|
128
|
-
|
|
129
|
-
it 'works with kwargs' do
|
|
130
|
-
expect(Amenable.call(method(:test_fn), :x, a: 1, b: 2)).to be :x
|
|
131
|
-
end
|
|
132
|
-
end
|
|
133
|
-
|
|
134
|
-
context 'with a method that only takes varargs' do
|
|
135
|
-
before do
|
|
136
|
-
def test_fn(*args)
|
|
137
|
-
args
|
|
138
|
-
end
|
|
139
|
-
end
|
|
140
|
-
|
|
141
|
-
it 'works without args' do
|
|
142
|
-
expect(Amenable.call(method(:test_fn))).to eq []
|
|
143
|
-
end
|
|
144
|
-
|
|
145
|
-
it 'works with args' do
|
|
146
|
-
expect(Amenable.call(method(:test_fn), :x, :y)).to eq [ :x, :y ]
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
it 'works with kwargs' do
|
|
150
|
-
expect(Amenable.call(method(:test_fn), :x, :y, a: 1, b: 2)).to eq [ :x, :y ]
|
|
151
|
-
end
|
|
152
|
-
end
|
|
153
|
-
|
|
154
|
-
context 'with a method that only takes keywords' do
|
|
155
|
-
before do
|
|
156
|
-
def test_fn(a:)
|
|
157
|
-
a
|
|
158
|
-
end
|
|
159
|
-
end
|
|
160
|
-
|
|
161
|
-
it 'fails without args' do
|
|
162
|
-
expect {
|
|
163
|
-
Amenable.call(method(:test_fn))
|
|
164
|
-
}.to raise_error(ArgumentError)
|
|
165
|
-
end
|
|
166
|
-
|
|
167
|
-
it 'works with args' do
|
|
168
|
-
expect(Amenable.call(method(:test_fn), :x, a: 1)).to be 1
|
|
169
|
-
end
|
|
170
|
-
|
|
171
|
-
it 'works with kwargs' do
|
|
172
|
-
expect(Amenable.call(method(:test_fn), a: 1, b: 2)).to be 1
|
|
173
|
-
end
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
it 'requires a Method or Proc' do
|
|
177
|
-
expect {
|
|
178
|
-
Amenable.call(:foo)
|
|
179
|
-
}.to raise_error(ArgumentError)
|
|
180
|
-
end
|
|
181
|
-
end
|
|
182
|
-
end
|
data/spec/readme_spec.rb
DELETED
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
class Dog
|
|
2
|
-
using Amenable
|
|
3
|
-
|
|
4
|
-
amenable def bark(n = 1)
|
|
5
|
-
([ :woof ] * n).join(" ")
|
|
6
|
-
end
|
|
7
|
-
end
|
|
8
|
-
|
|
9
|
-
describe Dog do
|
|
10
|
-
it 'barks' do
|
|
11
|
-
expect(Dog.new.bark).to eq "woof"
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
it 'barks n times' do
|
|
15
|
-
expect(Dog.new.bark(2)).to eq "woof woof"
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
it 'ignores excessive parameters' do
|
|
19
|
-
expect(Dog.new.bark(2, 3, :x, a: 1)).to eq "woof woof"
|
|
20
|
-
end
|
|
21
|
-
end
|
data/spec/refinement_spec.rb
DELETED
|
@@ -1,159 +0,0 @@
|
|
|
1
|
-
class Animal
|
|
2
|
-
using Amenable
|
|
3
|
-
|
|
4
|
-
def initialize(name)
|
|
5
|
-
@name = name
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
amenable def name
|
|
9
|
-
@name
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
amenable def self.species
|
|
13
|
-
to_s.downcase
|
|
14
|
-
end
|
|
15
|
-
end
|
|
16
|
-
|
|
17
|
-
class Dog < Animal
|
|
18
|
-
using Amenable
|
|
19
|
-
|
|
20
|
-
def bark(n = 1)
|
|
21
|
-
[ :woof ] * n
|
|
22
|
-
end
|
|
23
|
-
amenable :bark
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
describe Dog do
|
|
28
|
-
let(:gino) { Dog.new('Gino') }
|
|
29
|
-
|
|
30
|
-
before do
|
|
31
|
-
expect(Amenable).to receive(:call).and_call_original
|
|
32
|
-
end
|
|
33
|
-
|
|
34
|
-
describe '#bark' do
|
|
35
|
-
it 'works with the default value' do
|
|
36
|
-
expect(gino.bark).to eq [ :woof ]
|
|
37
|
-
end
|
|
38
|
-
|
|
39
|
-
it 'accepts args' do
|
|
40
|
-
expect(gino.bark(2)).to eq [ :woof, :woof ]
|
|
41
|
-
end
|
|
42
|
-
|
|
43
|
-
it 'ignores excessive args' do
|
|
44
|
-
expect(gino.bark(2, :x, a: 4)).to eq [ :woof, :woof ]
|
|
45
|
-
end
|
|
46
|
-
end
|
|
47
|
-
|
|
48
|
-
describe '#name' do
|
|
49
|
-
it 'works without args' do
|
|
50
|
-
expect(gino.name).to eq 'Gino'
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
it 'works with any args' do
|
|
54
|
-
expect(gino.name(:x, a: 1)).to eq 'Gino'
|
|
55
|
-
end
|
|
56
|
-
end
|
|
57
|
-
|
|
58
|
-
describe '.species' do
|
|
59
|
-
it 'works without args' do
|
|
60
|
-
expect(Dog.species).to eq 'dog'
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
it 'works with any args' do
|
|
64
|
-
expect(Dog.species(:x, a: 1)).to eq 'dog'
|
|
65
|
-
end
|
|
66
|
-
end
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
class Cat < Animal
|
|
71
|
-
using Amenable
|
|
72
|
-
|
|
73
|
-
private amenable def asleep?
|
|
74
|
-
true
|
|
75
|
-
end
|
|
76
|
-
|
|
77
|
-
protected def lives
|
|
78
|
-
9
|
|
79
|
-
end
|
|
80
|
-
amenable :lives
|
|
81
|
-
|
|
82
|
-
private
|
|
83
|
-
|
|
84
|
-
amenable def nice?
|
|
85
|
-
false
|
|
86
|
-
end
|
|
87
|
-
|
|
88
|
-
private_class_method def self.agreeable?
|
|
89
|
-
false
|
|
90
|
-
end
|
|
91
|
-
amenable :agreeable?
|
|
92
|
-
|
|
93
|
-
class << self
|
|
94
|
-
private
|
|
95
|
-
|
|
96
|
-
amenable def evil?
|
|
97
|
-
true
|
|
98
|
-
end
|
|
99
|
-
end
|
|
100
|
-
end
|
|
101
|
-
|
|
102
|
-
describe Cat do
|
|
103
|
-
let(:cat) { Cat.new('Cheshire') }
|
|
104
|
-
|
|
105
|
-
describe '#lives' do
|
|
106
|
-
it 'is protected' do
|
|
107
|
-
expect(Cat.protected_instance_methods(false)).to include(:lives)
|
|
108
|
-
expect { cat.lives }.to raise_error(NoMethodError)
|
|
109
|
-
end
|
|
110
|
-
|
|
111
|
-
it 'works with any params' do
|
|
112
|
-
expect(cat.send(:lives, :x, :y, a: 1, b: 2)).to be 9
|
|
113
|
-
end
|
|
114
|
-
end
|
|
115
|
-
|
|
116
|
-
describe '#asleep?' do
|
|
117
|
-
it 'is private' do
|
|
118
|
-
expect(Cat.private_instance_methods(false)).to include(:asleep?)
|
|
119
|
-
expect { cat.asleep? }.to raise_error(NoMethodError)
|
|
120
|
-
end
|
|
121
|
-
|
|
122
|
-
it 'works with any params' do
|
|
123
|
-
expect(cat.send(:asleep?, :x, :y, a: 1, b: 2)).to be true
|
|
124
|
-
end
|
|
125
|
-
end
|
|
126
|
-
|
|
127
|
-
describe '#nice?' do
|
|
128
|
-
it 'is private' do
|
|
129
|
-
expect(Cat.private_instance_methods(false)).to include(:nice?)
|
|
130
|
-
expect { cat.nice? }.to raise_error(NoMethodError)
|
|
131
|
-
end
|
|
132
|
-
|
|
133
|
-
it 'works with any params' do
|
|
134
|
-
expect(cat.send(:nice?, :x, :y, a: 1, b: 2)).to be false
|
|
135
|
-
end
|
|
136
|
-
end
|
|
137
|
-
|
|
138
|
-
describe '.agreeable?' do
|
|
139
|
-
it 'is private' do
|
|
140
|
-
expect(Cat.private_methods(false)).to include(:agreeable?)
|
|
141
|
-
expect { Cat.agreeable? }.to raise_error(NoMethodError)
|
|
142
|
-
end
|
|
143
|
-
|
|
144
|
-
it 'works with any params' do
|
|
145
|
-
expect(Cat.send(:agreeable?, :x, :y, a: 1, b: 2)).to be false
|
|
146
|
-
end
|
|
147
|
-
end
|
|
148
|
-
|
|
149
|
-
describe '.evil?' do
|
|
150
|
-
it 'is private' do
|
|
151
|
-
expect(Cat.private_methods(false)).to include(:evil?)
|
|
152
|
-
expect { Cat.evil? }.to raise_error(NoMethodError)
|
|
153
|
-
end
|
|
154
|
-
|
|
155
|
-
it 'works with any params' do
|
|
156
|
-
expect(Cat.send(:evil?, :x, :y, a: 1, b: 2)).to be true
|
|
157
|
-
end
|
|
158
|
-
end
|
|
159
|
-
end
|