duck_puncher 2.9.3 → 2.10.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: b2ebd6df2befa524cfa62d7e94bd86c95b6e05f4
4
- data.tar.gz: efc4ed3226ffb5ed36fafd67735d294a48f0f8e2
3
+ metadata.gz: e0a5aa2de7709f8b53e6ab183581335b3493eb5e
4
+ data.tar.gz: 9838712103fbf49a3614ee930b53c6c7171457b8
5
5
  SHA512:
6
- metadata.gz: e13d18b83abee84c985762eb05639c63a8a9e32f2a936d709c4a26b100cb5e95008ee561640d16f2ccf5fca4aedf6745347cf2d26e2026637ddae5dc2ed9d693
7
- data.tar.gz: 4d777965f7a99d0cec674d290d9290b512394068e9cca996b59565e27e640a29bb3308d0079aa0132a672cc73a6223fc2421acb8de064ed52e1f6ea1310c598b
6
+ metadata.gz: d3d4032fcfd695eaec9baeaaa6d4c4c6f9d6e8144cbfaa5cd01edda887320818fac8d55b5b49391f1edc129db8d703ad275449b191850236606bcfe16530a122
7
+ data.tar.gz: f3d6bb17dd490336d578d7e13747393b14233fc1c1da36857fc1966ea8384ae8a20ca2a716004391192662681e25674f9ef841b1fd70b12e4c079c0de518bfc0
data/README.md CHANGED
@@ -1,26 +1,26 @@
1
- # DuckPuncher
1
+ # DuckPuncher [![Gem Version](https://badge.fury.io/rb/duck_puncher.svg)](http://badge.fury.io/rb/duck_puncher) [![Build Status](https://travis-ci.org/ridiculous/duck_puncher.svg)](https://travis-ci.org/ridiculous/duck_puncher) [![Code Climate](https://codeclimate.com/github/ridiculous/duck_puncher/badges/gpa.svg)](https://codeclimate.com/github/ridiculous/duck_puncher)
2
2
 
3
3
  Since Ruby objects walk and talk like ducks, they must therefore _be_ ducks. But ducks don't always behave, and some times they need
4
- tough love. You know, lil love punches! :punch: :heart:
4
+ tough love! :punch: :heart:
5
5
 
6
6
  These are the ducks I love the most:
7
7
 
8
8
  ```ruby
9
- Array#m => `[].m(:to_s)` => `[].map(&:to_s)`
10
- Array#mm => `[].mm(:sub, /[aeiou]/, '*')` => `[].map { |x| x.sub(/[aeiou]/, '*') }`
11
- Array#get => `[].methods.get('ty?')` => [:empty?]
12
- Hash#dig => `{a: 1, b: {c: 2}}.dig(:b, :c)` => 2 (Part of standard lib in Ruby >= 2.3)
13
- Numeric#to_currency => `25.245.to_currency` => 25.25
14
- Numeric#to_duration => `10_000.to_duration` => '2 h 46 min'
15
- Numeric#to_time_ago => `10_000.to_time_ago` => '2 hours ago'
16
- Numeric#to_rad => `10.15.to_rad` => 0.17715091907742445
17
- String#pluralize => `'hour'.pluralize(2)` => "hours"
18
- String#underscore => `'DuckPuncher::JSONStorage'.underscore` => 'duck_puncher/json_storage'
19
- Object#clone! => `Object.new.clone!` => a deep clone of the object (using Marshal.dump)
20
- Object#punch => `'duck'.punch` => a copy of 'duck' with the mixed String punches
21
- Object#track => `'duck'.punch.track` => downloads the [ObjectTracker](https://github.com/ridiculous/object_tracker) gem if it's not available and starts tracking this object
22
- Method#to_instruct => `Benchmark.method(:measure).to_instruct` returns the Ruby VM instruction sequence for the method
23
- Method#to_source => `Benchmark.method(:measure).to_source` returns the method definition as a string
9
+ Array #m => `[].m(:to_s)` => `[].map(&:to_s)`
10
+ #mm => `[].mm(:sub, /[aeiou]/, '*')` => `[].map { |x| x.sub(/[aeiou]/, '*') }`
11
+ #get => `[].methods.get('ty?')` => [:empty?]
12
+ Hash #dig => `{a: 1, b: {c: 2}}.dig(:b, :c)` => 2 (Part of standard lib in Ruby >= 2.3)
13
+ Numeric #to_currency => `25.245.to_currency` => 25.25
14
+ #to_duration => `10_000.to_duration` => '2 h 46 min'
15
+ #to_time_ago => `10_000.to_time_ago` => '2 hours ago'
16
+ #to_rad => `10.15.to_rad` => 0.17715091907742445
17
+ String #pluralize => `'hour'.pluralize(2)` => "hours"
18
+ #underscore => `'DuckPuncher::JSONStorage'.underscore` => 'duck_puncher/json_storage'
19
+ Object #clone! => `Object.new.clone!` => a deep clone of the object (using Marshal.dump)
20
+ #punch => `'duck'.punch` => a copy of 'duck' with the mixed String punches
21
+ #track => `'duck'.punch.track` => downloads the [ObjectTracker](https://github.com/ridiculous/object_tracker) gem if it's not available and starts tracking this object
22
+ Method #to_instruct => `Benchmark.method(:measure).to_instruct` returns the Ruby VM instruction sequence for the method
23
+ #to_source => `Benchmark.method(:measure).to_source` returns the method definition as a string
24
24
  ```
