duck_puncher 4.4.2 → 5.0.1

Sign up to get free protection for your applications and to get access to all the features.
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