duck_puncher 2.4.0 → 2.5.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: d30be8964065fb57d3de53e4e23b2deaf8ceb277
4
- data.tar.gz: f2bc471c1a31a1f438680f21440b86d489092cd1
3
+ metadata.gz: 2afbd4dd98468db97339d8094564c7830a8f6aa8
4
+ data.tar.gz: 2cabadf15f55e7b38b5028d0c702fb71ac809a4b
5
5
  SHA512:
6
- metadata.gz: e117be77a8e6a2a80f0ba415315039fa727ba1495e52bfa9b83cd7b5d631b92ebce331eaac51f6b7fb2d5c76a00e2f88f19892f4f84039686fab277976a40588
7
- data.tar.gz: 64a2e78858356beacb9f30ea6cd6955046c8f90fc596fd0d15c64cbe2e121423e85b9ffe384e08826655f45eddc74bae824a6c401877e65ebf62c44908adc851
6
+ metadata.gz: f3016a12e3e5ba11571b05ad62a58163a25db0fc4866d0c82c39cfeeaeada1c7ff44999bfabcb81021b3f4c810973e2b8b0a79e9c522426597b17e5f9f6d4d9e
7
+ data.tar.gz: c35a7d1dd77ac403f42884935f56963354e4aa5e75dcc36442b7a125ae000f3417a991da536aa8bcdb6b30ddb95f6619ced25004a6c44ce4efddfce85b6f0745
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # DuckPuncher
2
2
 
3
- These are the ducks I can punch:
3
+ Ruby objects walk and talk like ducks, therefore they _are_ ducks. But ducks don't always behave, and some times they need
4
+ tough love. You know, lil love punches! :punch: :heart:
5
+
6
+ These are the ducks I love the most:
4
7
 
5
8
  Array#m => `[].m(:to_s)` => `[].map(&:to_s)`
6
9
  Array#mm => `[].mm(:sub, /[aeiou]/, '*')` => `[].map { |x| x.sub(/[aeiou]/, '*') }`
@@ -48,8 +51,25 @@ Try it out if your feeling frisky! However, I noticed it doesn't work well with
48
51
  Ducks need to be _loaded_ before they can be punched! Maybe do this in an initializer?
49
52
 
50
53
  ```ruby
51
- DuckPuncher.punch! :Hash, :Object #=> only punches the specified ducks
52
- DuckPuncher.punch_all! #=> punches all the ducks
54
+ DuckPuncher.punch_all! #=> punches all the ducks forever
55
+ DuckPuncher.punch! :Hash, :Object #=> only punches the Hash and Object ducks
56
+ DuckPuncher.punch :String #=> returns an anonymous punched duck that inherits from String
57
+ DuckString = DuckPuncher.punch :String #=> give the anonymous duck a name, so that you can use it!
58
+ DuckString.new.respond_to? :underscore #=> true
59
+ ```
60
+
61
+ DuckPuncher defines a global `punch` method. This method creates and caches a delegation class pre-punched with only the
62
+ ducks you want!
63
+
64
+ Here's an example of how to use it:
65
+
66
+ ```ruby
67
+ >> a = punch :Array, [punch(:String, 'foo'), punch(:String, 'bar')]
68
+ => ["foo", "bar"]
69
+ >> a.m :upcase
70
+ => ["FOO", "BAR"]
71
+ >> a.mm :pluralize, 2
72
+ => ["foos", "bars"]
53
73
  ```
54
74
 
55
75
  ## Contributing
data/Rakefile CHANGED
@@ -2,8 +2,12 @@ require 'bundler/gem_tasks'
2
2
  require 'rake'
3
3
  require 'rake/testtask'
4
4
 
5
- task default: :test
5
+ Rake::TestTask.new(:soft_punch_test) do |t|
6
+ t.pattern = 'test/soft_punch/*_test.rb'
7
+ end
6
8
 
7
- Rake::TestTask.new do |t|
8
- t.pattern = 'test/**/*_test.rb'
9
+ Rake::TestTask.new(:hard_punch_test) do |t|
10
+ t.pattern = 'test/duck_puncher/*_test.rb'
9
11
  end
