duck_puncher 4.5.1 → 5.0.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/duck_puncher.gemspec +2 -2
- data/lib/duck_puncher.rb +8 -0
- data/lib/duck_puncher/duck.rb +5 -3
- data/lib/duck_puncher/ducks/object.rb +16 -7
- data/lib/duck_puncher/registration.rb +19 -7
- data/lib/duck_puncher/version.rb +1 -1
- metadata +4 -39
- data/.gitignore +0 -19
- data/.ruby-gemset +0 -1
- data/.ruby-version +0 -1
- data/.travis.yml +0 -10
- data/CHANGELOG.md +0 -31
- data/Gemfile +0 -9
- data/LICENSE.txt +0 -22
- data/README.md +0 -272
- data/Rakefile +0 -13
- data/bin/console +0 -30
- data/test/fixtures/test_classes.rb +0 -42
- data/test/fixtures/wut.rb +0 -7
- data/test/lib/duck_puncher/duck_test.rb +0 -45
- data/test/lib/duck_puncher/enumerable_test.rb +0 -64
- data/test/lib/duck_puncher/hash_test.rb +0 -23
- data/test/lib/duck_puncher/method_test.rb +0 -32
- data/test/lib/duck_puncher/module_test.rb +0 -9
- data/test/lib/duck_puncher/numeric_test.rb +0 -48
- data/test/lib/duck_puncher/object_test.rb +0 -78
- data/test/lib/duck_puncher/string_test.rb +0 -39
- data/test/lib/duck_puncher_test.rb +0 -71
- data/test/test_helper.rb +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5eef188156f1414f02333fe56d300125533d0316
|
4
|
+
data.tar.gz: d15076d2567674bcc912862412c119466d029c5b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0e82ce6e8fa66fed2a53d06e19e873fbb6d85211bbadf6c4137c46aa07396152ed1a046a966afd3d362ce1c1474f126b55c01d0ffdfc7547b57dff85c56ea830
|
7
|
+
data.tar.gz: 76415a29eac87178fb5e3be11bf3c0644cfd048ce1993f913a4c933f6455aabc8e00dcd8df557b6ea24bb47307204a8f35ecbdc17eeaca6393e580b258664c5f
|
data/duck_puncher.gemspec
CHANGED
@@ -13,9 +13,9 @@ Gem::Specification.new do |spec|
|
|
13
13
|
spec.homepage = "https://github.com/ridiculous/duck_puncher"
|
14
14
|
spec.license = "MIT"
|
15
15
|
|
16
|
-
spec.files = `git ls-files`.split($/)
|
16
|
+
spec.files = `git ls-files`.split($/).keep_if { |f| f =~ /duck_puncher/ and f !~ %r{test/} }
|
17
17
|
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
-
spec.test_files = spec.files.grep(%r{^(test
|
18
|
+
spec.test_files = spec.files.grep(%r{^(test)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.required_ruby_version = ">= 2.0.0"
|
data/lib/duck_puncher.rb
CHANGED
@@ -62,6 +62,14 @@ module DuckPuncher
|
|
62
62
|
@punched_ducks ||= Set.new
|
63
63
|
end
|
64
64
|
|
65
|
+
# Register an extension with a target class
|
66
|
+
# When given a block, the block is used to create an anonymous module
|
67
|
+
# @param target [Class,Module,Object] constant or instance to extend
|
68
|
+
# @param mods [Array<Module>] modules to extend or mix into the target. The last argument can be a hash of options to customize the extension
|
69
|
+
# @option :only [Symbol, Array<Symbol>] list of methods to extend onto the target (the module must have these defined)
|
70
|
+
# @option :method [Symbol,String] the method used to apply the module, e.g. :extend (:include)
|
71
|
+
# @option :before [Proc] A hook that is called with the target class before #punch
|
72
|
+
# @option :after [Proc] A hook that is called with the target class after #punch
|
65
73
|
def register(*)
|
66
74
|
target, *_ = super
|
67
75
|
decorators[target] = build_decorator_class(*Ducks[target])
|
data/lib/duck_puncher/duck.rb
CHANGED
@@ -5,6 +5,8 @@ module DuckPuncher
|
|
5
5
|
# @param target [String,Class] Class or module to punch
|
6
6
|
# @param mod [String,Module] The module that defines the extensions
|
7
7
|
# @param [Hash] options to modify the duck #punch method behavior
|
8
|
+
# @option options :only [Symbol, Array<Symbol>] list of methods to extend onto the target (the module must have these defined)
|
9
|
+
# @option options :method [Symbol,String] the method used to apply the module, e.g. :extend (:include)
|
8
10
|
# @option options :before [Proc] A hook that is called with the target class before +punch+
|
9
11
|
# @option options :after [Proc] A hook that is called with the target class after +punch+
|
10
12
|
def initialize(target, mod, options = {})
|
@@ -14,9 +16,9 @@ module DuckPuncher
|
|
14
16
|
end
|
15
17
|
|
16
18
|
# @param [Hash] opts to modify punch
|
17
|
-
# @option options [Class]
|
18
|
-
# @option options [Array,Symbol]
|
19
|
-
# @option options [Symbol,String]
|
19
|
+
# @option options :target [Class] Specifies the class to be punched (overrides @target)
|
20
|
+
# @option options :only [Array,Symbol] Specifies the methods to extend onto the current object
|
21
|
+
# @option options :method [Symbol,String] Specifies if the methods should be included or prepended (:include)
|
20
22
|
# @return [Class] The class that was just punched
|
21
23
|
def call(opts = {})
|
22
24
|
opts = options.merge(opts)
|
@@ -5,10 +5,17 @@ module DuckPuncher
|
|
5
5
|
Marshal.load Marshal.dump self
|
6
6
|
end unless defined? clone!
|
7
7
|
|
8
|
-
def require!(file_or_gem, version = '')
|
8
|
+
def require!(file_or_gem, version = '', patience: 1)
|
9
9
|
if DuckPuncher::GemInstaller.new.perform(file_or_gem, version)
|
10
|
-
require file_or_gem.tr('-', '/')
|
10
|
+
if require file_or_gem.tr('-', '/')
|
11
|
+
true
|
12
|
+
elsif patience > 0
|
13
|
+
sleep 0.005
|
14
|
+
require!(file_or_gem, version, patience: patience - 1)
|
15
|
+
end
|
11
16
|
end
|
17
|
+
rescue ::LoadError
|
18
|
+
require!(file_or_gem, version, patience: patience - 1) unless patience.zero?
|
12
19
|
end
|
13
20
|
|
14
21
|
# @description Returns a new decorated version of ourself with the punches mixed in (adds ancestors decorators)
|
@@ -32,15 +39,17 @@ module DuckPuncher
|
|
32
39
|
self
|
33
40
|
end
|
34
41
|
|
35
|
-
def track
|
42
|
+
def track!(patience: 1)
|
36
43
|
begin
|
37
|
-
require 'object_tracker' || raise(LoadError)
|
38
|
-
rescue LoadError
|
44
|
+
require 'object_tracker' || raise(::LoadError)
|
45
|
+
rescue ::LoadError
|
39
46
|
DuckPuncher.(Object, only: :require!) unless respond_to? :require!
|
40
47
|
require! 'object_tracker'
|
41
48
|
end
|
42
|
-
|
43
|
-
|
49
|
+
::ObjectTracker.(self)
|
50
|
+
rescue ::Exception
|
51
|
+
sleep 0.005
|
52
|
+
track!(patience: patience - 1) unless patience.zero?
|
44
53
|
end
|
45
54
|
end
|
46
55
|
end
|
@@ -1,25 +1,37 @@
|
|
1
1
|
module DuckPuncher
|
2
2
|
# @note When updating this file please update comment regarding this module in duck_puncher.rb
|
3
3
|
module Registration
|
4
|
-
|
4
|
+
# Register an extension with a target class
|
5
|
+
# When given a block, the block is used to create an anonymous module
|
6
|
+
# @param target [Class,Module,Object] constant or instance to extend
|
7
|
+
# @param mods [Array<Module>] modules to extend or mix into the target. The last argument can be a hash of options to customize the extension
|
8
|
+
# @option :only [Symbol, Array<Symbol>] list of methods to extend onto the target (the module must have these defined)
|
9
|
+
# @option :method [Symbol,String] the method used to apply the module, e.g. :extend (:include)
|
10
|
+
# @option :before [Proc] A hook that is called with the target class before #punch
|
11
|
+
# @option :after [Proc] A hook that is called with the target class after #punch
|
12
|
+
def register(target, *mods, &block)
|
5
13
|
options = mods.last.is_a?(Hash) ? mods.pop : {}
|
14
|
+
mods << Module.new(&block) if block
|
6
15
|
target = DuckPuncher.lookup_constant target
|
7
16
|
Ducks.list[target] = Set.new [] unless Ducks.list.key?(target)
|
8
17
|
mods = Array(mods).each do |mod|
|
9
|
-
duck = UniqueDuck.new Duck.new
|
18
|
+
duck = UniqueDuck.new Duck.new(target, mod, options)
|
10
19
|
Ducks.list[target] << duck
|
11
20
|
end
|
12
21
|
[target, *mods]
|
13
22
|
end
|
14
23
|
|
15
|
-
|
16
|
-
|
24
|
+
# Register an extension and then immediately activate it
|
25
|
+
# See #register for accepted arguments
|
26
|
+
def register!(*args, &block)
|
27
|
+
register *args, &block
|
17
28
|
call args.first
|
18
29
|
end
|
19
30
|
|
20
|
-
|
21
|
-
|
22
|
-
|
31
|
+
# Remove extensions for a given class or list of classes
|
32
|
+
def deregister(*targets)
|
33
|
+
targets.each &Ducks.list.method(:delete)
|
34
|
+
targets.each &decorators.method(:delete)
|
23
35
|
end
|
24
36
|
end
|
25
37
|
end
|
data/lib/duck_puncher/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: duck_puncher
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version:
|
4
|
+
version: 5.0.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Ryan Buckley
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2017-
|
11
|
+
date: 2017-09-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: usable
|
@@ -83,21 +83,10 @@ dependencies:
|
|
83
83
|
description: Administer precision punches
|
84
84
|
email:
|
85
85
|
- arebuckley@gmail.com
|
86
|
-
executables:
|
87
|
-
- console
|
86
|
+
executables: []
|
88
87
|
extensions: []
|
89
88
|
extra_rdoc_files: []
|
90
89
|
files:
|
91
|
-
- ".gitignore"
|
92
|
-
- ".ruby-gemset"
|
93
|
-
- ".ruby-version"
|
94
|
-
- ".travis.yml"
|
95
|
-
- CHANGELOG.md
|
96
|
-
- Gemfile
|
97
|
-
- LICENSE.txt
|
98
|
-
- README.md
|
99
|
-
- Rakefile
|
100
|
-
- bin/console
|
101
90
|
- duck_puncher.gemspec
|
102
91
|
- lib/duck_puncher.rb
|
103
92
|
- lib/duck_puncher/ancestral_hash.rb
|
@@ -119,18 +108,6 @@ files:
|
|
119
108
|
- lib/duck_puncher/unique_duck.rb
|
120
109
|
- lib/duck_puncher/utilities.rb
|
121
110
|
- lib/duck_puncher/version.rb
|
122
|
-
- test/fixtures/test_classes.rb
|
123
|
-
- test/fixtures/wut.rb
|
124
|
-
- test/lib/duck_puncher/duck_test.rb
|
125
|
-
- test/lib/duck_puncher/enumerable_test.rb
|
126
|
-
- test/lib/duck_puncher/hash_test.rb
|
127
|
-
- test/lib/duck_puncher/method_test.rb
|
128
|
-
- test/lib/duck_puncher/module_test.rb
|
129
|
-
- test/lib/duck_puncher/numeric_test.rb
|
130
|
-
- test/lib/duck_puncher/object_test.rb
|
131
|
-
- test/lib/duck_puncher/string_test.rb
|
132
|
-
- test/lib/duck_puncher_test.rb
|
133
|
-
- test/test_helper.rb
|
134
111
|
homepage: https://github.com/ridiculous/duck_puncher
|
135
112
|
licenses:
|
136
113
|
- MIT
|
@@ -155,16 +132,4 @@ rubygems_version: 2.5.1
|
|
155
132
|
signing_key:
|
156
133
|
specification_version: 4
|
157
134
|
summary: Administer precision extensions (a.k.a "punches") to your favorite Ruby classes
|
158
|
-
test_files:
|
159
|
-
- test/fixtures/test_classes.rb
|
160
|
-
- test/fixtures/wut.rb
|
161
|
-
- test/lib/duck_puncher/duck_test.rb
|
162
|
-
- test/lib/duck_puncher/enumerable_test.rb
|
163
|
-
- test/lib/duck_puncher/hash_test.rb
|
164
|
-
- test/lib/duck_puncher/method_test.rb
|
165
|
-
- test/lib/duck_puncher/module_test.rb
|
166
|
-
- test/lib/duck_puncher/numeric_test.rb
|
167
|
-
- test/lib/duck_puncher/object_test.rb
|
168
|
-
- test/lib/duck_puncher/string_test.rb
|
169
|
-
- test/lib/duck_puncher_test.rb
|
170
|
-
- test/test_helper.rb
|
135
|
+
test_files: []
|
data/.gitignore
DELETED
data/.ruby-gemset
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
duck_puncher
|
data/.ruby-version
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
ruby-2.3.0
|
data/.travis.yml
DELETED
data/CHANGELOG.md
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
4.5.1 (01/19/2016)
|
2
|
-
==================
|
3
|
-
|
4
|
-
* Fix `ActiveRecord#associations` for Rails 4
|
5
|
-
|
6
|
-
4.5.0 (01/19/2016)
|
7
|
-
==================
|
8
|
-
|
9
|
-
* Fix `ActiveRecord#associations` to use LIMIT 1 for has_many associations
|
10
|
-
* Remove redundant `ActiveRecord.latest`
|
11
|
-
|
12
|
-
4.4.2 (01/10/2016)
|
13
|
-
==================
|
14
|
-
|
15
|
-
* Fix glaring bug in Duck class where the `:only` option is ignored (since 4.4.0)
|
16
|
-
|
17
|
-
4.4.1 (12/19/2016)
|
18
|
-
==================
|
19
|
-
|
20
|
-
* Lazy load the rubygems/dependency_installer only when `require!` is called
|
21
|
-
|
22
|
-
4.4.0 (12/4/2016)
|
23
|
-
=================
|
24
|
-
|
25
|
-
* Target objects are no longer extended with the [Usable](https://github.com/ridiculous/usable) module
|
26
|
-
|
27
|
-
4.3.0 (10/10/2016)
|
28
|
-
==================
|
29
|
-
|
30
|
-
* Fix issue with not being able to punch the same duck with different options
|
31
|
-
* Add the `:target` option to `.call` to override the receiving class
|
data/Gemfile
DELETED
data/LICENSE.txt
DELETED
@@ -1,22 +0,0 @@
|
|
1
|
-
Copyright (c) 2016 Ryan Buckley
|
2
|
-
|
3
|
-
MIT License
|
4
|
-
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
-
a copy of this software and associated documentation files (the
|
7
|
-
"Software"), to deal in the Software without restriction, including
|
8
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
-
permit persons to whom the Software is furnished to do so, subject to
|
11
|
-
the following conditions:
|
12
|
-
|
13
|
-
The above copyright notice and this permission notice shall be
|
14
|
-
included in all copies or substantial portions of the Software.
|
15
|
-
|
16
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
-
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
-
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
-
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
-
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
DELETED
@@ -1,272 +0,0 @@
|
|
1
|
-
# DuckPuncher [](http://badge.fury.io/rb/duck_puncher) [](https://travis-ci.org/ridiculous/duck_puncher) [](https://codeclimate.com/github/ridiculous/duck_puncher)
|
2
|
-
|
3
|
-
DuckPuncher provides an interface for administering __duck punches__ (a.k.a "monkey patches"). Punches can be applied permanently via an extension
|
4
|
-
or temporarily as a decorator. Decorator classes are generated when an extension is registered and used via `Object#punch`. The object is wrapped
|
5
|
-
in one decorator for each of the object's ancestors (with registered punches) and behaves much like it's extended cousin, `Object.punch!`.
|
6
|
-
|
7
|
-
## Install
|
8
|
-
|
9
|
-
```ruby
|
10
|
-
gem 'duck_puncher', '~> 4.3'
|
11
|
-
```
|
12
|
-
|
13
|
-
## Usage
|
14
|
-
|
15
|
-
Punch all registered ducks:
|
16
|
-
|
17
|
-
```ruby
|
18
|
-
DuckPuncher.()
|
19
|
-
```
|
20
|
-
|
21
|
-
Punch individual ducks by name:
|
22
|
-
|
23
|
-
```ruby
|
24
|
-
DuckPuncher.(Hash, Object)
|
25
|
-
```
|
26
|
-
|
27
|
-
Add `punch` as a proxy method to all punches:
|
28
|
-
|
29
|
-
```ruby
|
30
|
-
DuckPuncher.(Object, only: :punch)
|
31
|
-
```
|
32
|
-
|
33
|
-
Redirect the punches registered for a class to another target:
|
34
|
-
|
35
|
-
```ruby
|
36
|
-
DuckPuncher.(Object, target: String)
|
37
|
-
```
|
38
|
-
|
39
|
-
### Tactical punches
|
40
|
-
|
41
|
-
`DuckPuncher` extends the amazing [Usable](https://github.com/ridiculous/usable) gem, so you can configure only the punches you want! For instance:
|
42
|
-
|
43
|
-
```ruby
|
44
|
-
DuckPuncher.(Numeric, only: [:to_currency, :to_duration])
|
45
|
-
```
|
46
|
-
|
47
|
-
If you punch `Object` then you can use `#punch!` on any object to extend individual instances:
|
48
|
-
|
49
|
-
```ruby
|
50
|
-
>> DuckPuncher.(Object, only: :punch!)
|
51
|
-
>> %w[yes no 1].punch!.m!(:punch).m(:to_boolean)
|
52
|
-
=> [true, false, true]
|
53
|
-
```
|
54
|
-
|
55
|
-
Alternatively, there is also the `Object#punch` method which returns a decorated copy of an object with punches mixed in:
|
56
|
-
```ruby
|
57
|
-
>> DuckPuncher.(Object, only: :punch)
|
58
|
-
>> %w[1 2 3].punch.m(:to_i)
|
59
|
-
=> [1, 2, 3]
|
60
|
-
```
|
61
|
-
|
62
|
-
The `#punch!` method will lookup the extension by the object's class name. The above example works because `Array` and `String` are default extensions. If you want to punch a specific extension, then you can specify it as an argument:
|
63
|
-
```ruby
|
64
|
-
>> LovableDuck = Module.new { def inspect() "I love #{self.first}" end }
|
65
|
-
>> DuckPuncher.register Array, LovableDuck
|
66
|
-
>> ducks = %w[ducks]
|
67
|
-
>> soft_punch = ducks.punch
|
68
|
-
=> "I love ducks"
|
69
|
-
>> soft_punch.class
|
70
|
-
=> DuckPuncher::ArrayDelegator
|
71
|
-
>> ducks.punch!.class
|
72
|
-
=> Array
|
73
|
-
```
|
74
|
-
|
75
|
-
When there are no punches registered for a class, it'll search the ancestor list for a class with registered punches. For example, `Array` doesn't have
|
76
|
-
a method defined `echo`, but when we punch `Object`, it means all subclasses have access to the same methods, even with soft punches.
|
77
|
-
|
78
|
-
```ruby
|
79
|
-
>> DuckPuncher.(Object, only: [:punch, :punch!])
|
80
|
-
>> def soft_punch() ('a'..'z').punch.echo(1).map(&:upcase) end
|
81
|
-
>> def hard_punch() ('a'..'z').punch!.echo(1).mm(:*, 3) end
|
82
|
-
>> soft_punch
|
83
|
-
"a".."z"
|
84
|
-
* /.../delegate.rb:85:in `method_missing'
|
85
|
-
=> ["A", "B", "C", "D", "E", "F", "G", "H", "I", ...]
|
86
|
-
>> hard_punch
|
87
|
-
"a".."z"
|
88
|
-
* (irb):11:in `hard_punch'
|
89
|
-
=> ["AAA", "BBB", "CCC", "DDDD", ...]
|
90
|
-
```
|
91
|
-
|
92
|
-
## Default extensions
|
93
|
-
|
94
|
-
#### Enumerable (including Array, Set, Range, and Enumerator)
|
95
|
-
```ruby
|
96
|
-
[].m(:to_s)
|
97
|
-
[].m!(:upcase)
|
98
|
-
[].mm(:sub, /[aeiou]/, '*')
|
99
|
-
[].mm!(:sub, /[aeiou]/, '*')
|
100
|
-
[].except(:foo, :bar, :baz)
|
101
|
-
[].map_keys(:id)
|
102
|
-
```
|
103
|
-
|
104
|
-
#### Hash
|
105
|
-
```ruby
|
106
|
-
{ a: 1, b: { c: 2 }}.dig(:b, :c) # => 2
|
107
|
-
# ii Standard in Ruby >= 2.3
|
108
|
-
{ a: 1, b: nil }.compact # => {a: 1}
|
109
|
-
# !! destructive
|
110
|
-
```
|
111
|
-
|
112
|
-
#### Numeric
|
113
|
-
```ruby
|
114
|
-
25.245.to_currency # => "25.25"
|
115
|
-
10_000.to_duration # => "2 h 46 min"
|
116
|
-
10_000.to_time_ago # => "2 hours ago"
|
117
|
-
10.15.to_rad # => "0.17715091907742445"
|
118
|
-
```
|
119
|
-
|
120
|
-
#### String
|
121
|
-
```ruby
|
122
|
-
'hour'.pluralize(2) # => "hours"
|
123
|
-
'DJ::JSONStorage'.underscore # => "dj/json_storage"
|
124
|
-
'true'.to_boolean # => "true"
|
125
|
-
'MiniTest::Test'.constantize # => MiniTest::Test
|
126
|
-
```
|
127
|
-
|
128
|
-
#### Module
|
129
|
-
```ruby
|
130
|
-
Kernel.local_methods # => methods defined directly in the class + nested constants w/ methods
|
131
|
-
```
|
132
|
-
|
133
|
-
#### Object
|
134
|
-
```ruby
|
135
|
-
Object.new.clone! # => a deep clone of the object (using Marshal.dump)
|
136
|
-
Object.new.punch # => a copy of Object.new with String punches mixed in
|
137
|
-
Object.new.punch! # => destructive version applies extensions directly to the base object
|
138
|
-
Object.new.echo # => prints and returns itself. Accepts a number,
|
139
|
-
# indicating how many lines of the trace to display
|
140
|
-
Object.new.track # => Trace methods calls to the object
|
141
|
-
# !! requires [object_tracker](https://github.com/ridiculous/object_tracker), which it'll try to download
|
142
|
-
```
|
143
|
-
|
144
|
-
#### Method
|
145
|
-
```ruby
|
146
|
-
require 'benchmark'
|
147
|
-
|
148
|
-
Benchmark.method(:measure).to_instruct # => the Ruby VM instruction sequence for the method
|
149
|
-
Benchmark.method(:measure).to_source # => the method definition as a string
|
150
|
-
```
|
151
|
-
|
152
|
-
## Registering custom punches
|
153
|
-
|
154
|
-
DuckPuncher allows you to utilize the `punch` and `punch!` interface to __decorate__ or __extend__, respectively, any object with your own punches. Simply
|
155
|
-
call `DuckPuncher.register` with the name of your module (or an array of names) and any of
|
156
|
-
[these options](https://github.com/ridiculous/duck_puncher/blob/master/lib/duck_puncher/duck.rb#L10).
|
157
|
-
|
158
|
-
```ruby
|
159
|
-
DuckPuncher.register <class>, [<module>, ...]
|
160
|
-
```
|
161
|
-
|
162
|
-
A full example:
|
163
|
-
```ruby
|
164
|
-
# Define some extensions
|
165
|
-
module Billable
|
166
|
-
def call(amt)
|
167
|
-
puts "Attempting to bill #{name} for $#{amt}"
|
168
|
-
fail Errno::ENOENT
|
169
|
-
end
|
170
|
-
end
|
171
|
-
|
172
|
-
module Retryable
|
173
|
-
def call_with_retry(*args, retries: 3)
|
174
|
-
call *args
|
175
|
-
rescue Errno::ENOENT
|
176
|
-
puts 'retrying'
|
177
|
-
retry if (retries -= 1) > 0
|
178
|
-
end
|
179
|
-
end
|
180
|
-
```
|
181
|
-
|
182
|
-
```ruby
|
183
|
-
# Our duck
|
184
|
-
class User < Struct.new(:name)
|
185
|
-
end
|
186
|
-
|
187
|
-
# Register the extensions
|
188
|
-
DuckPuncher.register User, Billable, Retryable
|
189
|
-
|
190
|
-
# Add the #punch method to User instances
|
191
|
-
DuckPuncher.(Object, only: :punch)
|
192
|
-
|
193
|
-
# Usage
|
194
|
-
user = User.new('Ryan').punch
|
195
|
-
user.call_with_retry(19.99)
|
196
|
-
```
|
197
|
-
|
198
|
-
To register the extension _and_ punch the class, use `DuckPuncher.register!`
|
199
|
-
|
200
|
-
## Logging
|
201
|
-
|
202
|
-
Get notified of all punches/extensions by changing the logger level:
|
203
|
-
|
204
|
-
```ruby
|
205
|
-
DuckPuncher.logger.level = Logger::INFO
|
206
|
-
```
|
207
|
-
|
208
|
-
The default log level is `DEBUG`
|
209
|
-
|
210
|
-
## Experimental
|
211
|
-
|
212
|
-
__Object#require!__ will try to require a gem, or, if it's not found, then _download_ it! It will also keep track of any
|
213
|
-
downloaded gems and load them for subsequent IRB/rails console sessions. Gems are _not_
|
214
|
-
saved to the Gemfile.
|
215
|
-
|
216
|
-
In the wild:
|
217
|
-
|
218
|
-
```bash
|
219
|
-
>> `require 'pry'`
|
220
|
-
LoadError: cannot load such file -- pry
|
221
|
-
from (irb):1:in `require'
|
222
|
-
from (irb):1
|
223
|
-
from bin/console:10:in `<main>'
|
224
|
-
>> DuckPuncher.(Object, only: :require!)
|
225
|
-
=> nil
|
226
|
-
>> require! 'pry'
|
227
|
-
Fetching: method_source-0.8.2.gem (100%)
|
228
|
-
Fetching: slop-3.6.0.gem (100%)
|
229
|
-
Fetching: coderay-1.1.0.gem (100%)
|
230
|
-
Fetching: pry-0.10.3.gem (100%)
|
231
|
-
=> true
|
232
|
-
>> Pry.start
|
233
|
-
[1] pry(main)>
|
234
|
-
```
|
235
|
-
|
236
|
-
Perfect! Mostly ... although, it doesn't work well with bigger gems or those with native extensions ¯\\\_(ツ)_/¯
|
237
|
-
|
238
|
-
__Object#track__ builds upon `require!` to download the [ObjectTracker](https://github.com/ridiculous/object_tracker) gem,
|
239
|
-
if it's not available in the current load path, and starts tracking the current object!
|
240
|
-
|
241
|
-
```ruby
|
242
|
-
Duck = Class.new
|
243
|
-
Donald = Module.new { def tap_tap() self end }
|
244
|
-
DuckPuncher.(:Object, only: :track)
|
245
|
-
Donald.track
|
246
|
-
Duck.track
|
247
|
-
>> Duck.usable Donald, only: :tap_tap
|
248
|
-
# => * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00002)
|
249
|
-
# => * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00001)
|
250
|
-
# => * called "Donald.respond_to?" with to_ary, true [RUBY CORE] (0.00001)
|
251
|
-
# => * called "Donald.to_s" [RUBY CORE] (0.00001)
|
252
|
-
# => * called "Duck.usable_config" [ruby-2.3.0@duck_puncher/gems/usable-1.2.0/lib/usable.rb:10] (0.00002)
|
253
|
-
# => * called "Duck.usable_config" [ruby-2.3.0@duck_puncher/gems/usable-1.2.0/lib/usable.rb:10] (0.00001)
|
254
|
-
# => * called "Donald.const_defined?" with UsableSpec [RUBY CORE] (0.00001)
|
255
|
-
# => * called "Donald.dup" [RUBY CORE] (0.00002)
|
256
|
-
# => * called "Donald.name" [RUBY CORE] (0.00000)
|
257
|
-
# => * called "Donald.instance_methods" [RUBY CORE] (0.00001)
|
258
|
-
# => * called "Duck.const_defined?" with DonaldUsed [RUBY CORE] (0.00001)
|
259
|
-
# => ...
|
260
|
-
```
|
261
|
-
|
262
|
-
## Contributing
|
263
|
-
|
264
|
-
* Fork it
|
265
|
-
* Run tests with `rake`
|
266
|
-
* Start an IRB console that already has all your ducks in a row: `bin/console`
|
267
|
-
* Start an IRB console without punching ducks: `PUNCH=no bin/console`
|
268
|
-
* Make changes and submit a PR to [https://github.com/ridiculous/duck_puncher](https://github.com/ridiculous/duck_puncher)
|
269
|
-
|
270
|
-
## License
|
271
|
-
|
272
|
-
MIT
|
data/Rakefile
DELETED
@@ -1,13 +0,0 @@
|
|
1
|
-
require 'bundler/gem_tasks'
|
2
|
-
require 'rake'
|
3
|
-
require 'rake/testtask'
|
4
|
-
|
5
|
-
root = Pathname.new File.expand_path('..', __FILE__)
|
6
|
-
tasks = []
|
7
|
-
Dir[root.join('test/lib/**/*_test.rb')].each do |file|
|
8
|
-
tasks << Rake::TestTask.new(file.split('/').last[/\w+/].to_sym) do |t|
|
9
|
-
t.pattern = file.sub(root.to_s + '/', '')
|
10
|
-
end
|
11
|
-
end
|
12
|
-
|
13
|
-
task default: tasks.map(&:name)
|
data/bin/console
DELETED
@@ -1,30 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
require 'bundler/setup'
|
4
|
-
require 'pp'
|
5
|
-
require 'irb'
|
6
|
-
require 'byebug'
|
7
|
-
|
8
|
-
# Stub out Rails
|
9
|
-
module Rails
|
10
|
-
module VERSION
|
11
|
-
MAJOR = 4
|
12
|
-
end
|
13
|
-
end
|
14
|
-
|
15
|
-
# And AR
|
16
|
-
module ActiveRecord
|
17
|
-
class Base
|
18
|
-
end
|
19
|
-
end
|
20
|
-
|
21
|
-
require 'duck_puncher'
|
22
|
-
require_relative '../test/fixtures/wut'
|
23
|
-
|
24
|
-
DuckPuncher.logger.level = Logger::DEBUG
|
25
|
-
|
26
|
-
if ENV['PUNCH'] != 'no'
|
27
|
-
DuckPuncher.()
|
28
|
-
end
|
29
|
-
|
30
|
-
IRB.start
|
@@ -1,42 +0,0 @@
|
|
1
|
-
module CustomPunch
|
2
|
-
def talk
|
3
|
-
p self
|
4
|
-
self
|
5
|
-
end
|
6
|
-
end
|
7
|
-
|
8
|
-
module CustomPunch2
|
9
|
-
def quack
|
10
|
-
'quack'
|
11
|
-
end
|
12
|
-
end
|
13
|
-
|
14
|
-
module CustomPunch3
|
15
|
-
def wobble
|
16
|
-
end
|
17
|
-
end
|
18
|
-
|
19
|
-
module ModWithOverride
|
20
|
-
def talk
|
21
|
-
'talk is cheap'
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
module ModWithNestedMod
|
26
|
-
def instance_method_1
|
27
|
-
end
|
28
|
-
|
29
|
-
module ClassMethods
|
30
|
-
def class_method_1
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
|
35
|
-
class Animal
|
36
|
-
end
|
37
|
-
|
38
|
-
class Dog < Animal
|
39
|
-
end
|
40
|
-
|
41
|
-
class Kaia < Dog
|
42
|
-
end
|
data/test/fixtures/wut.rb
DELETED
@@ -1,45 +0,0 @@
|
|
1
|
-
require_relative '../../test_helper'
|
2
|
-
|
3
|
-
class DuckTest < MiniTest::Test
|
4
|
-
def setup
|
5
|
-
@class = Class.new
|
6
|
-
@object = @class.new
|
7
|
-
@mod = Module.new { %w[foo bar baz].each { |x| define_method(x, -> {}) } }
|
8
|
-
@subject = DuckPuncher::Duck.new(@class, @mod)
|
9
|
-
end
|
10
|
-
|
11
|
-
def test_punch
|
12
|
-
@subject.target = Kaia
|
13
|
-
refute_respond_to Kaia.new, :baz
|
14
|
-
@subject.call
|
15
|
-
assert_respond_to Kaia.new, :foo
|
16
|
-
assert_respond_to Kaia.new, :bar
|
17
|
-
assert_respond_to Kaia.new, :baz
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_punch_with_instance
|
21
|
-
e = assert_raises ArgumentError do
|
22
|
-
@subject.call target: @object
|
23
|
-
end
|
24
|
-
assert_match /Invalid target #<#{@class}:.*>\. Please pass a module as :target/,
|
25
|
-
e.message
|
26
|
-
end
|
27
|
-
|
28
|
-
def test_punch_with_only
|
29
|
-
refute_respond_to @object, :foo
|
30
|
-
refute_respond_to @object, :bar
|
31
|
-
@subject.call(only: :foo)
|
32
|
-
refute_respond_to @object, :bar
|
33
|
-
assert_respond_to @object, :foo
|
34
|
-
@subject.call(only: :bar)
|
35
|
-
assert_respond_to @object, :bar
|
36
|
-
end
|
37
|
-
|
38
|
-
def test_punch_with_only_target
|
39
|
-
refute_respond_to @object, :bar
|
40
|
-
@subject.call target: @class, only: [:foo, :bar]
|
41
|
-
assert_respond_to @object, :bar
|
42
|
-
assert_respond_to @object, :foo
|
43
|
-
refute_respond_to @object, :baz
|
44
|
-
end
|
45
|
-
end
|
@@ -1,64 +0,0 @@
|
|
1
|
-
require_relative '../../test_helper'
|
2
|
-
|
3
|
-
DuckPuncher.punch! Object
|
4
|
-
|
5
|
-
class EnumerableTest < MiniTest::Test
|
6
|
-
attr_reader :subject
|
7
|
-
|
8
|
-
def setup
|
9
|
-
@subject = ('a'..'m').to_a
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_m
|
13
|
-
subject.punch!
|
14
|
-
assert_equal subject.map(&:upcase), subject.m(:upcase)
|
15
|
-
refute_equal subject.object_id, subject.m(:upcase).object_id
|
16
|
-
assert_equal subject.map!(&:upcase), subject.m!(:upcase)
|
17
|
-
assert_equal subject.object_id, subject.m!(:upcase).object_id
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_m_with_range
|
21
|
-
@subject = ('a'..'f')
|
22
|
-
assert_equal %w[A B C D E F], @subject.punch.m(:upcase)
|
23
|
-
assert_equal %w[A B C D E F], @subject.punch!.m(:upcase)
|
24
|
-
end
|
25
|
-
|
26
|
-
def test_m_with_enum
|
27
|
-
@subject = ('A'..'F').to_enum
|
28
|
-
assert_equal %w[B C D E F G], @subject.punch.m(:next)
|
29
|
-
assert_equal %w[B C D E F G], @subject.punch!.m(:next)
|
30
|
-
end
|
31
|
-
|
32
|
-
def test_mm_with_set
|
33
|
-
@subject = Set.new %w[a b c d e f]
|
34
|
-
assert_equal %w[A B C D E F], @subject.punch.m(:upcase)
|
35
|
-
assert_equal %w[A B C D E F], @subject.punch!.m(:upcase)
|
36
|
-
assert_equal %w[B C D E F G], @subject.punch!.m!(:upcase).m(:next)
|
37
|
-
end
|
38
|
-
|
39
|
-
def test_mm_with_two_args
|
40
|
-
subject.punch!
|
41
|
-
assert_equal subject.map { |x| x.prepend('btn-') }, subject.mm(:prepend, 'btn-')
|
42
|
-
refute_equal subject.object_id, subject.mm(:prepend, 'btn-')
|
43
|
-
assert_equal subject.map! { |x| x.prepend('btn-') }, subject.mm!(:prepend, 'btn-')
|
44
|
-
assert_equal subject.object_id, subject.mm!(:prepend, 'btn-').object_id
|
45
|
-
end
|
46
|
-
|
47
|
-
def test_mm_with_three_args
|
48
|
-
@subject = @subject.punch
|
49
|
-
assert_equal subject.map { |x| x.sub(/[aeiou]/, '*') }, subject.mm(:sub, /[aeiou]/, '*')
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_except
|
53
|
-
@subject = @subject.punch
|
54
|
-
assert_equal subject.except('a'), %w[b c d e f g h i j k l m]
|
55
|
-
assert_equal subject.except('a', 'b', 'c'), %w[d e f g h i j k l m]
|
56
|
-
assert_equal subject.except, subject
|
57
|
-
end
|
58
|
-
|
59
|
-
def test_map_keys
|
60
|
-
@subject = [{ id: 1, name: 'a' }, { id: 2, name: 'b' }, { name: 'c' }]
|
61
|
-
assert_respond_to @subject.punch, :map_keys
|
62
|
-
assert_equal %w[a b c], @subject.punch.map_keys(:name)
|
63
|
-
end
|
64
|
-
end
|
@@ -1,23 +0,0 @@
|
|
1
|
-
require_relative '../../test_helper'
|
2
|
-
|
3
|
-
DuckPuncher.punch! Hash
|
4
|
-
|
5
|
-
class HashTest < MiniTest::Test
|
6
|
-
def setup
|
7
|
-
@subject = { a: 1, b: { c: 2 } }
|
8
|
-
end
|
9
|
-
|
10
|
-
def test_dig
|
11
|
-
assert_equal @subject.dig(:a), 1
|
12
|
-
assert_equal @subject.dig(:b, :a), nil
|
13
|
-
assert_equal @subject.dig(:b, :c), 2
|
14
|
-
assert_equal @subject.dig(:b), { c: 2 }
|
15
|
-
end
|
16
|
-
|
17
|
-
def test_compact
|
18
|
-
assert_equal @subject.compact, { a: 1, b: { c: 2 } }
|
19
|
-
@subject[:b] = nil
|
20
|
-
assert_equal @subject, { a: 1, b: nil }
|
21
|
-
assert_equal @subject.compact, { a: 1 }
|
22
|
-
end
|
23
|
-
end
|
@@ -1,32 +0,0 @@
|
|
1
|
-
require_relative '../../test_helper'
|
2
|
-
require_relative '../../fixtures/wut'
|
3
|
-
DuckPuncher.punch! Method
|
4
|
-
|
5
|
-
class MethodTest < MiniTest::Test
|
6
|
-
|
7
|
-
# Called before every test method runs. Can be used
|
8
|
-
# to set up fixture information.
|
9
|
-
def setup
|
10
|
-
@subject = Wut.new
|
11
|
-
end
|
12
|
-
|
13
|
-
def test_to_instruct
|
14
|
-
assert_match /:to_a/, @subject.method(:to_a).to_instruct
|
15
|
-
assert_match /newarray/, @subject.method(:to_a).to_instruct
|
16
|
-
assert_match /opt_plus/, @subject.method(:to_a).to_instruct
|
17
|
-
assert_equal nil, [].method(:to_s).to_instruct
|
18
|
-
end
|
19
|
-
|
20
|
-
def test_to_instruct_single_line
|
21
|
-
assert_match /:to_f/, @subject.method(:to_f).to_instruct
|
22
|
-
assert_match /getconstant\s*:INFINITY/, @subject.method(:to_f).to_instruct
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_to_source
|
26
|
-
assert_equal "def to_a\n ['w'] + ['u'] + ['t']\nend\n", @subject.method(:to_a).to_source
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_to_source_with_no_source
|
30
|
-
assert_equal '', @subject.method(:object_id).to_source
|
31
|
-
end
|
32
|
-
end
|
@@ -1,48 +0,0 @@
|
|
1
|
-
require_relative '../../test_helper'
|
2
|
-
DuckPuncher.(Numeric, String)
|
3
|
-
|
4
|
-
class NumericTest < MiniTest::Test
|
5
|
-
|
6
|
-
def test_to_currency
|
7
|
-
assert_equal '0.00', 0.to_currency
|
8
|
-
assert_equal '25.00', 25.to_currency
|
9
|
-
assert_equal '25.20', 25.2.to_currency
|
10
|
-
assert_equal '25.25', 25.245.to_currency
|
11
|
-
assert_equal '$25.25', 25.245.to_currency('$')
|
12
|
-
end
|
13
|
-
|
14
|
-
def test_to_duration
|
15
|
-
assert_equal '', 10.to_duration
|
16
|
-
assert_equal '1 min', 100.to_duration
|
17
|
-
assert_equal '16 min', 1_000.to_duration
|
18
|
-
assert_equal '2 h 46 min', 10_000.to_duration
|
19
|
-
assert_equal '27 h 46 min', 100_000.to_duration
|
20
|
-
end
|
21
|
-
|
22
|
-
def test_to_duration_with_seconds
|
23
|
-
assert_equal '10 s', 10.to_duration(true)
|
24
|
-
assert_equal '1 min 40 s', 100.to_duration(true)
|
25
|
-
assert_equal '16 min 40 s', 1_000.to_duration(true)
|
26
|
-
assert_equal '2 h 46 min', 10_000.to_duration(true)
|
27
|
-
end
|
28
|
-
|
29
|
-
def test_to_rad
|
30
|
-
assert_equal 0.0, 0.to_rad
|
31
|
-
assert_equal 0.17715091907742445, 10.15.to_rad
|
32
|
-
assert_equal 0.36035409894869713, 20.646769.to_rad
|
33
|
-
assert_equal -2.730392366234936, -156.439959.to_rad
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_to_time_ago
|
37
|
-
assert_equal 'less than a minute ago', 10.to_time_ago
|
38
|
-
assert_equal '1 minute ago', 100.to_time_ago
|
39
|
-
assert_equal '2 minutes ago', 130.to_time_ago
|
40
|
-
assert_equal '16 minutes ago', 1_000.to_time_ago
|
41
|
-
assert_equal '1 hour ago', 3_600.to_time_ago
|
42
|
-
assert_equal '1 hour ago', 4_600.to_time_ago
|
43
|
-
assert_equal '2 hours ago', 7_300.to_time_ago
|
44
|
-
assert_equal '1 day ago', 86_400.to_time_ago
|
45
|
-
assert_equal '1 day ago', 100_000.to_time_ago
|
46
|
-
assert_equal '2 days ago', 180_000.to_time_ago
|
47
|
-
end
|
48
|
-
end
|
@@ -1,78 +0,0 @@
|
|
1
|
-
require_relative '../../test_helper'
|
2
|
-
DuckPuncher.punch! Object
|
3
|
-
|
4
|
-
class ObjectTest < MiniTest::Test
|
5
|
-
def setup
|
6
|
-
@animal = Animal.new
|
7
|
-
@dog = Dog.new
|
8
|
-
@kaia = Kaia.new
|
9
|
-
end
|
10
|
-
|
11
|
-
def teardown
|
12
|
-
DuckPuncher.deregister Animal, Dog, Kaia
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_clone!
|
16
|
-
cloned = @dog.clone!
|
17
|
-
assert_equal cloned.class, @dog.class
|
18
|
-
refute_equal cloned, @dog
|
19
|
-
end
|
20
|
-
|
21
|
-
def test_punch_with_a_core_duck
|
22
|
-
assert [].punch.respond_to?(:m)
|
23
|
-
end
|
24
|
-
|
25
|
-
def test_punch_on_a_custom_duck
|
26
|
-
DuckPuncher.register Animal, CustomPunch2
|
27
|
-
assert @animal.punch.respond_to?(:quack)
|
28
|
-
end
|
29
|
-
|
30
|
-
def test_punch_with_multiple_custom_duck
|
31
|
-
DuckPuncher.register Animal, CustomPunch2
|
32
|
-
DuckPuncher.register Animal, CustomPunch3
|
33
|
-
assert @animal.punch.respond_to?(:wobble)
|
34
|
-
end
|
35
|
-
|
36
|
-
def test_punch_call_stack
|
37
|
-
Animal.send(:define_method, :foo) { quack }
|
38
|
-
Animal.send(:define_method, :quack) { 'foo' }
|
39
|
-
assert_equal 'foo', @animal.foo
|
40
|
-
DuckPuncher.register Animal, CustomPunch2
|
41
|
-
@animal.punch!
|
42
|
-
assert_equal 'quack', @animal.foo
|
43
|
-
end
|
44
|
-
|
45
|
-
def test_punch_on_ancestor_only
|
46
|
-
DuckPuncher.register Dog, CustomPunch2
|
47
|
-
assert_respond_to @dog.punch, :quack
|
48
|
-
end
|
49
|
-
|
50
|
-
def test_punch_includes_all_ancestors
|
51
|
-
DuckPuncher.register Animal, CustomPunch2
|
52
|
-
DuckPuncher.register Dog, CustomPunch
|
53
|
-
DuckPuncher.register Kaia, CustomPunch3
|
54
|
-
@kaia = Kaia.new
|
55
|
-
@kaia.punch!
|
56
|
-
assert_respond_to @kaia, :wobble
|
57
|
-
assert_respond_to @kaia, :talk
|
58
|
-
assert_respond_to @kaia, :quack
|
59
|
-
end
|
60
|
-
|
61
|
-
def test_soft_punch_includes_all_ancestors
|
62
|
-
DuckPuncher.register Animal, CustomPunch2
|
63
|
-
DuckPuncher.register Dog, CustomPunch
|
64
|
-
DuckPuncher.register Kaia, CustomPunch3
|
65
|
-
@kaia = Kaia.new.punch
|
66
|
-
assert_respond_to @kaia, :wobble
|
67
|
-
assert_respond_to @kaia, :talk
|
68
|
-
assert_respond_to @kaia, :quack
|
69
|
-
end
|
70
|
-
|
71
|
-
def test_soft_punch_with_parent_override
|
72
|
-
DuckPuncher.register Animal, CustomPunch
|
73
|
-
DuckPuncher.register Kaia, ModWithOverride
|
74
|
-
@kaia = Kaia.new.punch
|
75
|
-
assert_respond_to @kaia, :talk
|
76
|
-
assert_equal @kaia.talk, 'talk is cheap'
|
77
|
-
end
|
78
|
-
end
|
@@ -1,39 +0,0 @@
|
|
1
|
-
require_relative '../../test_helper'
|
2
|
-
|
3
|
-
DuckPuncher.(String)
|
4
|
-
|
5
|
-
class StringTest < MiniTest::Test
|
6
|
-
def test_pluralize
|
7
|
-
assert_equal 'hour', 'hour'.pluralize(1)
|
8
|
-
assert_equal 'hours', 'hour'.pluralize(0)
|
9
|
-
assert_equal 'hours', 'hour'.pluralize(2)
|
10
|
-
end
|
11
|
-
|
12
|
-
def test_underscore
|
13
|
-
assert_equal 'mini_test', 'MiniTest'.underscore
|
14
|
-
assert_equal 'mini_test_do_it_to_it', 'MiniTestDoItToIt'.underscore
|
15
|
-
assert_equal 'mini_test/helper', 'MiniTest::Helper'.underscore
|
16
|
-
assert_equal 'mini_test/helper/expectations', 'MiniTest::Helper::Expectations'.underscore
|
17
|
-
assert_equal 'mini_test.rb', 'mini_test.rb'.underscore
|
18
|
-
assert_equal 'duck_puncher/json_storage', 'DuckPuncher::JSONStorage'.underscore
|
19
|
-
end
|
20
|
-
|
21
|
-
def test_to_boolean
|
22
|
-
assert 'true'.to_boolean
|
23
|
-
assert '1'.to_boolean
|
24
|
-
assert 'y'.to_boolean
|
25
|
-
assert 'on'.to_boolean
|
26
|
-
assert 'yes'.to_boolean
|
27
|
-
refute 'false'.to_boolean
|
28
|
-
refute '0'.to_boolean
|
29
|
-
refute 'no'.to_boolean
|
30
|
-
refute 'off'.to_boolean
|
31
|
-
refute ''.to_boolean
|
32
|
-
refute 'f'.to_boolean
|
33
|
-
end
|
34
|
-
|
35
|
-
def test_constantize
|
36
|
-
assert_equal MiniTest, 'MiniTest'.constantize
|
37
|
-
assert_equal MiniTest::Test, 'MiniTest::Test'.constantize
|
38
|
-
end
|
39
|
-
end
|
@@ -1,71 +0,0 @@
|
|
1
|
-
require_relative '../test_helper'
|
2
|
-
|
3
|
-
DuckPuncher.punch! Object, only: :punch
|
4
|
-
|
5
|
-
class DuckPuncherTest < MiniTest::Test
|
6
|
-
def setup
|
7
|
-
@subject = Animal.new
|
8
|
-
@kaia = Kaia.new
|
9
|
-
end
|
10
|
-
|
11
|
-
def teardown
|
12
|
-
DuckPuncher.deregister Animal, Kaia, Dog
|
13
|
-
end
|
14
|
-
|
15
|
-
def test_punch!
|
16
|
-
refute_respond_to @kaia, :talk
|
17
|
-
refute_respond_to @kaia.punch, :talk
|
18
|
-
DuckPuncher.register Kaia, CustomPunch
|
19
|
-
DuckPuncher.punch! Kaia, only: :talk
|
20
|
-
assert_respond_to @kaia, :talk
|
21
|
-
assert_respond_to @kaia.punch, :talk
|
22
|
-
end
|
23
|
-
|
24
|
-
def test_punch_all!
|
25
|
-
DuckPuncher.()
|
26
|
-
expected_methods = DuckPuncher::Ducks.list.values.m(:to_a).flatten.m(:mod).m(:local_methods).flatten
|
27
|
-
assert expected_methods.size > 1
|
28
|
-
good_ducks = DuckPuncher::Ducks.list.select { |_, ducks|
|
29
|
-
ducks.all? { |duck| (duck.mod.local_methods - duck.target.instance_methods(:false)).size.zero? }
|
30
|
-
}
|
31
|
-
assert good_ducks.size > 5
|
32
|
-
end
|
33
|
-
|
34
|
-
def test_call
|
35
|
-
DuckPuncher.()
|
36
|
-
expected_methods = DuckPuncher::Ducks.list.values.m(:to_a).flatten.m(:mod).m(:local_methods).flatten
|
37
|
-
assert expected_methods.size > 1
|
38
|
-
# Find all ducks that have copied all their methods to the target class (e.g. String)
|
39
|
-
good_ducks = DuckPuncher::Ducks.list.select { |_, ducks|
|
40
|
-
ducks.all? { |duck| (duck.mod.local_methods - duck.target.instance_methods(:false)).size.zero? }
|
41
|
-
}
|
42
|
-
assert good_ducks.size == 6, "Good ducks should be equal to 6 but are #{good_ducks.size}"
|
43
|
-
end
|
44
|
-
|
45
|
-
def test_call_with_target
|
46
|
-
DuckPuncher.(String, only: [:to_boolean])
|
47
|
-
refute_respond_to @subject, :to_boolean
|
48
|
-
DuckPuncher.(String, target: @subject.class)
|
49
|
-
assert_respond_to @subject, :to_boolean
|
50
|
-
end
|
51
|
-
|
52
|
-
def test_register_with_multiple_mods
|
53
|
-
refute_respond_to @subject, :talk
|
54
|
-
refute_respond_to @subject, :wobble
|
55
|
-
refute_respond_to @subject.punch, :talk
|
56
|
-
refute_respond_to @subject.punch, :wobble
|
57
|
-
DuckPuncher.register Animal, CustomPunch, CustomPunch3
|
58
|
-
assert_respond_to @subject.punch, :talk
|
59
|
-
assert_respond_to @subject.punch, :wobble
|
60
|
-
end
|
61
|
-
|
62
|
-
def test_deregister
|
63
|
-
refute_respond_to @subject, :talk
|
64
|
-
refute_respond_to @subject.punch, :talk
|
65
|
-
DuckPuncher.register Animal, CustomPunch
|
66
|
-
assert_respond_to @subject.punch, :talk
|
67
|
-
refute_respond_to @subject, :talk
|
68
|
-
DuckPuncher.deregister Animal
|
69
|
-
refute_respond_to @subject.punch, :talk
|
70
|
-
end
|
71
|
-
end
|