duck_puncher 4.4.2 → 5.0.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.
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
@@ -1,10 +0,0 @@
1
- language: ruby
2
- rvm:
3
- - 2.0.0
4
- - 2.1.0
5
- - 2.1.5
6
- - 2.2.1
7
- - 2.2.2
8
- - 2.2.3
9
- - 2.3.0
10
- before_install: gem install bundler -v 1.10.5
data/CHANGELOG.md DELETED
@@ -1,20 +0,0 @@
1
- 4.4.2 (01/10/2016)
2
- ==================
3
-
4
- * Fix glaring bug in Duck class where the `:only` option is ignored (since 4.4.0)
5
-
6
- 4.4.1 (12/19/2016)
7
- ==================
8
-
9
- * Lazy load the rubygems/dependency_installer only when `require!` is called
10
-
11
- 4.4.0 (12/4/2016)
12
- =================
13
-
14
- * Target objects are no longer extended with the [Usable](https://github.com/ridiculous/usable) module
15
-
16
- 4.3.0 (10/10/2016)
17
- ==================
18
-
19
- * Fix issue with not being able to punch the same duck with different options
20
- * Add the `:target` option to `.call` to override the receiving class
data/Gemfile DELETED
@@ -1,9 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in duck_puncher.gemspec
4
- gemspec
5
-
6
- if RUBY_VERSION >= '2.0'
7
- gem 'byebug'
8
- # gem 'object_tracker'
9
- end
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,7 +0,0 @@
1
- class Wut
2
- def to_a
3
- ['w'] + ['u'] + ['t']
4
- end
5
-
6
- def to_f() Float::INFINITY end
7
- end
@@ -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