12
+
13
+ task default: [:soft_punch_test, :hard_punch_test]
@@ -6,6 +6,6 @@ require 'duck_puncher'
6
6
  require 'irb'
7
7
  require_relative '../test/fixtures/wut'
8
8
 
9
- DuckPuncher.punch_all!
9
+ DuckPuncher.punch_all! if ENV['PUNCH'] != 'no'
10
10
 
11
11
  IRB.start
@@ -1,5 +1,6 @@
1
1
  require 'pathname'
2
2
  require 'fileutils'
3
+ require 'delegate'
3
4
  require 'logger'
4
5
  require 'duck_puncher/version'
5
6
 
@@ -9,35 +10,63 @@ module DuckPuncher
9
10
  autoload :Duck, 'duck_puncher/duck'
10
11
  autoload :Ducks, 'duck_puncher/ducks'
11
12
 
12
- def self.punch!(*names)
13
- names.each do |name|
14
- if duck = Ducks[name]
13
+ class << self
14
+ attr_accessor :log
15
+
16
+ def delegate_class(name)
17
+ @delegations ||= {}
18
+ @delegations[name] ||= Ducks[name].dup.delegated
19
+ end
20
+
21
+ # @description Extends functionality to a copy of the specified class
22
+ def punch(*names)
23
+ singular = names.size == 1
24
+ punched_ducks = names.map do |name|
25
+ duck = Ducks[name]
26
+ duck_class = Class.new(duck.klass)
27
+ if duck.punch duck_class
28
+ duck_class
29
+ else
30
+ log.error %Q(Failed to punch #{name}!)
31
+ end
32
+ end
33
+ punched_ducks.compact!
34
+ punched_ducks = punched_ducks.first if singular
35
+ punched_ducks
36
+ end
37
+
38
+ def punch!(*names)
39
+ names.each do |name|
40
+ duck = Ducks[name]
15
41
  if duck.punched?
16
42
  log.info %Q(Already punched #{name})
17
43
  else
18
- log.warn %Q(Punching the #{name} ducky)
44
+ log.warn %Q(Punching #{name} ducky)
19
45
  unless duck.punch
20
46
  log.error %Q(Failed to punch #{name}!)
21
47
  end
22
48
  end
23
- else
24
- log.info %Q(Couldn't find "#{name}" in my list of Ducks! I know about: #{Ducks.list.map(&:name).map(&:to_s)})
25
49
  end
50
+ nil
26
51
  end
27
- nil
28
- end
29
52
 
30
- def self.punch_all!
31
- log.warn 'Punching all ducks! Watch out!'
32
- Ducks.list.each &:punch
33
- end
34
-
35
- class << self
36
- attr_accessor :log
53
+ def punch_all!
54
+ log.warn 'Punching all ducks! Watch out!'
55
+ Ducks.list.each &:punch
56
+ end
37
57
  end
38
58
 
59
+ # @description Default logger
60
+ # @example Silence logging
61
+ #
62
+ # `DuckPuncher.log.level = Logger::ERROR`
63
+ #
39
64
  self.log = Logger.new(STDOUT).tap do |config|
40
65
  config.level = Logger::INFO
41
66
  config.formatter = proc { |*args| "#{args.first}: #{args.last.to_s}\n" }
42
67
  end
43
68
  end
69
+
70
+ def punch(name, val)
71
+ DuckPuncher.delegate_class(name).new val
72
+ end
@@ -8,23 +8,34 @@ module DuckPuncher
8
8
  @punched = false
9
9
  end
10
10
 
11
+ # @note Assumes the String duck is loaded first
11
12
  def load_path
12
- "duck_puncher/ducks/#{name.to_s.downcase}"
13
+ path_name = if name == :String
14
+ name.to_s.downcase
15
+ else
16
+ Object.send(:punch, :String, name.to_s).underscore
17
+ end
18
+ "duck_puncher/ducks/#{path_name}"
13
19
  end
14
20
 
15
- def punch
21
+ def punch(target = nil)
16
22
  return false if options[:if] && !options[:if].call
17
23
  options[:before].call if options[:before]
18
- const.send :include, DuckPuncher::Ducks.const_get(name)
24
+ (target || klass).send :include, DuckPuncher::Ducks.const_get(name)
25
+ options[:after].call if options[:after]
19
26
  @punched = true
20
27
  end
21
28
 
22
- def const
23
- @const ||= (options[:class] || name).to_s.split('::').inject(Kernel) { |k, part| k.const_get part }
29
+ def klass
30
+ @klass ||= (options[:class] || name).to_s.split('::').inject(Kernel) { |k, part| k.const_get part }
24
31
  end
25
32
 
26
33
  def punched?
27
34
  @punched
28
35
  end
36
+
37
+ def delegated
38
+ DelegateClass(klass).tap { |k| punch k }
39
+ end
29
40
  end
30
41
  end
@@ -3,10 +3,10 @@ module DuckPuncher
3
3
  class << self
4
4
  def list
5
5
  @list ||= [
6
+ Duck.new(:String),
6
7
  Duck.new(:Array),
7
8
  Duck.new(:Numeric),
8
9
  Duck.new(:Hash),
9
- Duck.new(:String),
10
10
  Duck.new(:Object),
11
11
  Duck.new(:Method, before: -> { DuckPuncher::GemInstaller.initialize! }),
12
12
  Duck.new(:ActiveRecord, class: 'ActiveRecord::Base', if: -> { defined? ::ActiveRecord })
@@ -14,7 +14,8 @@ module DuckPuncher
14
14
  end
15
15
 
16
16
  def [](name)
17
- list.find { |duck| duck.name == name.to_sym }
17
+ list.find { |duck| duck.name == name.to_sym } ||
18
+ DuckPuncher.log.info(%Q(Couldn't find "#{name}" in my list of Ducks! I know about: #{list.map(&:name).map(&:to_s)}))
18
19
  end
19
20
  end
20
21
 
@@ -1,48 +1,50 @@
1
1
  module DuckPuncher
2
- module ActiveRecord
3
- def self.included(base)
4
- base.extend(ClassMethods)
5
- end
6
-
7
- def associations?
8
- associations.present?
9
- end
10
-
11
- def associations
12
- reflections.select { |key, _| send(key).present? rescue nil }.keys
13
- end
14
-
15
- module ClassMethods
16
- def except_for(*ids)
17
- scoped.where("#{quoted_table_name}.id NOT IN (?)", ids)
18
- end
19
-
20
- def since(time)
21
- scoped.where("#{quoted_table_name}.created_at > ?", time)
22
- end
23
-
24
- alias created_since since
25
-
26
- def before(time)
27
- scoped.where("#{quoted_table_name}.created_at < ?", time)
28
- end
29
-
30
- def updated_since(time)
31
- scoped.where("#{quoted_table_name}.updated_at > ?", time)
32
- end
33
-
34
- def between(start_at, end_at)
35
- scoped.where("#{quoted_table_name}.created_at BETWEEN ? AND ", start_at, end_at)
36
- end
37
-
38
- def latest
39
- scoped.order("#{quoted_table_name}.id ASC").last
40
- end
41
-
42
- # shim for backwards compatibility with Rails 3
43
- def scoped
44
- where(nil)
45
- end if Rails::VERSION::MAJOR != 3
46
- end
47
- end
2
+ module Ducks
3
+ module ActiveRecord
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ def associations?
9
+ associations.present?
10
+ end
11
+
12
+ def associations
13
+ reflections.select { |key, _| send(key).present? rescue nil }.keys
14
+ end
15
+
16
+ module ClassMethods
17
+ def except_for(*ids)
18
+ scoped.where("#{quoted_table_name}.id NOT IN (?)", ids)
19
+ end
20
+
21
+ def since(time)
22
+ scoped.where("#{quoted_table_name}.created_at > ?", time)
23
+ end
24
+
25
+ alias created_since since
26
+
27
+ def before(time)
28
+ scoped.where("#{quoted_table_name}.created_at < ?", time)
29
+ end
30
+
31
+ def updated_since(time)
32
+ scoped.where("#{quoted_table_name}.updated_at > ?", time)
33
+ end
34
+
35
+ def between(start_at, end_at)
36
+ scoped.where("#{quoted_table_name}.created_at BETWEEN ? AND ", start_at, end_at)
37
+ end
38
+
39
+ def latest
40
+ scoped.order("#{quoted_table_name}.id ASC").last
41
+ end
42
+
43
+ # shim for backwards compatibility with Rails 3
44
+ def scoped
45
+ where(nil)
46
+ end if Rails::VERSION::MAJOR != 3
47
+ end
48
+ end
49
+ end
48
50
  end
@@ -16,3 +16,4 @@ module DuckPuncher
16
16
  end
17
17
  end
18
18
  end
19
+
@@ -1,3 +1,3 @@
1
1
  module DuckPuncher
2
- VERSION = '2.4.0'.freeze
2
+ VERSION = '2.5.0'.freeze
3
3
  end
@@ -1,4 +1,5 @@
1
1
  require_relative '../test_helper'
2
+ DuckPuncher.punch! :Array
2
3
 
3
4
  class ArrayTest < MiniTest::Test
4
5
  attr_reader :subject
@@ -1,4 +1,5 @@
1
1
  require_relative '../test_helper'
2
+ DuckPuncher.punch! :Hash
2
3
 
3
4
  class HashTest < MiniTest::Test
4
5
  def test_seek
@@ -1,5 +1,6 @@
1
1
  require_relative '../test_helper'
2
2
  require_relative '../fixtures/wut'
3
+ DuckPuncher.punch! :Method
3
4
 
4
5
  class MethodTest < MiniTest::Test
5
6
 
@@ -1,4 +1,5 @@
1
1
  require_relative '../test_helper'
2
+ DuckPuncher.punch! :Numeric
2
3
 
3
4
  class NumericTest < MiniTest::Test
4
5
 
@@ -1,4 +1,5 @@
1
1
  require_relative '../test_helper'
2
+ DuckPuncher.punch! :Object
2
3
 
3
4
  class ObjectTest < MiniTest::Test
4
5
  def test_clone!
@@ -1,5 +1,7 @@
1
1
  require_relative '../test_helper'
2
2
 
3
+ DuckPuncher.punch! :String
4
+
3
5
  class StringTest < MiniTest::Test
4
6
  def test_pluralize
5
7
  assert_equal 'hour'.pluralize(1), 'hour'
@@ -0,0 +1,21 @@
1
+ require_relative '../test_helper'
2
+
3
+ class DuckPuncherTest < MiniTest::Test
4
+ DuckString = DuckPuncher.punch :String
5
+ DuckNumber, DuckArray = DuckPuncher.punch :Numeric, :Array
6
+
7
+ def test_punch
8
+ refute_respond_to '', :underscore
9
+ assert_respond_to DuckString.new, :underscore
10
+ refute_respond_to '', :underscore
11
+ end
12
+
13
+ def test_punch_multiple
14
+ refute_respond_to 25, :to_currency
15
+ refute_respond_to [], :m
16
+ assert_respond_to DuckNumber.new, :to_currency
17
+ assert_respond_to DuckArray.new, :m
18
+ refute_respond_to 25, :to_currency
19
+ refute_respond_to [], :m
20
+ end
21
+ end
@@ -4,4 +4,3 @@ require 'minitest/reporters'
4
4
  require 'duck_puncher'
5
5
 
6
6
  Minitest::Reporters.use!
7
- DuckPuncher.punch_all!
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.0
4
+ version: 2.5.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-01-30 00:00:00.000000000 Z
11
+ date: 2016-01-31 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -116,6 +116,7 @@ files:
116
116
  - test/duck_puncher/object_test.rb
117
117
  - test/duck_puncher/string_test.rb
118
118
  - test/fixtures/wut.rb
119
+ - test/soft_punch/duck_puncher_test.rb
119
120
  - test/test_helper.rb
120
121
  homepage: https://github.com/ridiculous/duck_puncher
121
122
  licenses:
@@ -149,4 +150,5 @@ test_files:
149
150
  - test/duck_puncher/object_test.rb
150
151
  - test/duck_puncher/string_test.rb
151
152
  - test/fixtures/wut.rb
153
+ - test/soft_punch/duck_puncher_test.rb
152
154
  - test/test_helper.rb