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 +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
|