duck_puncher 2.16.0 → 3.0.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: 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