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 +4 -4
- data/README.md +30 -12
- data/duck_puncher.gemspec +1 -1
- data/lib/duck_puncher.rb +0 -17
- data/lib/duck_puncher/duck.rb +20 -14
- data/lib/duck_puncher/ducks.rb +1 -1
- data/lib/duck_puncher/ducks/array.rb +8 -0
- data/lib/duck_puncher/ducks/object.rb +2 -1
- data/lib/duck_puncher/version.rb +1 -1
- data/test/lib/duck_puncher/array_test.rb +6 -0
- data/test/lib/duck_puncher/object_test.rb +10 -0
- data/test/lib/duck_puncher_test.rb +4 -0
- data/test/test_helper.rb +1 -0
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6050f09ba1e9b843078f3d6eb434b668da5687a8
|
4
|
+
data.tar.gz: 30a1083c6ccb5edfa9520e3f395b63c65b221040
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
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
|
-
|
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
|
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).
|
74
|
+
>> %w[yes no 1].punch.m!(:punch).m(:to_boolean)
|
74
75
|
=> [true, false, true]
|
75
76
|
```
|
76
77
|
|
77
|
-
|
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
|
82
|
-
call
|
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
|
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
|
-
|
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
|
|
data/duck_puncher.gemspec
CHANGED
@@ -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
|
|
data/lib/duck_puncher.rb
CHANGED
@@ -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
|
data/lib/duck_puncher/duck.rb
CHANGED
@@ -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
|
data/lib/duck_puncher/ducks.rb
CHANGED
@@ -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
|
-
|
16
|
+
extend Ducks[duck_name.to_sym].mod
|
16
17
|
end
|
17
18
|
|
18
19
|
def track
|
data/lib/duck_puncher/version.rb
CHANGED
@@ -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
|
data/test/test_helper.rb
CHANGED
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
|
+
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-
|
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
|