25
25
 
26
26
  ## Tactical punches
@@ -29,7 +29,7 @@ Sometimes you don't want to punch all the ducks. That's why you can punch only c
29
29
 
30
30
  ```ruby
31
31
  >> DuckPuncher.punch! :Numeric, only: [:to_currency, :to_duration]
32
- INFO: Already punched Numeric
32
+ WARN: Punching [:to_currency, :to_duration] onto Numeric
33
33
  => nil
34
34
  >> 100.to_currency '$'
35
35
  => "$100.00"
@@ -39,29 +39,6 @@ INFO: Already punched Numeric
39
39
  NoMethodError: undefined method `to_time_ago' for 100:Fixnum
40
40
  ```
41
41
 
42
- There is also an experimental punch that tries to download the required gem if it doesn't exist on your computer. The
43
- method is called `require!` and works like this:
44
-
45
- Downloads and activates a gem for the current and subsequent consoles. For example:
46
-
47
- ```bash
48
- >> `require 'pry'`
49
- LoadError: cannot load such file -- pry
50
- from (irb):1:in `require'
51
- from (irb):1
52
- from bin/console:10:in `<main>'
53
- >> require! 'pry'
54
- Fetching: method_source-0.8.2.gem (100%)
55
- Fetching: slop-3.6.0.gem (100%)
56
- Fetching: coderay-1.1.0.gem (100%)
57
- Fetching: pry-0.10.3.gem (100%)
58
- => true
59
- >> Pry.start
60
- [1] pry(main)>
61
- ```
62
-
63
- Pretty cool, right? Although, it doesn't work well with bigger gems or those with native extensions.
64
-
65
42
  ## Install
66
43
 
67
44
  gem 'duck_puncher'
@@ -72,29 +49,92 @@ Ducks need to be _loaded_ before they can be punched! Maybe put this in an initi
72
49
 
73
50
  ```ruby
74
51
  # config/initializers/duck_puncher.rb
75
- DuckPuncher.punch_all! #=> punches all the ducks forever
76
- DuckPuncher.punch! :Hash, :Object #=> only punches the Hash and Object ducks
77
- DuckPuncher.punch! :Object, only: :punch #=> only opens a can of whoop ass! Define one method to rule them all
52
+ DuckPuncher.punch_all! # => punches all the ducks forever
53
+ DuckPuncher.punch! :Hash, :Object # => only punches the Hash and Object ducks
54
+ DuckPuncher.punch! :Object, only: :punch # => only opens a can of whoop ass! Define one method to rule them all
78
55
  ```
79
56
 
80
- Create a new class of your favorite duck pre-punched:
57
+ The `.punch` method creates and caches a new punched class that inherits from the original. This avoids altering built-in
58
+ classes. For example:
81
59
 
82
60
  ```ruby
83
- DuckPuncher.punch :String #=> returns an anonymous punched duck that inherits from String
84
- DuckString = DuckPuncher.punch :String #=> give the anonymous duck a name, so that you can use it!
85
- DuckString.new.respond_to? :underscore #=> true
61
+ >> DuckPuncher.punch :String
62
+ => DuckPuncher::StringDuck
63
+ >> DuckPuncher::StringDuck.new('Yes').to_boolean
64
+ => true
65
+ >> String.new('Yes').respond_to? :to_boolean
66
+ => false
86
67
  ```
87
68
 
88
69
  If you punch `Object` then you can use `punch` on any object to get a new decorated copy of the class with the desired
89
70
  functionality mixed in:
90
71
 
91
72
  ```ruby
