amenable 0.0.2 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 133a9c4b0a84e4325394ededc6826cb7408a9505d1c45bdfdd06674a10fac5ff
4
- data.tar.gz: 1345444593420700a2b84938c64f58343e6f33c8a6399fd955fb09cd717c74c0
3
+ metadata.gz: 641c3e9528672faac363aedc32ab19e35494d4ed23b247f8a0962f02b0c53bc7
4
+ data.tar.gz: e4e39cf70b4f5b90b78c8896655c50df183c005eb59550b2556541df1d46c5a6
5
5
  SHA512:
6
- metadata.gz: 7fcb746e4541e5360d25b5281d0b407be1b851ba97196d5bae01fec41eae898227bc87aa09dba59d91e22b5045aa14dd93d63856323805cc4944641b79df71b6
7
- data.tar.gz: 9072334b6a123882a656f07db3797064cf726fdaf7900ecddc90e808b27ca6503246c0417e1f953e4941fc9c69e2ddce1a5a04be69e245aada6a3b6cf64bccf1
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
- wrap(fn).call(*args, **kwargs, &block)
8
- end
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
- proc do |*args, **kwargs, &block|
33
- # remove unreferenced params
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
@@ -1,3 +1,3 @@
1
1
  module Amenable
2
- VERSION = '0.0.2'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -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
@@ -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.2
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-26 00:00:00.000000000 Z
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