duck_puncher 4.2.3 → 4.3.0

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