duck_puncher 4.2.3 → 4.3.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3f3865242be8b73f05bd76345f5d48af3ce41dcb
4
- data.tar.gz: 8b5d46549c916081521a43ca500abd836d1d9cc0
3
+ metadata.gz: 54ed4f3463d0da2ed92a25d2d91953a9ac1ac4ab
4
+ data.tar.gz: 8972aa4d5fb7e54300db682202d41147d9dc770f
5
5
  SHA512:
6
- metadata.gz: 5c7e01041bc9440fb98bfe60a52e5be1597c5cfd3aaa38f96b0a66042b7bf34cdd70d2c21e647328fa375d123969f57c6d1f709b4eae68db90bfceeb29e606a9
7
- data.tar.gz: 6093aeca1a64b78ad8376ed2075c765e950d320a0a1fca42a915aebc8ab88c8ca60d51a5f15b5a53ce8860e08a1073bf8cd7fb0ee2e245a181b54b156f64f0dd
6
+ metadata.gz: 1a460f7785607d0b87656670d0c1fbb5f2d8868803d40aed864b202ae4fb60449922f99224ecac2aaa75d3d72c686390cfeb1593d265b4392de62f0263477eae
7
+ data.tar.gz: c931bcbf428f270460f7f41509a2cd808393bc6f2ac377c8c611c3342da1e4c8a9514c329b993d633b1d59584c8a277cec1c3b849815f617a2617ae58e004548
@@ -0,0 +1,5 @@
1
+ 4.3.0 (10/10/2016)
2
+ ==================
3
+
4
+ * Fix issue with not being able to punch the same duck with different options
5
+ * Add the `:target` option to `.call` to override the receiving class
data/README.md CHANGED
@@ -1,44 +1,13 @@
1
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
2
 
3
- DuckPuncher provides an interface for administering __duck punches__ (a.k.a "monkey patches"). Punches can be administered in several ways:
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!`.
4
6
 
5
- * as an extension
6
- * as a decorator
7
-
8
- Default extensions:
7
+ ## Install
9
8
 
10
9
  ```ruby
11
- Enumerable (including Array, Set, Range, and Enumerator)
12
- #m # => `[].m(:to_s)`
13
- #m! # => `[].m!(:upcase)`
14
- #mm # => `[].mm(:sub, /[aeiou]/, '*')`
15
- #mm! # => `[].mm!(:sub, /[aeiou]/, '*')`
16
- #except # => `[].except('foo', 'bar')`
17
- #map_keys # => `[].map_keys(:id)`
18
- Hash
19
- #dig # => `{a: 1, b: {c: 2}}.dig(:b, :c)` (Standard in Ruby >= 2.3)
20
- #compact # => `{a: 1, b: nil}.compact` # => {a: 1}
21
- Numeric
22
- #to_currency # => `25.245.to_currency` # => 25.25
23
- #to_duration # => `10_000.to_duration` # => '2 h 46 min'
24
- #to_time_ago # => `10_000.to_time_ago` # => '2 hours ago'
25
- #to_rad # => `10.15.to_rad` # => 0.17715091907742445
26
- String
27
- #pluralize # => `'hour'.pluralize(2)` # => "hours"
28
- #underscore # => `'DJ::JSONStorage'.underscore` # => 'dj/json_storage'
29
- #to_boolean # => `'true'.to_boolean` # => true
30
- #constantize # => `'MiniTest::Test'.constantize` # => MiniTest::Test
31
- Module
32
- #local_methods # => `Kernel.local_methods` # => returns the methods defined directly in the class + nested constants w/ methods
33
- Object
34
- #clone! # => `Object.new.clone!` # => a deep clone of the object (using Marshal.dump)
35
- #punch # => `Object.new.punch` # => a copy of Object.new with String punches mixed in
36
- #punch! # => `Object.new.punch!` # => destructive version applies extensions directly to the base object
37
- #echo # => `Object.new.echo.inspect` # => spits out the caller and value of the object and returns the object
38
- #track # => `Object.new.track` # => Trace methods calls to the object (requires [object_tracker](https://github.com/ridiculous/object_tracker), which it'll try to download)
39
- Method
40
- #to_instruct # => `Benchmark.method(:measure).to_instruct` returns the Ruby VM instruction sequence for the method
41
- #to_source # => `Benchmark.method(:measure).to_source` returns the method definition as a string
10
+ gem 'duck_puncher', '~> 4.3'
42
11
  ```
43
12
 
44
13
  ## Usage
@@ -55,12 +24,18 @@ Punch individual ducks by name:
55
24
  DuckPuncher.(Hash, Object)
56
25
  ```
