duck_puncher 4.5.1 → 5.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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 [![Gem Version](https://badge.fury.io/rb/duck_puncher.svg)](http://badge.fury.io/rb/duck_puncher) [![Build Status](https://travis-ci.org/ridiculous/duck_puncher.svg)](https://travis-ci.org/ridiculous/duck_puncher) [![Code Climate](https://codeclimate.com/github/ridiculous/duck_puncher/badges/gpa.svg)](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
|