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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 641c3e9528672faac363aedc32ab19e35494d4ed23b247f8a0962f02b0c53bc7
4
- data.tar.gz: e4e39cf70b4f5b90b78c8896655c50df183c005eb59550b2556541df1d46c5a6
3
+ metadata.gz: 0b63301072a15feda94f5c8a68ca8be4ed71ed08a4f33591c8bd50e0e70913d6
4
+ data.tar.gz: d066ce9fc0f6e20510a17715e68bd36ebf333b3e268775dff8d7a38fe8349892
5
5
  SHA512:
6
- metadata.gz: b4bafaf94f2a612bc53f1000426d56a6d42f67076fc33dd887fc53c36dca2a616080a6eb7c35f4c2a59a41b2b2b5a408b4040dce948d655954d53a275d2e9d87
7
- data.tar.gz: d1c499194cb06d2975ccbe73ae044ad217952feac63257b310be218c0b18f0262eb7a9ade8c5d25c158bcbac154a594612a709aa9a51ac64408176c15846f6a7
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
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
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
+ ![Gem](https://img.shields.io/gem/dt/amenable?style=plastic)
98
+ [![codecov](https://codecov.io/gh/dpep/amenable_rb/branch/main/graph/badge.svg)](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
@@ -1,3 +1,3 @@
1
1
  module Amenable
2
- VERSION = '0.1.0'
2
+ VERSION = '0.1.1'
3
3
  end
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 !keys.empty? || keyrest
36
+ if nokey
37
+ kwargs = {}
38
+ elsif !keys.empty? || keyrest
32
39
  # remove excessive keywords
33
40
  kwargs = kwargs.slice(*keys) unless keyrest
34
- fn.call(*args, **kwargs, &block)
35
- else
36
- fn.call(*args, &block)
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
- # 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
53
+ defines = ->(owner) do
54
+ owner.method_defined?(name, false) || owner.private_method_defined?(name, false)
69
55
  end
70
56
 
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)
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
- Amenable.call(fn, *args, &block)
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.0
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: 2021-07-06 00:00:00.000000000 Z
10
+ date: 1980-01-02 00:00:00.000000000 Z
12
11
  dependencies:
13
12
  - !ruby/object:Gem::Dependency
14
- name: byebug
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: Flexibility when you need it.
70
- email:
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
- post_install_message:
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: '0'
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: 3.0.8
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: []
@@ -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
@@ -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