hobo_support 1.3.0.RC
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.
- data/CHANGES.txt +5 -0
- data/README.txt +36 -0
- data/Rakefile +16 -0
- data/VERSION +1 -0
- data/hobo_support.gemspec +29 -0
- data/lib/generators/hobo_support/eval_template.rb +14 -0
- data/lib/generators/hobo_support/model.rb +50 -0
- data/lib/generators/hobo_support/thor_shell.rb +40 -0
- data/lib/hobo_support.rb +25 -0
- data/lib/hobo_support/array.rb +40 -0
- data/lib/hobo_support/blankslate.rb +13 -0
- data/lib/hobo_support/command.rb +156 -0
- data/lib/hobo_support/common_tasks.rb +33 -0
- data/lib/hobo_support/enumerable.rb +89 -0
- data/lib/hobo_support/fixes/chronic.rb +21 -0
- data/lib/hobo_support/fixes/module.rb +37 -0
- data/lib/hobo_support/fixes/pp.rb +13 -0
- data/lib/hobo_support/hash.rb +124 -0
- data/lib/hobo_support/implies.rb +15 -0
- data/lib/hobo_support/kernel.rb +16 -0
- data/lib/hobo_support/metaid.rb +28 -0
- data/lib/hobo_support/methodcall.rb +117 -0
- data/lib/hobo_support/methodphitamine.rb +33 -0
- data/lib/hobo_support/module.rb +102 -0
- data/lib/hobo_support/string.rb +34 -0
- data/lib/hobo_support/xss.rb +11 -0
- data/test/hobosupport.rdoctest +75 -0
- data/test/hobosupport/chronic.rdoctest +18 -0
- data/test/hobosupport/enumerable.rdoctest +130 -0
- data/test/hobosupport/hash.rdoctest +123 -0
- data/test/hobosupport/implies.rdoctest +20 -0
- data/test/hobosupport/metaid.rdoctest +59 -0
- data/test/hobosupport/methodphitamine.rdoctest +18 -0
- data/test/hobosupport/module.rdoctest +108 -0
- data/test/hobosupport/xss.rdoctest +44 -0
- metadata +111 -0
@@ -0,0 +1,89 @@
|
|
1
|
+
module Enumerable
|
2
|
+
|
3
|
+
def map_and_find(not_found=nil)
|
4
|
+
each do |x|
|
5
|
+
val = yield(x)
|
6
|
+
return val if val
|
7
|
+
end
|
8
|
+
not_found
|
9
|
+
end
|
10
|
+
|
11
|
+
def map_with_index(res=[])
|
12
|
+
each_with_index {|x, i| res << yield(x, i)}
|
13
|
+
res
|
14
|
+
end
|
15
|
+
|
16
|
+
def build_hash(res={})
|
17
|
+
each do |x|
|
18
|
+
pair = block_given? ? yield(x) : x
|
19
|
+
res[pair.first] = pair.last if pair
|
20
|
+
end
|
21
|
+
res
|
22
|
+
end
|
23
|
+
|
24
|
+
def map_hash(res={})
|
25
|
+
each do |x|
|
26
|
+
v = yield x
|
27
|
+
res[x] = v
|
28
|
+
end
|
29
|
+
res
|
30
|
+
end
|
31
|
+
|
32
|
+
def rest
|
33
|
+
self[1..-1] || []
|
34
|
+
end
|
35
|
+
|
36
|
+
class MultiSender
|
37
|
+
|
38
|
+
undef_method(*(instance_methods.map{|m| m.to_s} - %w*__id__ __send__ object_id*))
|
39
|
+
|
40
|
+
def initialize(enumerable, method)
|
41
|
+
@enumerable = enumerable
|
42
|
+
@method = method
|
43
|
+
end
|
44
|
+
|
45
|
+
def method_missing(name, *args, &block)
|
46
|
+
@enumerable.send(@method) { |x| x.send(name, *args, &block) }
|
47
|
+
end
|
48
|
+
|
49
|
+
end
|
50
|
+
|
51
|
+
def *()
|
52
|
+
MultiSender.new(self, :map)
|
53
|
+
end
|
54
|
+
|
55
|
+
def where
|
56
|
+
MultiSender.new(self, :select)
|
57
|
+
end
|
58
|
+
|
59
|
+
def where_not
|
60
|
+
MultiSender.new(self, :reject)
|
61
|
+
end
|
62
|
+
|
63
|
+
def drop_while
|
64
|
+
drop = 0
|
65
|
+
drop += 1 while yield(self[drop])
|
66
|
+
self[drop..-1]
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def take_while
|
71
|
+
take = 0
|
72
|
+
take += 1 while yield(self[take])
|
73
|
+
self[0..take-1]
|
74
|
+
end
|
75
|
+
|
76
|
+
end
|
77
|
+
|
78
|
+
|
79
|
+
class Object
|
80
|
+
|
81
|
+
def in?(enum)
|
82
|
+
!enum.nil? && enum.include?(self)
|
83
|
+
end
|
84
|
+
|
85
|
+
def not_in?(enum)
|
86
|
+
enum.nil? || !enum.include?(self)
|
87
|
+
end
|
88
|
+
|
89
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# --- Fix Chronic - can't parse '12th Jan' --- #
|
2
|
+
begin
|
3
|
+
require 'chronic'
|
4
|
+
|
5
|
+
module Chronic
|
6
|
+
|
7
|
+
class << self
|
8
|
+
def parse_with_hobo_fix(s, *options)
|
9
|
+
if s =~ /^\s*\d+\s*(st|nd|rd|th)\s+[a-zA-Z]+(\s+\d+)?\s*$/
|
10
|
+
s = s.sub(/\s*\d+(st|nd|rd|th)/) {|s| s[0..-3]}
|
11
|
+
end
|
12
|
+
|
13
|
+
# Chronic can't parse '1/1/2008 1:00' or '1/1/2008 1:00 PM',
|
14
|
+
# so convert them to '1/1/2008 @ 1:00' and '1/1/2008 @ 1:00 PM'
|
15
|
+
s = s.sub(/^\s*(\d+\/\d+\/\d+)\s+(\d+:\d+.*)/, '\1 @ \2')
|
16
|
+
parse_without_hobo_fix(s, *options)
|
17
|
+
end
|
18
|
+
alias_method_chain :parse, :hobo_fix
|
19
|
+
end
|
20
|
+
end
|
21
|
+
rescue LoadError; end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
# Custom alias_method_chain that won't cause inifinite recursion if
|
4
|
+
# called twice.
|
5
|
+
# Calling alias_method_chain on alias_method_chain
|
6
|
+
# was just way to confusing, so I copied it :-/
|
7
|
+
def alias_method_chain(target, feature)
|
8
|
+
# Strip out punctuation on predicates or bang methods since
|
9
|
+
# e.g. target?_without_feature is not a valid method name.
|
10
|
+
aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
|
11
|
+
yield(aliased_target, punctuation) if block_given?
|
12
|
+
without = "#{aliased_target}_without_#{feature}#{punctuation}"
|
13
|
+
unless method_defined?(without)
|
14
|
+
alias_method without, target
|
15
|
+
alias_method target, "#{aliased_target}_with_#{feature}#{punctuation}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
# Fix delegate so it doesn't go bang if 'to' is nil
|
21
|
+
def delegate(*methods)
|
22
|
+
options = methods.pop
|
23
|
+
unless options.is_a?(Hash) && to = options[:to]
|
24
|
+
raise ArgumentError, ("Delegation needs a target. Supply an options hash with a :to key" +
|
25
|
+
"as the last argument (e.g. delegate :hello, :to => :greeter).")
|
26
|
+
end
|
27
|
+
|
28
|
+
methods.each do |method|
|
29
|
+
module_eval(<<-EOS, "(__DELEGATION__)", 1)
|
30
|
+
def #{method}(*args, &block)
|
31
|
+
(_to = #{to}) && _to.__send__(#{method.inspect}, *args, &block)
|
32
|
+
end
|
33
|
+
EOS
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,124 @@
|
|
1
|
+
class Hash
|
2
|
+
|
3
|
+
def select_hash(&b)
|
4
|
+
res = {}
|
5
|
+
each {|k,v| res[k] = v if (b.arity == 1 ? yield(v) : yield(k, v)) }
|
6
|
+
res
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
def map_hash(&b)
|
11
|
+
res = {}
|
12
|
+
each {|k,v| res[k] = b.arity == 1 ? yield(v) : yield(k, v) }
|
13
|
+
res
|
14
|
+
end
|
15
|
+
|
16
|
+
def partition_hash(keys=nil)
|
17
|
+
yes = {}
|
18
|
+
no = {}
|
19
|
+
each do |k,v|
|
20
|
+
if block_given? ? yield(k,v) : keys.include?(k)
|
21
|
+
yes[k] = v
|
22
|
+
else
|
23
|
+
no[k] = v
|
24
|
+
end
|
25
|
+
end
|
26
|
+
[yes, no]
|
27
|
+
end
|
28
|
+
|
29
|
+
def recursive_update(hash)
|
30
|
+
hash.each_pair do |key, value|
|
31
|
+
current = self[key]
|
32
|
+
if current.is_a?(Hash) and value.is_a?(Hash)
|
33
|
+
current.recursive_update(value)
|
34
|
+
else
|
35
|
+
self[key] = value
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
def -(keys)
|
41
|
+
res = {}
|
42
|
+
each_pair {|k, v| res[k] = v unless k.in?(keys)}
|
43
|
+
res
|
44
|
+
end
|
45
|
+
|
46
|
+
def &(keys)
|
47
|
+
res = {}
|
48
|
+
keys.each {|k| res[k] = self[k] if has_key?(k)}
|
49
|
+
res
|
50
|
+
end
|
51
|
+
|
52
|
+
alias_method :| , :merge
|
53
|
+
|
54
|
+
def get(*args)
|
55
|
+
args.map {|a| self[a] }
|
56
|
+
end
|
57
|
+
|
58
|
+
def compact
|
59
|
+
res = {}
|
60
|
+
each { |k, v| res[k] = v unless v.nil? }
|
61
|
+
res
|
62
|
+
end
|
63
|
+
|
64
|
+
def compact!
|
65
|
+
keys.each { |k| delete(k) if self[k].nil? }
|
66
|
+
end
|
67
|
+
|
68
|
+
# Ruby 1.9.1 complains about the use of index and recommends key
|
69
|
+
# but Ruby 1.8 doesn't have key. Add it.
|
70
|
+
alias_method(:key, :index) unless method_defined?(:key)
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
|
75
|
+
# HashWithIndifferentAccess from ActiveSupport needs different
|
76
|
+
# versions of these
|
77
|
+
|
78
|
+
if defined? HashWithIndifferentAccess
|
79
|
+
|
80
|
+
class HashWithIndifferentAccess
|
81
|
+
|
82
|
+
def -(keys)
|
83
|
+
res = HashWithIndifferentAccess.new
|
84
|
+
keys = keys.map {|k| k.is_a?(Symbol) ? k.to_s : k }
|
85
|
+
each_pair { |k, v| res[k] = v unless k.in?(keys) }
|
86
|
+
res
|
87
|
+
end
|
88
|
+
|
89
|
+
def &(keys)
|
90
|
+
res = HashWithIndifferentAccess.new
|
91
|
+
keys.each do |k|
|
92
|
+
k = k.to_s if k.is_a?(Symbol)
|
93
|
+
res[k] = self[k] if has_key?(k)
|
94
|
+
end
|
95
|
+
res
|
96
|
+
end
|
97
|
+
|
98
|
+
def partition_hash(keys=nil)
|
99
|
+
keys = keys._?.map {|k| k.is_a?(Symbol) ? k.to_s : k }
|
100
|
+
yes = HashWithIndifferentAccess.new
|
101
|
+
no = HashWithIndifferentAccess.new
|
102
|
+
each do |k,v|
|
103
|
+
if block_given? ? yield(k,v) : keys.include?(k)
|
104
|
+
yes[k] = v
|
105
|
+
else
|
106
|
+
no[k] = v
|
107
|
+
end
|
108
|
+
end
|
109
|
+
[yes, no]
|
110
|
+
end
|
111
|
+
|
112
|
+
end
|
113
|
+
|
114
|
+
end
|
115
|
+
|
116
|
+
if defined? ActiveSupport
|
117
|
+
class ActiveSupport::OrderedHash
|
118
|
+
alias each_pair each
|
119
|
+
|
120
|
+
def first
|
121
|
+
empty? ? nil : [keys.first, values.first]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# Thanks to _why
|
2
|
+
|
3
|
+
class Object
|
4
|
+
|
5
|
+
def metaclass; class << self; self; end; end
|
6
|
+
|
7
|
+
def meta_eval(src=nil, &blk)
|
8
|
+
if src
|
9
|
+
metaclass.instance_eval(src)
|
10
|
+
else
|
11
|
+
metaclass.instance_eval &blk
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
def metaclass_eval(src=nil, &blk)
|
16
|
+
if src
|
17
|
+
metaclass.class_eval(src)
|
18
|
+
else
|
19
|
+
metaclass.class_eval &blk
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
# Adds methods to a metaclass
|
24
|
+
def meta_def(name, &blk)
|
25
|
+
meta_eval { define_method name, &blk }
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
@@ -0,0 +1,117 @@
|
|
1
|
+
# The dot operator calls methods on objects. Power Dots are dots with extra features
|
2
|
+
#
|
3
|
+
# .? calls a method if the receiver is not nil, returns nil
|
4
|
+
# otherwise. We have to write it ._?. in order to be valid Ruby
|
5
|
+
#
|
6
|
+
# .try. calls a method only if the recipient responds to that method
|
7
|
+
|
8
|
+
require 'delegate'
|
9
|
+
require 'singleton'
|
10
|
+
require 'blankslate'
|
11
|
+
|
12
|
+
module HoboSupport
|
13
|
+
def self.hobo_try(this, *args, &block)
|
14
|
+
if args.length==0
|
15
|
+
# Hobo style try
|
16
|
+
CallIfAvailable.new(this)
|
17
|
+
else
|
18
|
+
# activesupport 2.3 style try
|
19
|
+
this.send(*args, &block)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Object
|
25
|
+
|
26
|
+
def _?()
|
27
|
+
self
|
28
|
+
end
|
29
|
+
|
30
|
+
def try(*args, &block)
|
31
|
+
HoboSupport.hobo_try(self, *args, &block)
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
|
37
|
+
class NilClass
|
38
|
+
def _?()
|
39
|
+
SafeNil.instance
|
40
|
+
end
|
41
|
+
|
42
|
+
|
43
|
+
def try(*args)
|
44
|
+
if args.length==0
|
45
|
+
# Hobo style try
|
46
|
+
CallIfAvailable.new(self)
|
47
|
+
else
|
48
|
+
# activesupport 2.3 style try
|
49
|
+
nil
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
class SafeNil
|
57
|
+
include Singleton
|
58
|
+
|
59
|
+
begin
|
60
|
+
old_verbose, $VERBOSE = $VERBOSE, nil # just like Rails silence_warnings: suppress "warning: undefining `object_id' may cause serious problem"
|
61
|
+
instance_methods.each { |m| undef_method m unless m[0,2] == '__' }
|
62
|
+
ensure
|
63
|
+
$VERBOSE = old_verbose
|
64
|
+
end
|
65
|
+
|
66
|
+
def method_missing(method, *args, &b)
|
67
|
+
return nil
|
68
|
+
end
|
69
|
+
end
|
70
|
+
|
71
|
+
|
72
|
+
alias DelegateClass_without_safe_nil DelegateClass
|
73
|
+
def DelegateClass(klass)
|
74
|
+
c = DelegateClass_without_safe_nil(klass)
|
75
|
+
c.class_eval do
|
76
|
+
def _?
|
77
|
+
self
|
78
|
+
end
|
79
|
+
end
|
80
|
+
c
|
81
|
+
end
|
82
|
+
|
83
|
+
|
84
|
+
class CallIfAvailable < BlankSlate
|
85
|
+
|
86
|
+
def initialize(target)
|
87
|
+
@target = target
|
88
|
+
end
|
89
|
+
|
90
|
+
def method_missing(name, *args, &b)
|
91
|
+
@target.send(name, *args, &b) if @target.respond_to?(name)
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
module ActiveRecord
|
97
|
+
module Associations
|
98
|
+
class AssociationProxy
|
99
|
+
|
100
|
+
# we need to make sure we don't trigger AssociationCollections' method_missing
|
101
|
+
def try(*args, &block)
|
102
|
+
HoboSupport.hobo_try(self, *args, &block)
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
module NamedScope
|
108
|
+
class Scope
|
109
|
+
# we need to make sure we don't trigger AssociationCollections' method_missing
|
110
|
+
def try(*args, &block)
|
111
|
+
HoboSupport.hobo_try(self, *args, &block)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
end
|
117
|
+
|