57
26
 
58
- One method to rule them all:
27
+ Add `punch` as a proxy method to all punches:
59
28
 
60
29
  ```ruby
61
30
  DuckPuncher.(Object, only: :punch)
62
31
  ```
63
32
 
33
+ Redirect the punches registered for a class to another target:
34
+
35
+ ```ruby
36
+ DuckPuncher.(Object, target: String)
37
+ ```
38
+
64
39
  ### Tactical punches
65
40
 
66
41
  `DuckPuncher` extends the amazing [Usable](https://github.com/ridiculous/usable) gem, so you can configure only the punches you want! For instance:
@@ -101,27 +76,84 @@ When there are no punches registered for a class, it'll search the ancestor list
101
76
  a method defined `echo`, but when we punch `Object`, it means all subclasses have access to the same methods, even with soft punches.
102
77
 
103
78
  ```ruby
104
- def soft_punch
105
- ('a'..'z').punch.echo.to_a.map(&:upcase)
106
- end
107
-
108
- def hard_punch
109
- ('a'..'z').punch!.m!(:upcase).mm!(:*, 3).echo
110
- end
111
-
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
112
82
  >> soft_punch
113
- "a..z -- (irb):8:in `soft_punch'"
114
- => ["A", "B", "C", "D", ...]
83
+ "a".."z"
84
+ * /.../delegate.rb:85:in `method_missing'
85
+ => ["A", "B", "C", "D", "E", "F", "G", "H", "I", ...]
115
86
  >> hard_punch
116
- "[\"AAA\", \"BBB\", \"CCC\", \"DDD\", ...] -- (irb):12:in `hard_punch'"
87
+ "a".."z"
88
+ * (irb):11:in `hard_punch'
117
89
  => ["AAA", "BBB", "CCC", "DDDD", ...]
118
90
  ```
119
91
 
120
- ### Registering custom punches
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
+ ```
121
143
 
122
- DuckPuncher allows you to utilize the `punch` interface to __extend__ any kind of object with your own punches. Simply
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
123
155
  call `DuckPuncher.register` with the name of your module (or an array of names) and any of
124
- [these options](https://github.com/ridiculous/duck_puncher/blob/master/lib/duck_puncher/duck.rb#L11).
156
+ [these options](https://github.com/ridiculous/duck_puncher/blob/master/lib/duck_puncher/duck.rb#L10).
125
157
 
126
158
 
127
159
  ```ruby
@@ -159,13 +191,7 @@ user = User.new('Ryan').punch
159
191
  user.call_with_retry(19.99)
160
192
  ```
161
193
 
162
- To register _and_ punch in one swoop, use `DuckPuncher.register!`
163
-
164
- ## Install
165
-
166
- ```ruby
167
- gem 'duck_puncher'
168
- ```
194
+ To register the extension _and_ punch the class, use `DuckPuncher.register!`
169
195
 
170
196
  ## Logging
171
197
 
@@ -215,36 +241,18 @@ DuckPuncher.(:Object, only: :track)
215
241
  Donald.track
216
242
  Duck.track
217
243
  >> Duck.usable Donald, only: :tap_tap
218
- * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00002)
219
- * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00001)
220
- * called "Donald.respond_to?" with to_ary, true [RUBY CORE] (0.00001)
221
- * called "Donald.to_s" [RUBY CORE] (0.00001)
222
- * called "Duck.usable_config" [ruby-2.3.0@duck_puncher/gems/usable-1.2.0/lib/usable.rb:10] (0.00002)
223
- * called "Duck.usable_config" [ruby-2.3.0@duck_puncher/gems/usable-1.2.0/lib/usable.rb:10] (0.00001)
224
- * called "Donald.const_defined?" with UsableSpec [RUBY CORE] (0.00001)
225
- * called "Donald.dup" [RUBY CORE] (0.00002)
226
- * called "Donald.name" [RUBY CORE] (0.00000)
227
- * called "Donald.instance_methods" [RUBY CORE] (0.00001)
228
- * called "Duck.const_defined?" with DonaldUsed [RUBY CORE] (0.00001)
229
- * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00001)
230
- * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00000)
231
- * called "Donald.respond_to?" with to_ary, true [RUBY CORE] (0.00000)
232
- * called "Donald.to_s" [RUBY CORE] (0.00035)
233
- * called "Duck.const_set" with DonaldUsed, #<Module:0x007fe23a261618> [RUBY CORE] (0.00002)
234
- * called "Duck.usable_config" [ruby-2.3.0@duck_puncher/gems/usable-1.2.0/lib/usable.rb:10] (0.00000)
235
- * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00000)
236
- * called "Donald.respond_to?" with to_ary, true [RUBY CORE] (0.00001)
237
- * called "Donald.to_s" [RUBY CORE] (0.00019)
238
- * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00001)
239
- * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00000)
240
- * called "Donald.respond_to?" with to_ary, true [RUBY CORE] (0.00000)
241
- * called "Donald.to_s" [RUBY CORE] (0.00000)
242
- * called "Duck.include" with Duck::DonaldUsed [RUBY CORE] (0.00001)
243
- * called "Duck#send" with include, Duck::DonaldUsed [RUBY CORE] (0.00024)
244
- * called "Duck.usable!" with #<Usable::ModExtender:0x007fe23a261ca8> [ruby-2.3.0@duck_puncher/gems/usable-1.2.0/lib/usable.rb:41] (0.00143)
245
- * called "Donald.const_defined?" with UsableSpec [RUBY CORE] (0.00001)
246
- * called "Duck.usable" with Donald, {:only=>:tap_tap} [ruby-2.3.0@duck_puncher/gems/usable-1.2.0/lib/usable.rb:30] (0.00189)
247
- # ... You get the idea.
244
+ # => * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00002)
245
+ # => * called "Donald.respond_to?" with to_str, true [RUBY CORE] (0.00001)
246
+ # => * called "Donald.respond_to?" with to_ary, true [RUBY CORE] (0.00001)
247
+ # => * called "Donald.to_s" [RUBY CORE] (0.00001)
248
+ # => * called "Duck.usable_config" [ruby-2.3.0@duck_puncher/gems/usable-1.2.0/lib/usable.rb:10] (0.00002)
249
+ # => * called "Duck.usable_config" [ruby-2.3.0@duck_puncher/gems/usable-1.2.0/lib/usable.rb:10] (0.00001)
250
+ # => * called "Donald.const_defined?" with UsableSpec [RUBY CORE] (0.00001)
251
+ # => * called "Donald.dup" [RUBY CORE] (0.00002)
252
+ # => * called "Donald.name" [RUBY CORE] (0.00000)
253
+ # => * called "Donald.instance_methods" [RUBY CORE] (0.00001)
254
+ # => * called "Duck.const_defined?" with DonaldUsed [RUBY CORE] (0.00001)
255
+ # => ...
248
256
  ```
249
257
 
250
258
  ## Contributing
data/Rakefile CHANGED
@@ -2,12 +2,12 @@ require 'bundler/gem_tasks'
2
2
  require 'rake'
3
3
  require 'rake/testtask'
4
4
 
5
- # Rake::TestTask.new(:soft_punch_test) do |t|
6
- # t.pattern = 'test/soft_punch/*_test.rb'
7
- # end
8
-
9
- Rake::TestTask.new(:hard_punch_test) do |t|
10
- t.pattern = 'test/lib/**/*_test.rb'
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
11
  end
12
12
 
13
- task default: [:hard_punch_test]
13
+ task default: tasks.map(&:name)
@@ -15,6 +15,7 @@ require 'duck_puncher/utilities'
15
15
  require 'duck_puncher/ancestral_hash'
16
16
  require 'duck_puncher/duck'
17
17
  require 'duck_puncher/ducks'
18
+ require 'duck_puncher/unique_duck'
18
19
 
19
20
  module DuckPuncher
20
21
  autoload :GemInstaller, 'duck_puncher/gem_installer'
@@ -22,11 +23,10 @@ module DuckPuncher
22
23
 
23
24
  class << self
24
25
  # @description Include additional functionality
25
- # Registration[:register, :deregister]
26
- # Decoration[:decorators, :build_decorator_class, :decorate, :cached_decorators, :undecorate]
27
- # Utilities[:lookup_constant, :redefine_constant]
28
- # AncestralHash[:ancestral_hash]
29
- include Registration, Decoration, Utilities, AncestralHash
26
+ include Registration # [:register, :deregister]
27
+ include Decoration # [:decorators, :build_decorator_class, :decorate, :cached_decorators, :undecorate]
28
+ include Utilities # [:lookup_constant, :redefine_constant]
29
+ include AncestralHash # [:ancestral_hash]
30
30
 
31
31
  attr_accessor :logger
32
32
 
@@ -39,14 +39,15 @@ module DuckPuncher
39
39
  classes = args.any? ? args : Ducks.list.keys
40
40
  classes.each do |klass|
41
41
  klass = lookup_constant(klass)
42
- (Ducks[klass] - punched_ducks).sort.each do |duck|
43
- punches = Array(options[:only] || duck.options[:only] || Ducks::Module.instance_method(:local_methods).bind(duck.mod).call)
44
- options[:target] = klass
45
- logger.info %Q(#{klass}#{" <-- #{duck.mod.name}#{punches}" if punches.any?})
46
- if duck.punch(options)
42
+ Ducks[klass].sort.each do |duck|
43
+ duck.punch_options = Ducks::Object.instance_method(:clone!).bind(options).call
44
+ duck.punch_options[:target] ||= klass
45
+ if punched_ducks.include?(duck)
46
+ logger.warn %(Already punched #{duck.mod.name})
47
+ elsif duck.punch(duck.punch_options).any?
47
48
  punched_ducks << duck
48
49
  else
49
- logger.error %Q(Failed to punch #{name})
50
+ logger.warn %(No punches were thrown)
50
51
  end
51
52
  end
52
53
  end
@@ -58,7 +59,18 @@ module DuckPuncher
58
59
  alias punch! call
59
60
 
60
61
  def punched_ducks
61
- @punched_ducks ||= []
62
+ @punched_ducks ||= Set.new
63
+ end
64
+
65
+ def register(*)
66
+ target, *_ = super
67
+ decorators[target] = build_decorator_class(*Ducks[target])
68
+ @cached_decorators = nil
69
+ end
70
+
71
+ def deregister(*)
72
+ super
73
+ @cached_decorators = nil
62
74
  end
63
75
  end
64
76
  end
@@ -3,7 +3,7 @@ module DuckPuncher
3
3
  attr_accessor :target, :mod, :options
4
4
 
5
5
  # @param target [String,Class] Class or module to punch
6
- # @param mod [String,Module] The module that defines the extensions (@name is used by default)
6
+ # @param mod [String,Module] The module that defines the extensions
7
7
  # @param [Hash] options to modify the duck #punch method behavior
8
8
  # @option options :before [Proc] A hook that is called with the target class before +punch+
9
9
  # @option options :after [Proc] A hook that is called with the target class after +punch+
@@ -19,35 +19,17 @@ module DuckPuncher
19
19
  # @option options [Symbol,String] :method Specifies if the methods should be included or prepended (:include)
20
20
  # @return [Class] The class that was just punched
21
21
  def punch(opts = {})
22
- opts = options.merge opts
22
+ opts = options.merge(opts)
23
23
  targets = Array(opts[:target] || self.target)
24
24
  targets.each do |target|
25
25
  options[:before].call(target) if options[:before]
26
+ punches = Array(opts[:only] || Ducks::Module.instance_method(:local_methods).bind(mod).call)
27
+ DuckPuncher.logger.info %Q(#{target}#{" <-- #{mod.name}#{punches}" if punches.any?})
26
28
  target.extend Usable
27
29
  target.usable mod, only: opts[:only], method: opts[:method]
28
30
  options[:after].call(target) if options[:after]
29
31
  end
30
32
  targets
31
33
  end
32
-
33
- #
34
- # Required to play nice in a Set
35
- #
36
-
37
- def eql?(other)
38
- "#{target}-#{mod}" == "#{other.target}-#{other.mod}"
39
- end
40
-
41
- def hash
42
- target.to_s.hash + mod.to_s.hash
43
- end
44
-
45
- #
46
- # Required for sorting
47
- #
48
-
49
- def <=>(other)
50
- target <=> other.target
51
- end
52
34
  end
53
35
  end
@@ -25,8 +25,10 @@ module DuckPuncher
25
25
  self
26
26
  end
27
27
 
28
- def echo
29
- p "#{self} -- #{caller_locations[respond_to?(:__getobj__) ? 2 : 0]}"
28
+ # @param [Integer] trace The number of lines from the stack trace to print (nil)
29
+ def echo(trace = nil)
30
+ p self
31
+ puts caller_locations.take(trace).map { |l| l.to_s.prepend('* ') }.join("\n") if trace
30
32
  self
31
33
  end
32
34
 
@@ -1,7 +1,7 @@
1
1
  module DuckPuncher
2
2
  module Ducks
3
3
  module String
4
- BOOLEAN_MAP = ::Hash[%w(true 1 yes y on).product([true]) + ['false', '0', 'no', 'n', 'off', ''].product([false])].freeze
4
+ BOOLEAN_MAP = ::Hash[%w(true 1 yes y on).product([true])].freeze
5
5
 
6
6
  def pluralize(count)
7
7
  "#{self}#{'s' if count != 1}"
@@ -5,12 +5,11 @@ module DuckPuncher
5
5
  options = mods.last.is_a?(Hash) ? mods.pop : {}
6
6
  target = DuckPuncher.lookup_constant target
7
7
  Ducks.list[target] = Set.new [] unless Ducks.list.key?(target)
8
- Array(mods).each do |mod|
9
- duck = Duck.new target, mod, options
8
+ mods = Array(mods).each do |mod|
9
+ duck = UniqueDuck.new Duck.new target, mod, options
10
10
  Ducks.list[target] << duck
11
- decorators[target] = build_decorator_class(duck, *Ducks[target])
12
11
  end
13
- @cached_decorators = nil
12
+ [target, *mods]
14
13
  end
15
14
 
16
15
  def register!(*args)
@@ -21,7 +20,6 @@ module DuckPuncher
21
20
  def deregister(*classes)
22
21
  classes.each &Ducks.list.method(:delete)
23
22
  classes.each &decorators.method(:delete)
24
- @cached_decorators = nil
25
23
  end
26
24
  end
27
25
  end
@@ -0,0 +1,26 @@
1
+ module DuckPuncher
2
+ class UniqueDuck < DelegateClass(Duck)
3
+ attr_accessor :punch_options
4
+
5
+ #
6
+ # Required to play nice in a Set
7
+ #
8
+
9
+ def eql?(other)
10
+ "#{target}-#{mod}" == "#{other.target}-#{other.mod}"
11
+ end
12
+
13
+ def hash
14
+ target.to_s.hash + mod.to_s.hash + punch_options.to_s.hash
15
+ end
16
+
17
+ #
18
+ # Sorting
19
+ #
20
+
21
+ def <=>(other)
22
+ target <=> other.target
23
+ end
24
+
25
+ end
26
+ end
@@ -1,3 +1,3 @@
1
1
  module DuckPuncher
2
- VERSION = '4.2.3'.freeze
2
+ VERSION = '4.3.0'.freeze
3
3
  end
@@ -1,5 +1,5 @@
1
1
  require_relative '../../test_helper'
2
- DuckPuncher.punch! Numeric
2
+ DuckPuncher.(Numeric, String)
3
3
 
4
4
  class NumericTest < MiniTest::Test
5
5
 
@@ -1,6 +1,6 @@
1
1
  require_relative '../../test_helper'
2
2
 
3
- DuckPuncher.punch! String
3
+ DuckPuncher.(String)
4
4
 
5
5
  class StringTest < MiniTest::Test
6
6
  def test_pluralize
@@ -29,7 +29,7 @@ class StringTest < MiniTest::Test
29
29
  refute 'no'.to_boolean
30
30
  refute 'off'.to_boolean
31
31
  refute ''.to_boolean
32
- refute 'asd'.to_boolean
32
+ refute 'f'.to_boolean
33
33
  end
34
34
 
35
35
  def test_constantize
@@ -35,11 +35,18 @@ class DuckPuncherTest < MiniTest::Test
35
35
  DuckPuncher.()
36
36
  expected_methods = DuckPuncher::Ducks.list.values.m(:to_a).flatten.m(:mod).m(:local_methods).flatten
37
37
  assert expected_methods.size > 1
38
+ # Find all ducks that have copied all their methods to the target class (e.g. String)
38
39
  good_ducks = DuckPuncher::Ducks.list.select { |_, ducks|
39
- # Assert that all methods were copied over
40
40
  ducks.all? { |duck| (duck.mod.local_methods - duck.target.instance_methods(:false)).size.zero? }
41
41
  }
42
- assert good_ducks.size > 5
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
43
50
  end
44
51
 
45
52
  def test_register_with_multiple_mods
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.2.3
4
+ version: 4.3.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: 2016-09-01 00:00:00.000000000 Z
11
+ date: 2016-10-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: usable
@@ -98,6 +98,7 @@ files:
98
98
  - ".ruby-gemset"
99
99
  - ".ruby-version"
100
100
  - ".travis.yml"
101
+ - CHANGELOG.md
101
102
  - Gemfile
102
103
  - LICENSE.txt
103
104
  - README.md
@@ -121,6 +122,7 @@ files:
121
122
  - lib/duck_puncher/gem_installer.rb
122
123
  - lib/duck_puncher/json_storage.rb
123
124
  - lib/duck_puncher/registration.rb
125
+ - lib/duck_puncher/unique_duck.rb
124
126
  - lib/duck_puncher/utilities.rb
125
127
  - lib/duck_puncher/version.rb
126
128
  - test/fixtures/test_classes.rb