duck_puncher 2.4.0 → 2.5.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 +23 -3
- data/Rakefile +7 -3
- data/bin/console +1 -1
- data/lib/duck_puncher.rb +44 -15
- data/lib/duck_puncher/duck.rb +16 -5
- data/lib/duck_puncher/ducks.rb +3 -2
- data/lib/duck_puncher/ducks/active_record.rb +48 -46
- data/lib/duck_puncher/ducks/array.rb +1 -0
- data/lib/duck_puncher/version.rb +1 -1
- data/test/duck_puncher/array_test.rb +1 -0
- data/test/duck_puncher/hash_test.rb +1 -0
- data/test/duck_puncher/method_test.rb +1 -0
- data/test/duck_puncher/numeric_test.rb +1 -0
- data/test/duck_puncher/object_test.rb +1 -0
- data/test/duck_puncher/string_test.rb +2 -0
- data/test/soft_punch/duck_puncher_test.rb +21 -0
- data/test/test_helper.rb +0 -1
- metadata +4 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 2afbd4dd98468db97339d8094564c7830a8f6aa8
|
4
|
+
data.tar.gz: 2cabadf15f55e7b38b5028d0c702fb71ac809a4b
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f3016a12e3e5ba11571b05ad62a58163a25db0fc4866d0c82c39cfeeaeada1c7ff44999bfabcb81021b3f4c810973e2b8b0a79e9c522426597b17e5f9f6d4d9e
|
7
|
+
data.tar.gz: c35a7d1dd77ac403f42884935f56963354e4aa5e75dcc36442b7a125ae000f3417a991da536aa8bcdb6b30ddb95f6619ced25004a6c44ce4efddfce85b6f0745
|
data/README.md
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
# DuckPuncher
|
2
2
|
|
3
|
-
|
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.
|
52
|
-
DuckPuncher.
|
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
|
-
|
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
|
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]
|
data/bin/console
CHANGED
data/lib/duck_puncher.rb
CHANGED
@@ -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
|
-
|
13
|
-
|
14
|
-
|
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
|
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
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
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
|
data/lib/duck_puncher/duck.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
23
|
-
@
|
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
|
data/lib/duck_puncher/ducks.rb
CHANGED
@@ -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
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
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
|
data/lib/duck_puncher/version.rb
CHANGED
@@ -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
|
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.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-
|
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
|