duck_puncher 2.16.0 → 3.0.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: a8a2fed5af6189e26acdeb046ec66ff7dec8368d
4
- data.tar.gz: 6f487f85bd05ea581fcc4463e11b2b1c611a7f1e
3
+ metadata.gz: 6050f09ba1e9b843078f3d6eb434b668da5687a8
4
+ data.tar.gz: 30a1083c6ccb5edfa9520e3f395b63c65b221040
5
5
  SHA512:
6
- metadata.gz: 16fac17fdb70185f7deed4f5657cfa32c29203b7427a6cda940765a454eb8300dedcd4ebf056b785c54669dc901eef05ced5e5aa3c2833e4217ff99d1fdd3ba7
7
- data.tar.gz: e40705f7bf0bb01eebdf5767e590254d5d83c8190df619cadd36fd31ff310d0d4a2a2ef2875626249ee32e3cd103e4ee9983b062776c5701089ce4924c802289
6
+ metadata.gz: fa163f85c3ec46d0a3071a4d9e75b3c2de14b37bcd5f29b8b8fe3b65d13e617f37f4740894e4fc393482c75a9aedeb25600da618e74469fc30341428512c7001
7
+ data.tar.gz: 86d3f879aba2e9e78694398d348a7b18e909931584dedf65617538b12e5c88344ef5b0bf0e711fc8899f7a1683e26d9b42400dd293803318d8e10b4371e618df
data/README.md CHANGED
@@ -4,13 +4,14 @@ DuckPuncher provides an interface for administering __duck punches__ (a.k.a "mon
4
4
 
5
5
  * as an extension
6
6
  * as a subclass
7
- * as a decorator
8
7
 
9
8
  Default extensions:
10
9
 
11
10
  ```ruby
12
- Array #m => `[].m(:to_s)` => `[].map(&:to_s)`
11
+ Array #m => `[].m(:to_s)` => `[].map(&:to_s)`
12
+ #m! => `[].m!(:upcase)` => `[].map!(&:upcase)`
13
13
  #mm => `[].mm(:sub, /[aeiou]/, '*')` => `[].map { |x| x.sub(/[aeiou]/, '*') }`
14
+ #mm! => `[].mm!(:sub, /[aeiou]/, '*')` => `[].map! { |x| x.sub(/[aeiou]/, '*') }`
14
15
  #except => `[].except('foo', 'bar')` => `[] - ['foo', 'bar']`
15
16
  Hash #dig => `{a: 1, b: {c: 2}}.dig(:b, :c)` => 2 (Part of standard lib in Ruby >= 2.3)
16
17
  Numeric #to_currency => `25.245.to_currency` => 25.25
@@ -20,6 +21,7 @@ Numeric #to_currency => `25.245.to_currency` => 25.25
20
21
  String #pluralize => `'hour'.pluralize(2)` => "hours"
21
22
  #underscore => `'DuckPuncher::JSONStorage'.underscore` => 'duck_puncher/json_storage'
22
23
  #to_boolean => `'1'.to_boolean` => true
24
+ #constantize => `'MiniTest::Test'.constantize` => MiniTest::Test
23
25
  Object #clone! => `Object.new.clone!` => a deep clone of the object (using Marshal.dump)
24
26
  #punch => `'duck'.punch` => a copy of 'duck' with String punches mixed in
25
27
  Method #to_instruct => `Benchmark.method(:measure).to_instruct` returns the Ruby VM instruction sequence for the method
@@ -48,13 +50,13 @@ DuckPuncher.punch! :Object, only: :punch
48
50
 
49
51
  ### Tactical punches
50
52
 
51
- Punch only certain methods onto a duck:
53
+ `DuckPuncher` extends the amazing [Usable](https://github.com/ridiculous/usable) gem, so you can configure only the punches you want! For instance:
52
54
 
53
55
  ```ruby
54
56
  DuckPuncher.punch! :Numeric, only: [:to_currency, :to_duration]
55
57
  ```
56
58
 
57
- The `.punch` method creates and caches a new punched class that __inherits__ from the original:
59
+ Use `DuckPuncher.punch` to create a new class that __inherits__ from the original (automatically cached for future calls):
58
60
 
59
61
  ```ruby
60
62
  >> DuckPuncher.punch :String
@@ -65,21 +67,26 @@ The `.punch` method creates and caches a new punched class that __inherits__ fro
65
67
  => false
66
68
  ```
67
69
 
68
- If you punch `Object` then you can use `punch` on any object to get a new __decorated__ copy of the object with the desired
69
- functionality mixed in:
70
+ If you punch `Object` then you can use `#punch` on any object to extend individual instances:
70
71
 
71
72
  ```ruby
72
73
  >> DuckPuncher.punch! :Object, only: :punch
73
- >> %w[yes no 1].punch.m(:punch).punch.m(:to_boolean)
74
+ >> %w[yes no 1].punch.m!(:punch).m(:to_boolean)
74
75
  => [true, false, true]
75
76
  ```
76
77
 
77
- Because `DuckPuncher` extends the amazing [Usable](https://github.com/ridiculous/usable) gem, you can configure only the punches you want!
78
+ 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:
79
+ ```ruby
80
+ >> LovableDuck = Module.new { def inspect() "I love #{self.first}" end }
81
+ >> DuckPuncher.register :with_love, mod: 'LovableDuck'
82
+ >> %w[ducks].punch(:with_love)
83
+ => "I love ducks"
84
+ ```
78
85
 
79
86
  ### Registering custom punches
80
87
 
81
- DuckPuncher allows you to utilize the `punch` interface to __decorate__ any kind of object with your own punches. Simply
82
- call `.register` with the name of your module (or an array of names) and any of
88
+ DuckPuncher allows you to utilize the `punch` interface to __extend__ any kind of object with your own punches. Simply
89
+ call `DuckPuncher.register` with the name of your module (or an array of names) and any of
83
90
  [these options](https://github.com/ridiculous/duck_puncher/blob/master/lib/duck_puncher/duck.rb#L11).
84
91
 
85
92
 
@@ -118,7 +125,7 @@ user = User.new('Ryan').punch(:Billable).punch(:Retryable)
118
125
  user.call_with_retry(19.99)
119
126
  ```
120
127
 
121
- Ducks can be registered under any name you want, as long as the `:mod` option specifies a module:
128
+ Ducks can be registered under any name, as long as the `:mod` option specifies a module:
122
129
 
123
130
  ```ruby
124
131
  DuckPuncher.register :bills, mod: 'Admin::Billable'
@@ -134,8 +141,19 @@ DuckPuncher.punch! :Billable
134
141
 
135
142
  ## Install
136
143
 
137
- gem 'duck_puncher'
144
+ ```ruby
145
+ gem 'duck_puncher'
146
+ ```
147
+
148
+ ## Logging
149
+
150
+ Get notified of all punches/extensions by changing the logger level:
151
+
152
+ ```ruby
153
+ DuckPuncher.log.level = Logger::INFO
154
+ ```
138
155
 
156
+ The default log level is `DEBUG`
139
157
 
140
158
  ## Experimental
141
159
 
@@ -9,7 +9,7 @@ Gem::Specification.new do |spec|
9
9
  spec.authors = ["Ryan Buckley"]
10
10
  spec.email = ["arebuckley@gmail.com"]
11
11
  spec.description = %q{Administer precision punches}
12
- spec.summary = %q{Administer precision punches to your favorite Ruby classes}
12
+ spec.summary = %q{Administer precision extensions (a.k.a "punches") to your favorite Ruby classes}
13
13
  spec.homepage = "https://github.com/ridiculous/duck_puncher"
14
14
  spec.license = "MIT"
15
15
 
@@ -1,6 +1,5 @@
1
1
  require 'pathname'
2
2
  require 'fileutils'
3
- require 'delegate'
4
3
  require 'logger'
5
4
  require 'usable'
6
5
  require 'duck_puncher/version'
@@ -14,26 +13,10 @@ module DuckPuncher
14
13
  class << self
15
14
  attr_accessor :log
16
15
 
17
- def delegations
18
- @delegations ||= {}
19
- end
20
-
21
16
  def classes
22
17
  @classes ||= {}
23
18
  end
24
19
 
25
- # @param [Symbol] duck_name
26
- # @param [Class] obj The object being punched
27
- def delegate_class(duck_name, obj = nil)
28
- delegations["#{obj.class}#{duck_name}"] ||= begin
29
- duck_const = duck_name.to_s
30
- if duck_const[/^[A-Z]/].nil?
31
- duck_const = duck_const.split('_').map(&:capitalize).join
32
- end
33
- const_set "#{duck_const}DuckDelegated", Ducks[duck_name.to_sym].dup.delegated(obj)
34
- end
35
- end
36
-
37
20
  def duck_class(name)
38
21
  classes[name] ||= const_set "#{name}Duck", Ducks[name].dup.classify
39
22
  end
@@ -24,11 +24,6 @@ module DuckPuncher
24
24
  DuckPuncher.log.info %Q(Skipping the punch for #{name}!)
25
25
  return nil
26
26
  end
27
- if options[:mod]
28
- mod = lookup_constant(options[:mod])
29
- else
30
- mod = DuckPuncher::Ducks.const_get(name)
31
- end
32
27
  target = opts.delete(:target) || lookup_class
33
28
  Array(target).each do |klass|
34
29
  options[:before].call(klass) if options[:before]
@@ -39,6 +34,14 @@ module DuckPuncher
39
34
  target
40
35
  end
41
36
 
37
+ def mod
38
+ if options[:mod]
39
+ lookup_constant(options[:mod])
40
+ else
41
+ DuckPuncher::Ducks.const_get(name)
42
+ end
43
+ end
44
+
42
45
  # @return [Class] The class that is given to initialize as the option :class or the name of the current duck (module extension)
43
46
  def lookup_class
44
47
  lookup_constant(options[:class] || name)
@@ -53,17 +56,20 @@ module DuckPuncher
53
56
  end
54
57
  end
55
58
 
56
- # @param [Class] obj The object being punched
57
- def delegated(obj = nil)
58
- obj_class = obj ? obj.class : lookup_class
59
- klass = DelegateClass(obj_class)
60
- punch target: klass, method: :prepend
61
- klass.usable DuckPuncher::Ducks::Object, only: :punch, method: :prepend
62
- klass
63
- end
64
-
65
59
  def classify
66
60
  Class.new(lookup_class).tap { |k| punch target: k }
67
61
  end
62
+
63
+ #
64
+ # Required to play nice in a Set
65
+ #
66
+
67
+ def eql?(other)
68
+ name == other.name
69
+ end
70
+
71
+ def hash
72
+ name.hash
73
+ end
68
74
  end
69
75
  end
@@ -2,7 +2,7 @@ module DuckPuncher
2
2
  module Ducks
3
3
  class << self
4
4
  def list
5
- @list ||= [
5
+ @list ||= Set.new [
6
6
  Duck.new(:String),
7
7
  Duck.new(:Array),
8
8
  Duck.new(:Numeric),
@@ -5,10 +5,18 @@ module DuckPuncher
5
5
  map(&method_name)
6
6
  end
7
7
 
8
+ def m!(method_name)
9
+ map!(&method_name)
10
+ end
11
+
8
12
  def mm(method_name, *args)
9
13
  map { |x| x.public_send(method_name, *args) }
10
14
  end
11
15
 
16
+ def mm!(method_name, *args)
17
+ map! { |x| x.public_send(method_name, *args) }
18
+ end
19
+
12
20
  def except(*args)
13
21
  self - args
14
22
  end
@@ -11,8 +11,9 @@ module DuckPuncher
11
11
  end
12
12
  end
13
13
 
14
+ # @description Adds the duck punches to the curren object (meant to be used on instances)
14
15
  def punch(duck_name = self.class.name)
15
- DuckPuncher.delegate_class(duck_name, self).new(self)
16
+ extend Ducks[duck_name.to_sym].mod
16
17
  end
17
18
 
18
19
  def track
@@ -1,3 +1,3 @@
1
1
  module DuckPuncher
2
- VERSION = '2.16.0'.freeze
2
+ VERSION = '3.0.0'.freeze
3
3
  end
@@ -11,10 +11,16 @@ class ArrayTest < MiniTest::Test
11
11
 
12
12
  def test_m
13
13
  assert_equal subject.map(&:upcase), subject.m(:upcase)
14
+ refute_equal subject.object_id, subject.m(:upcase).object_id
15
+ assert_equal subject.map!(&:upcase), subject.m!(:upcase)
16
+ assert_equal subject.object_id, subject.m!(:upcase).object_id
14
17
  end
15
18
 
16
19
  def test_mm_with_two_args
17
20
  assert_equal subject.map { |x| x.prepend('btn-') }, subject.mm(:prepend, 'btn-')
21
+ refute_equal subject.object_id, subject.mm(:prepend, 'btn-')
22
+ assert_equal subject.map! { |x| x.prepend('btn-') }, subject.mm!(:prepend, 'btn-')
23
+ assert_equal subject.object_id, subject.mm!(:prepend, 'btn-').object_id
18
24
  end
19
25
 
20
26
  def test_mm_with_three_args
@@ -44,4 +44,14 @@ class ObjectTest < MiniTest::Test
44
44
  DuckPuncher.register :super_admin, mod: 'CustomPunch3'
45
45
  assert @user.punch(:super_admin).respond_to?(:wobble)
46
46
  end
47
+
48
+ def test_punch_call_stack
49
+ User.send(:define_method, :foo) { quack }
50
+ User.send(:define_method, :quack) { 'foo' }
51
+ assert_equal 'foo', @user.foo
52
+ DuckPuncher.register :User, mod: 'CustomPunch2'
53
+ assert_equal 'quack', @user.punch.foo
54
+ User.send(:remove_method, :foo)
55
+ User.send(:remove_method, :quack)
56
+ end
47
57
  end
@@ -8,6 +8,10 @@ class DuckPuncherTest < MiniTest::Test
8
8
  assert_respond_to [], :tap_tap
9
9
  DuckPuncher.punch! :Object, only: :punch
10
10
  assert_respond_to [].punch(:CustomPunch), :tap_tap
11
+ # does not re-register duck with same name
12
+ duck_list_size = DuckPuncher::Ducks.list.size
13
+ DuckPuncher.register :CustomPunch, class: 'String'
14
+ assert_equal duck_list_size, DuckPuncher::Ducks.list.size
11
15
  end
12
16
 
13
17
  def test_register_with_array
@@ -16,6 +16,7 @@ end
16
16
 
17
17
  module CustomPunch2
18
18
  def quack
19
+ 'quack'
19
20
  end
20
21
  end
21
22
 
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: 2.16.0
4
+ version: 3.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: 2016-06-03 00:00:00.000000000 Z
11
+ date: 2016-08-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: usable
@@ -150,7 +150,7 @@ rubyforge_project:
150
150
  rubygems_version: 2.5.1
151
151
  signing_key:
152
152
  specification_version: 4
153
- summary: Administer precision punches to your favorite Ruby classes
153
+ summary: Administer precision extensions (a.k.a "punches") to your favorite Ruby classes
154
154
  test_files:
155
155
  - test/fixtures/wut.rb
156
156
  - test/lib/duck_puncher/array_test.rb