duck_puncher 2.15.0 → 2.16.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 +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
|