92
- DuckPuncher.punch! :Object, only: :punch
93
- %w[yes no 1].punch.m(:punch).punch.m(:to_boolean) #=> [true, false, true]
73
+ >> DuckPuncher.punch! :Object, only: :punch
74
+ >> %w[yes no 1].punch.m(:punch).punch.m(:to_boolean)
75
+ => [true, false, true]
94
76
  ```
95
77
 
96
78
  Because `DuckPuncher` extends the amazing [Usable](https://github.com/ridiculous/usable) gem, you can configure only the punches you want!
97
79
 
80
+ ## Registering custom punches
81
+
82
+ DuckPuncher allows you to utilize the `punch` interface to decorate any kind of object with your own punches. Simply call
83
+ `.register` with the name of your module:
84
+
85
+ ```ruby
86
+ module Donald
87
+ def tap_tap
88
+ p self
89
+ self
90
+ end
91
+ end
92
+
93
+ DuckPuncher.register :Donald, class: 'Array', if: -> { !defined?(::Rails) || Rails.env.development? }
94
+ ```
95
+
96
+ The register method takes the same options as [Duck#initialize](https://github.com/ridiculous/duck_puncher/blob/master/lib/duck_puncher/duck.rb#L11)
97
+ and will be used to configure punches.
98
+
99
+ Activate a custom punch:
100
+
101
+ ```ruby
102
+ DuckPuncher.punch! :Donald
103
+ [].tap_tap
104
+ # or
105
+ DuckPuncher.punch! :Object, only: :punch
106
+ [].punch(:Donald).tap_tap
107
+ ```
108
+
109
+ ## Experimental
110
+
111
+ __Object#require__ will try to require a gem, or, if it's not found, then _download_ it! It will also keep track of any
112
+ downloaded gems and load them for subsequent IRB/rails console sessions. Gems are _not_
113
+ saved to the Gemfile.
114
+
115
+ In the wild:
116
+
117
+ ```bash
118
+ >> `require 'pry'`
119
+ LoadError: cannot load such file -- pry
120
+ from (irb):1:in `require'
121
+ from (irb):1
122
+ from bin/console:10:in `<main>'
123
+ >> DuckPuncher.punch! :Object, only: :require!
124
+ WARN: Punching require! onto Object
125
+ => nil
126
+ >> require! 'pry'
127
+ Fetching: method_source-0.8.2.gem (100%)
128
+ Fetching: slop-3.6.0.gem (100%)
129
+ Fetching: coderay-1.1.0.gem (100%)
130
+ Fetching: pry-0.10.3.gem (100%)
131
+ => true
132
+ >> Pry.start
133
+ [1] pry(main)>
134
+ ```
135
+
136
+ Perfect! Mostly ... although, it doesn't work well with bigger gems or those with native extensions ¯\\\_(ツ)_/¯
137
+
98
138
  ## Contributing
99
139
 
100
140
  * Fork it
data/Rakefile CHANGED
@@ -7,7 +7,7 @@ Rake::TestTask.new(:soft_punch_test) do |t|
7
7
  end
8
8
 
9
9
  Rake::TestTask.new(:hard_punch_test) do |t|
10
- t.pattern = 'test/duck_puncher/*_test.rb'
10
+ t.pattern = 'test/lib/**/*_test.rb'
11
11
  end
12
12
 
13
13
  task default: [:soft_punch_test, :hard_punch_test]
@@ -2,15 +2,20 @@ module DuckPuncher
2
2
  class Duck
3
3
  attr_accessor :name, :options
4
4
 
5
+ # @param [Symbol] name of the duck
6
+ # @param [Hash] options to modify the duck #punch method behavior
7
+ # @option options [String] :class (name) to punch
8
+ # @option options [Proc] :if Stops +punch+ if it returns false
9
+ # @option options [Proc] :before A hook that is called with the target @klass before +punch+
10
+ # @option options [Proc] :after A hook that is called with the target @klass after +punch+
5
11
  def initialize(name, options = {})
6
12
  @name = name
7
13
  @options = options
8
14
  end
9
15
 
10
- def load_path
11
- "duck_puncher/ducks/#{name.to_s.gsub(/\B([A-Z])/, '_\1').downcase}"
12
- end
13
-
16
+ # @param [Hash] opts to modify punch
17
+ # @option options [Class] :target Overrides the @klass to be punched
18
+ # @option options [Array,Symbol] :only Specifies the methods to extend onto the current object
14
19
  def punch(opts = {})
15
20
  if options[:if] && !options[:if].call
16
21
  DuckPuncher.log.warn %Q(Failed to punch #{name}!)
@@ -28,8 +33,10 @@ module DuckPuncher
28
33
  @klass ||= (options[:class] || name).to_s.split('::').inject(Kernel) { |k, part| k.const_get part }
29
34
  end
30
35
 
31
- def delegated
32
- DelegateClass(klass).tap { |k| punch target: k }
36
+ # @param [Class] obj The object being punched
37
+ def delegated(obj = nil)
38
+ obj_class = obj ? obj.class : klass
39
+ DelegateClass(obj_class).tap { |k| punch target: k }
33
40
  end
34
41
 
35
42
  def classify
@@ -11,8 +11,8 @@ module DuckPuncher
11
11
  end
12
12
  end
13
13
 
14
- def punch
15
- DuckPuncher.delegate_class(self.class.name.to_sym).new(self)
14
+ def punch(duck_name = self.class.name)
15
+ DuckPuncher.delegate_class(duck_name.to_sym, self).new(self)
16
16
  end
17
17
 
18
18
  def track
@@ -17,6 +17,10 @@ module DuckPuncher
17
17
  list.find { |duck| duck.name == name.to_sym } ||
18
18
  DuckPuncher.log.info(%Q(Couldn't find "#{name}" in my list of Ducks! I know about: #{list.map(&:name).map(&:to_s)}))
19
19
  end
20
+
21
+ def load_path_for(duck)
22
+ "duck_puncher/ducks/#{duck.name.to_s.gsub(/\B([A-Z])/, '_\1').downcase}"
23
+ end
20
24
  end
21
25
 
22
26
  #
@@ -24,7 +28,7 @@ module DuckPuncher
24
28
  #
25
29
 
26
30
  list.each do |duck|
27
- autoload duck.name, duck.load_path
31
+ autoload duck.name, load_path_for(duck)
28
32
  end
29
33
  end
30
34
  end
@@ -1,3 +1,3 @@
1
1
  module DuckPuncher
2
- VERSION = '2.9.3'.freeze
2
+ VERSION = '2.10.0'.freeze
3
3
  end
data/lib/duck_puncher.rb CHANGED
@@ -14,17 +14,33 @@ module DuckPuncher
14
14
  class << self
15
15
  attr_accessor :log
16
16
 
17
- def delegate_class(name)
17
+ def delegations
18
18
  @delegations ||= {}
19
- @delegations[name] ||= Ducks[name].dup.delegated
19
+ end
20
+
21
+ def classes
22
+ @classes ||= {}
23
+ end
24
+
25
+ # @param [Symbol] duck_name
26
+ # @param [Class] obj The object being punched
27
+ def delegate_class(duck_name, obj = nil)
28
+ delegations[duck_name] ||= const_set "#{duck_name}DuckDelegated", Ducks[duck_name].dup.delegated(obj)
29
+ end
30
+
31
+ def duck_class(name)
32
+ classes[name] ||= const_set "#{name}Duck", Ducks[name].dup.classify
20
33
  end
21
34
 
22
35
  # @description Extends functionality to a copy of the specified class
23
36
  def punch(*names)
24
37
  singular = names.size == 1
25
- punched_ducks = names.map { |name| Ducks[name].dup.classify }.compact
26
- punched_ducks = punched_ducks.first if singular
27
- punched_ducks
38
+ punched_ducks = names.map(&method(:duck_class)).compact
39
+ if singular
40
+ punched_ducks.first
41
+ else
42
+ punched_ducks
43
+ end
28
44
  end
29
45
 
30
46
  def punch!(*names)
@@ -43,6 +59,10 @@ module DuckPuncher
43
59
  log.warn 'Punching all ducks! Watch out!'
44
60
  Ducks.list.each &:punch
45
61
  end
62
+
63
+ def register(*args)
64
+ Ducks.list << Duck.new(*args)
65
+ end
46
66
  end
47
67
 
48
68
  # @description Default logger
@@ -1,4 +1,5 @@
1
- require_relative '../test_helper'
1
+ require_relative '../../test_helper'
2
+
2
3
  DuckPuncher.punch! :Object
3
4
 
4
5
  class ArrayTest < MiniTest::Test
@@ -1,4 +1,4 @@
1
- require_relative '../test_helper'
1
+ require_relative '../../test_helper'
2
2
  DuckPuncher.punch! :Object
3
3
 
4
4
  class HashTest < MiniTest::Test
@@ -1,5 +1,5 @@
1
- require_relative '../test_helper'
2
- require_relative '../fixtures/wut'
1
+ require_relative '../../test_helper'
2
+ require_relative '../../fixtures/wut'
3
3
  DuckPuncher.punch! :Method
4
4
 
5
5
  class MethodTest < MiniTest::Test
@@ -1,4 +1,4 @@
1
- require_relative '../test_helper'
1
+ require_relative '../../test_helper'
2
2
  DuckPuncher.punch! :Numeric
3
3
 
4
4
  class NumericTest < MiniTest::Test
@@ -1,4 +1,4 @@
1
- require_relative '../test_helper'
1
+ require_relative '../../test_helper'
2
2
  DuckPuncher.punch! :Object
3
3
 
4
4
  class ObjectTest < MiniTest::Test
@@ -1,4 +1,4 @@
1
- require_relative '../test_helper'
1
+ require_relative '../../test_helper'
2
2
 
3
3
  DuckPuncher.punch! :String
4
4
 
@@ -0,0 +1,23 @@
1
+ require_relative '../test_helper'
2
+
3
+ module CustomPunch
4
+ def tap_tap
5
+ p self
6
+ self
7
+ end
8
+ end
9
+
10
+ class DuckPuncherTest < MiniTest::Test
11
+ def teardown
12
+ Object.send :remove_const, :CustomPunch
13
+ end
14
+
15
+ def test_register
16
+ refute_respond_to [], :tap_tap
17
+ DuckPuncher.register :CustomPunch, class: 'Array'
18
+ DuckPuncher.punch! :CustomPunch
19
+ assert_respond_to [], :tap_tap
20
+ DuckPuncher.punch! :Object, only: :punch
21
+ assert_respond_to [].punch(:CustomPunch), :tap_tap
22
+ end
23
+ end
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.9.3
4
+ version: 2.10.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-02-06 00:00:00.000000000 Z
11
+ date: 2016-02-16 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: usable
@@ -117,13 +117,14 @@ files:
117
117
  - lib/duck_puncher/gem_installer.rb
118
118
  - lib/duck_puncher/json_storage.rb
119
119
  - lib/duck_puncher/version.rb
120
- - test/duck_puncher/array_test.rb
121
- - test/duck_puncher/hash_test.rb
122
- - test/duck_puncher/method_test.rb
123
- - test/duck_puncher/numeric_test.rb
124
- - test/duck_puncher/object_test.rb
125
- - test/duck_puncher/string_test.rb
126
120
  - test/fixtures/wut.rb
121
+ - test/lib/duck_puncher/array_test.rb
122
+ - test/lib/duck_puncher/hash_test.rb
123
+ - test/lib/duck_puncher/method_test.rb
124
+ - test/lib/duck_puncher/numeric_test.rb
125
+ - test/lib/duck_puncher/object_test.rb
126
+ - test/lib/duck_puncher/string_test.rb
127
+ - test/lib/duck_puncher_test.rb
127
128
  - test/soft_punch/duck_puncher_test.rb
128
129
  - test/test_helper.rb
129
130
  homepage: https://github.com/ridiculous/duck_puncher
@@ -151,12 +152,13 @@ signing_key:
151
152
  specification_version: 4
152
153
  summary: Administer precision punches to your favorite Ruby classes
153
154
  test_files:
154
- - test/duck_puncher/array_test.rb
155
- - test/duck_puncher/hash_test.rb
156
- - test/duck_puncher/method_test.rb
157
- - test/duck_puncher/numeric_test.rb
158
- - test/duck_puncher/object_test.rb
159
- - test/duck_puncher/string_test.rb
160
155
  - test/fixtures/wut.rb
156
+ - test/lib/duck_puncher/array_test.rb
157
+ - test/lib/duck_puncher/hash_test.rb
158
+ - test/lib/duck_puncher/method_test.rb
159
+ - test/lib/duck_puncher/numeric_test.rb
160
+ - test/lib/duck_puncher/object_test.rb
161
+ - test/lib/duck_puncher/string_test.rb
162
+ - test/lib/duck_puncher_test.rb
161
163
  - test/soft_punch/duck_puncher_test.rb
162
164
  - test/test_helper.rb