duck_puncher 2.15.0 → 2.16.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 +9 -0
- data/lib/duck_puncher/duck.rb +34 -13
- data/lib/duck_puncher/ducks/object.rb +1 -1
- data/lib/duck_puncher/ducks/string.rb +4 -0
- data/lib/duck_puncher/ducks.rb +1 -1
- data/lib/duck_puncher/version.rb +1 -1
- data/lib/duck_puncher.rb +7 -1
- data/test/lib/duck_puncher/hash_test.rb +10 -6
- data/test/lib/duck_puncher/object_test.rb +40 -4
- data/test/lib/duck_puncher/string_test.rb +12 -7
- data/test/test_helper.rb +2 -0
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a8a2fed5af6189e26acdeb046ec66ff7dec8368d
|
4
|
+
data.tar.gz: 6f487f85bd05ea581fcc4463e11b2b1c611a7f1e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 16fac17fdb70185f7deed4f5657cfa32c29203b7427a6cda940765a454eb8300dedcd4ebf056b785c54669dc901eef05ced5e5aa3c2833e4217ff99d1fdd3ba7
|
7
|
+
data.tar.gz: e40705f7bf0bb01eebdf5767e590254d5d83c8190df619cadd36fd31ff310d0d4a2a2ef2875626249ee32e3cd103e4ee9983b062776c5701089ce4924c802289
|
data/README.md
CHANGED
@@ -100,7 +100,9 @@ module Retryable
|
|
100
100
|
retry if (retries -= 1) > 0
|
101
101
|
end
|
102
102
|
end
|
103
|
+
```
|
103
104
|
|
105
|
+
```ruby
|
104
106
|
# Register the extensions
|
105
107
|
DuckPuncher.register [:Billable, :Retryable]
|
106
108
|
|
@@ -116,6 +118,13 @@ user = User.new('Ryan').punch(:Billable).punch(:Retryable)
|
|
116
118
|
user.call_with_retry(19.99)
|
117
119
|
```
|
118
120
|
|
121
|
+
Ducks can be registered under any name you want, as long as the `:mod` option specifies a module:
|
122
|
+
|
123
|
+
```ruby
|
124
|
+
DuckPuncher.register :bills, mod: 'Admin::Billable'
|
125
|
+
User.new.punch(:bills)
|
126
|
+
```
|
127
|
+
|
119
128
|
When punching at a class level, the `:class` option is required:
|
120
129
|
|
121
130
|
```ruby
|
data/lib/duck_puncher/duck.rb
CHANGED
@@ -4,45 +4,66 @@ module DuckPuncher
|
|
4
4
|
|
5
5
|
# @param [Symbol] name of the duck
|
6
6
|
# @param [Hash] options to modify the duck #punch method behavior
|
7
|
-
# @option options [String] :
|
7
|
+
# @option options [String,Module] :mod The module that defines the extensions (@name is used by default)
|
8
|
+
# @option options [String,Class] :class (name) to punch
|
8
9
|
# @option options [Proc] :if Stops +punch+ if it returns false
|
9
|
-
# @option options [Proc] :before A hook that is called with the target
|
10
|
-
# @option options [Proc] :after A hook that is called with the target
|
10
|
+
# @option options [Proc] :before A hook that is called with the target class before +punch+
|
11
|
+
# @option options [Proc] :after A hook that is called with the target class after +punch+
|
11
12
|
def initialize(name, options = {})
|
12
13
|
@name = name
|
13
14
|
@options = options
|
14
15
|
end
|
15
16
|
|
16
17
|
# @param [Hash] opts to modify punch
|
17
|
-
# @option options [Class] :target
|
18
|
+
# @option options [Class] :target Specifies the class to be punched
|
18
19
|
# @option options [Array,Symbol] :only Specifies the methods to extend onto the current object
|
20
|
+
# @option options [Symbol,String] :method Specifies if the methods should be included or prepended (:include)
|
21
|
+
# @return [Class] The class that was just punched
|
19
22
|
def punch(opts = {})
|
20
23
|
if options[:if] && !options[:if].call
|
21
24
|
DuckPuncher.log.info %Q(Skipping the punch for #{name}!)
|
22
25
|
return nil
|
23
26
|
end
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
27
|
+
if options[:mod]
|
28
|
+
mod = lookup_constant(options[:mod])
|
29
|
+
else
|
30
|
+
mod = DuckPuncher::Ducks.const_get(name)
|
31
|
+
end
|
32
|
+
target = opts.delete(:target) || lookup_class
|
33
|
+
Array(target).each do |klass|
|
34
|
+
options[:before].call(klass) if options[:before]
|
35
|
+
klass.extend Usable
|
36
|
+
klass.usable mod, only: opts[:only], method: opts[:method]
|
37
|
+
options[:after].call(klass) if options[:after]
|
38
|
+
end
|
28
39
|
target
|
29
40
|
end
|
30
41
|
|
31
|
-
|
32
|
-
|
42
|
+
# @return [Class] The class that is given to initialize as the option :class or the name of the current duck (module extension)
|
43
|
+
def lookup_class
|
44
|
+
lookup_constant(options[:class] || name)
|
45
|
+
end
|
46
|
+
|
47
|
+
def lookup_constant(const)
|
48
|
+
return const if Module === const
|
49
|
+
if const.to_s.respond_to?(:constantize)
|
50
|
+
const.to_s.constantize
|
51
|
+
else
|
52
|
+
const.to_s.split('::').inject(Object) { |k, part| k.const_get(part) }
|
53
|
+
end
|
33
54
|
end
|
34
55
|
|
35
56
|
# @param [Class] obj The object being punched
|
36
57
|
def delegated(obj = nil)
|
37
|
-
obj_class = obj ? obj.class :
|
58
|
+
obj_class = obj ? obj.class : lookup_class
|
38
59
|
klass = DelegateClass(obj_class)
|
39
|
-
punch target: klass
|
60
|
+
punch target: klass, method: :prepend
|
40
61
|
klass.usable DuckPuncher::Ducks::Object, only: :punch, method: :prepend
|
41
62
|
klass
|
42
63
|
end
|
43
64
|
|
44
65
|
def classify
|
45
|
-
Class.new(
|
66
|
+
Class.new(lookup_class).tap { |k| punch target: k }
|
46
67
|
end
|
47
68
|
end
|
48
69
|
end
|
data/lib/duck_puncher/ducks.rb
CHANGED
@@ -15,7 +15,7 @@ module DuckPuncher
|
|
15
15
|
|
16
16
|
def [](name)
|
17
17
|
list.find { |duck| duck.name == name.to_sym } ||
|
18
|
-
|
18
|
+
fail(ArgumentError, %Q(Couldn't find "#{name}" in my list of Ducks! I know about: #{list.map(&:name).map(&:to_s)}))
|
19
19
|
end
|
20
20
|
|
21
21
|
def load_path_for(duck)
|
data/lib/duck_puncher/version.rb
CHANGED
data/lib/duck_puncher.rb
CHANGED
@@ -25,7 +25,13 @@ module DuckPuncher
|
|
25
25
|
# @param [Symbol] duck_name
|
26
26
|
# @param [Class] obj The object being punched
|
27
27
|
def delegate_class(duck_name, obj = nil)
|
28
|
-
delegations[
|
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
|
29
35
|
end
|
30
36
|
|
31
37
|
def duck_class(name)
|
@@ -1,12 +1,16 @@
|
|
1
1
|
require_relative '../../test_helper'
|
2
|
-
|
2
|
+
|
3
|
+
DuckPuncher.punch :Hash
|
3
4
|
|
4
5
|
class HashTest < MiniTest::Test
|
6
|
+
def setup
|
7
|
+
@subject = DuckPuncher::HashDuck.new.merge({ a: 1, b: { c: 2 } })
|
8
|
+
end
|
9
|
+
|
5
10
|
def test_dig
|
6
|
-
|
7
|
-
assert_equal
|
8
|
-
assert_equal
|
9
|
-
assert_equal
|
10
|
-
assert_equal my_hash.dig(:b), { c: 2 }
|
11
|
+
assert_equal @subject.dig(:a), 1
|
12
|
+
assert_equal @subject.dig(:b, :a), nil
|
13
|
+
assert_equal @subject.dig(:b, :c), 2
|
14
|
+
assert_equal @subject.dig(:b), { c: 2 }
|
11
15
|
end
|
12
16
|
end
|
@@ -2,10 +2,46 @@ require_relative '../../test_helper'
|
|
2
2
|
DuckPuncher.punch! :Object
|
3
3
|
|
4
4
|
class ObjectTest < MiniTest::Test
|
5
|
+
|
6
|
+
def setup
|
7
|
+
Object.const_set :User, Class.new
|
8
|
+
@subject = Object.new
|
9
|
+
@user = User.new
|
10
|
+
end
|
11
|
+
|
12
|
+
def teardown
|
13
|
+
DuckPuncher::Ducks.list.delete_if { |duck| [:admin, :super_admin, :User].include?(duck.name) }
|
14
|
+
Object.send :remove_const, :User
|
15
|
+
end
|
16
|
+
|
5
17
|
def test_clone!
|
6
|
-
|
7
|
-
cloned
|
8
|
-
|
9
|
-
|
18
|
+
cloned = @subject.clone!
|
19
|
+
assert_equal cloned.class, @subject.class
|
20
|
+
refute_equal cloned, @subject
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_punch_on_a_core_duck
|
24
|
+
refute [].respond_to?(:m)
|
25
|
+
assert [].respond_to?(:punch)
|
26
|
+
assert [].punch.respond_to?(:m)
|
27
|
+
end
|
28
|
+
|
29
|
+
def test_punch_with_a_core_duck
|
30
|
+
assert [].punch(:Array).respond_to?(:m)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_punch_on_a_custom_duck
|
34
|
+
DuckPuncher.register :User, mod: 'CustomPunch2'
|
35
|
+
assert @user.punch.respond_to?(:quack)
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_punch_with_a_custom_duck
|
39
|
+
refute @user.respond_to?(:quack)
|
40
|
+
DuckPuncher.register :admin, mod: 'CustomPunch2'
|
41
|
+
assert @user.punch(:admin).respond_to?(:quack)
|
42
|
+
|
43
|
+
refute @user.respond_to?(:wobble)
|
44
|
+
DuckPuncher.register :super_admin, mod: 'CustomPunch3'
|
45
|
+
assert @user.punch(:super_admin).respond_to?(:wobble)
|
10
46
|
end
|
11
47
|
end
|
@@ -4,16 +4,16 @@ DuckPuncher.punch! :String
|
|
4
4
|
|
5
5
|
class StringTest < MiniTest::Test
|
6
6
|
def test_pluralize
|
7
|
-
assert_equal 'hour'.pluralize(1)
|
8
|
-
assert_equal 'hour'.pluralize(0)
|
9
|
-
assert_equal 'hour'.pluralize(2)
|
7
|
+
assert_equal 'hour', 'hour'.pluralize(1)
|
8
|
+
assert_equal 'hours', 'hour'.pluralize(0)
|
9
|
+
assert_equal 'hours', 'hour'.pluralize(2)
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_underscore
|
13
|
-
assert_equal '
|
14
|
-
assert_equal '
|
15
|
-
assert_equal 'MiniTest::Helper'.underscore
|
16
|
-
assert_equal 'MiniTest::Helper::Expectations'.underscore
|
13
|
+
assert_equal 'mini_test', 'MiniTest'.underscore
|
14
|
+
assert_equal 'mini_test_do_it_to_it', 'MiniTestDoItToIt'.underscore
|
15
|
+
assert_equal 'mini_test/helper', 'MiniTest::Helper'.underscore
|
16
|
+
assert_equal 'mini_test/helper/expectations', 'MiniTest::Helper::Expectations'.underscore
|
17
17
|
assert_equal 'mini_test.rb', 'mini_test.rb'.underscore
|
18
18
|
assert_equal 'duck_puncher/json_storage', 'DuckPuncher::JSONStorage'.underscore
|
19
19
|
end
|
@@ -31,4 +31,9 @@ class StringTest < MiniTest::Test
|
|
31
31
|
refute ''.to_boolean
|
32
32
|
refute 'asd'.to_boolean
|
33
33
|
end
|
34
|
+
|
35
|
+
def test_constantize
|
36
|
+
assert_equal MiniTest, 'MiniTest'.constantize
|
37
|
+
assert_equal MiniTest::Test, 'MiniTest::Test'.constantize
|
38
|
+
end
|
34
39
|
end
|
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: 2.
|
4
|
+
version: 2.16.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-06-03 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: usable
|