amenable 0.0.2 → 0.1.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/lib/amenable.rb +54 -10
- data/lib/amenable/version.rb +1 -1
- data/spec/amenable_spec.rb +88 -25
- data/spec/readme_spec.rb +21 -0
- data/spec/refinement_spec.rb +159 -0
- metadata +6 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 641c3e9528672faac363aedc32ab19e35494d4ed23b247f8a0962f02b0c53bc7
|
4
|
+
data.tar.gz: e4e39cf70b4f5b90b78c8896655c50df183c005eb59550b2556541df1d46c5a6
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: b4bafaf94f2a612bc53f1000426d56a6d42f67076fc33dd887fc53c36dca2a616080a6eb7c35f4c2a59a41b2b2b5a408b4040dce948d655954d53a275d2e9d87
|
7
|
+
data.tar.gz: d1c499194cb06d2975ccbe73ae044ad217952feac63257b310be218c0b18f0262eb7a9ade8c5d25c158bcbac154a594612a709aa9a51ac64408176c15846f6a7
|
data/lib/amenable.rb
CHANGED
@@ -4,12 +4,8 @@ module Amenable
|
|
4
4
|
extend self
|
5
5
|
|
6
6
|
def call(fn, *args, **kwargs, &block)
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
def wrap(fn)
|
11
|
-
unless fn.respond_to?(:call)
|
12
|
-
raise ArgumentError, "fn must be callable: #{fn}"
|
7
|
+
unless fn.is_a?(Method) || fn.is_a?(Proc)
|
8
|
+
raise ArgumentError, "expecting type Method or Proc, found: #{fn.class}"
|
13
9
|
end
|
14
10
|
|
15
11
|
rest = keyrest = false
|
@@ -29,12 +25,60 @@ module Amenable
|
|
29
25
|
end
|
30
26
|
end
|
31
27
|
|
32
|
-
|
33
|
-
|
34
|
-
args.slice! params.count unless rest
|
35
|
-
kwargs = kwargs.slice(*keys) unless keyrest
|
28
|
+
# remove excessive args
|
29
|
+
args = args.slice(0, params.count) unless rest
|
36
30
|
|
31
|
+
if !keys.empty? || keyrest
|
32
|
+
# remove excessive keywords
|
33
|
+
kwargs = kwargs.slice(*keys) unless keyrest
|
37
34
|
fn.call(*args, **kwargs, &block)
|
35
|
+
else
|
36
|
+
fn.call(*args, &block)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
refine Object do
|
41
|
+
def amenable(name)
|
42
|
+
# identify method and way to dynamically redefine it
|
43
|
+
if methods(false).include?(name)
|
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
|
69
|
+
end
|
70
|
+
|
71
|
+
# rebuild method with Amenable wrapper
|
72
|
+
definer.call(name) do |*args, &block|
|
73
|
+
# bind method to instance or subclass, which is now available
|
74
|
+
if fn.respond_to? :bind
|
75
|
+
fn = fn.bind(self)
|
76
|
+
end
|
77
|
+
|
78
|
+
Amenable.call(fn, *args, &block)
|
79
|
+
end
|
80
|
+
|
81
|
+
name
|
38
82
|
end
|
39
83
|
end
|
40
84
|
end
|
data/lib/amenable/version.rb
CHANGED
data/spec/amenable_spec.rb
CHANGED
@@ -1,32 +1,7 @@
|
|
1
1
|
describe Amenable do
|
2
|
-
describe '.wrap' do
|
3
|
-
let(:fn) { proc {} }
|
4
|
-
|
5
|
-
it 'requires a function-like input' do
|
6
|
-
expect { Amenable.wrap(nil) }.to raise_error(ArgumentError)
|
7
|
-
end
|
8
|
-
|
9
|
-
it 'wraps a function with a function' do
|
10
|
-
res = Amenable.wrap(fn)
|
11
|
-
expect(res).to be_a Proc
|
12
|
-
expect(res).not_to be fn
|
13
|
-
end
|
14
|
-
|
15
|
-
it 'returns a function that takes any input' do
|
16
|
-
params = Amenable.wrap(fn).parameters.map &:first
|
17
|
-
expect(params).to eq [ :rest, :keyrest, :block ]
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
2
|
describe '.call' do
|
22
3
|
let(:fn) { proc {|x, y = :y, a:, b: 2| [ [ x, y ], { a: a, b: b } ] } }
|
23
4
|
|
24
|
-
it 'calls .wrap under the hood' do
|
25
|
-
fn = proc {}
|
26
|
-
expect(Amenable).to receive(:wrap).with(fn).and_call_original
|
27
|
-
Amenable.call(fn)
|
28
|
-
end
|
29
|
-
|
30
5
|
it 'takes args and kwargs as expected' do
|
31
6
|
expect(fn).to receive(:call).with(:x, :y, a: 1, b: 2)
|
32
7
|
Amenable.call(fn, :x, :y, a: 1, b: 2)
|
@@ -115,5 +90,93 @@ describe Amenable do
|
|
115
90
|
])
|
116
91
|
end
|
117
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
|
118
181
|
end
|
119
182
|
end
|
data/spec/readme_spec.rb
ADDED
@@ -0,0 +1,21 @@
|
|
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
|
@@ -0,0 +1,159 @@
|
|
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
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: amenable
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Pepper
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-06
|
11
|
+
date: 2021-07-06 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: byebug
|
@@ -38,20 +38,6 @@ dependencies:
|
|
38
38
|
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
|
-
- !ruby/object:Gem::Dependency
|
42
|
-
name: rake
|
43
|
-
requirement: !ruby/object:Gem::Requirement
|
44
|
-
requirements:
|
45
|
-
- - ">="
|
46
|
-
- !ruby/object:Gem::Version
|
47
|
-
version: '0'
|
48
|
-
type: :development
|
49
|
-
prerelease: false
|
50
|
-
version_requirements: !ruby/object:Gem::Requirement
|
51
|
-
requirements:
|
52
|
-
- - ">="
|
53
|
-
- !ruby/object:Gem::Version
|
54
|
-
version: '0'
|
55
41
|
- !ruby/object:Gem::Dependency
|
56
42
|
name: rspec
|
57
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -89,6 +75,8 @@ files:
|
|
89
75
|
- lib/amenable.rb
|
90
76
|
- lib/amenable/version.rb
|
91
77
|
- spec/amenable_spec.rb
|
78
|
+
- spec/readme_spec.rb
|
79
|
+
- spec/refinement_spec.rb
|
92
80
|
homepage: https://github.com/dpep/amenable_rb
|
93
81
|
licenses:
|
94
82
|
- MIT
|
@@ -114,3 +102,5 @@ specification_version: 4
|
|
114
102
|
summary: Amenable
|
115
103
|
test_files:
|
116
104
|
- spec/amenable_spec.rb
|
105
|
+
- spec/refinement_spec.rb
|
106
|
+
- spec/readme_spec.rb
|