hobosupport 0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +5 -0
- data/Manifest.txt +25 -0
- data/README.txt +36 -0
- data/Rakefile +17 -0
- data/lib/hobosupport/array.rb +22 -0
- data/lib/hobosupport/blankslate.rb +10 -0
- data/lib/hobosupport/enumerable.rb +92 -0
- data/lib/hobosupport/fixes/chronic.rb +18 -0
- data/lib/hobosupport/fixes/module.rb +37 -0
- data/lib/hobosupport/fixes/pp.rb +13 -0
- data/lib/hobosupport/fixes.rb +3 -0
- data/lib/hobosupport/hash.rb +100 -0
- data/lib/hobosupport/implies.rb +15 -0
- data/lib/hobosupport/metaid.rb +28 -0
- data/lib/hobosupport/methodcall.rb +61 -0
- data/lib/hobosupport/methodphitamine.rb +29 -0
- data/lib/hobosupport/module.rb +67 -0
- data/lib/hobosupport.rb +41 -0
- data/test/hobosupport/enumerable.rdoctest +120 -0
- data/test/hobosupport/hash.rdoctest +103 -0
- data/test/hobosupport/implies.rdoctest +21 -0
- data/test/hobosupport/metaid.rdoctest +60 -0
- data/test/hobosupport/methodphitamine.rdoctest +19 -0
- data/test/hobosupport/module.rdoctest +72 -0
- data/test/hobosupport.rdoctest +53 -0
- metadata +81 -0
data/Manifest.txt
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
lib/hobosupport.rb
|
6
|
+
lib/hobosupport/array.rb
|
7
|
+
lib/hobosupport/blankslate.rb
|
8
|
+
lib/hobosupport/enumerable.rb
|
9
|
+
lib/hobosupport/fixes.rb
|
10
|
+
lib/hobosupport/fixes/chronic.rb
|
11
|
+
lib/hobosupport/fixes/module.rb
|
12
|
+
lib/hobosupport/fixes/pp.rb
|
13
|
+
lib/hobosupport/hash.rb
|
14
|
+
lib/hobosupport/implies.rb
|
15
|
+
lib/hobosupport/metaid.rb
|
16
|
+
lib/hobosupport/methodcall.rb
|
17
|
+
lib/hobosupport/methodphitamine.rb
|
18
|
+
lib/hobosupport/module.rb
|
19
|
+
test/hobosupport.rdoctest
|
20
|
+
test/hobosupport/enumerable.rdoctest
|
21
|
+
test/hobosupport/hash.rdoctest
|
22
|
+
test/hobosupport/implies.rdoctest
|
23
|
+
test/hobosupport/metaid.rdoctest
|
24
|
+
test/hobosupport/methodphitamine.rdoctest
|
25
|
+
test/hobosupport/module.rdoctest
|
data/README.txt
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
= hobosupport
|
2
|
+
|
3
|
+
* http://hobocentral.net/hobosupport
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
A mixed bag of core Ruby extenstion that have been extracted from the Hobo project.
|
8
|
+
|
9
|
+
== INSTALL:
|
10
|
+
|
11
|
+
* sudo gem install hobosupport
|
12
|
+
|
13
|
+
== LICENSE:
|
14
|
+
|
15
|
+
(The MIT License)
|
16
|
+
|
17
|
+
Copyright (c) 2008 Tom Locke
|
18
|
+
|
19
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
20
|
+
a copy of this software and associated documentation files (the
|
21
|
+
'Software'), to deal in the Software without restriction, including
|
22
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
23
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
24
|
+
permit persons to whom the Software is furnished to do so, subject to
|
25
|
+
the following conditions:
|
26
|
+
|
27
|
+
The above copyright notice and this permission notice shall be
|
28
|
+
included in all copies or substantial portions of the Software.
|
29
|
+
|
30
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
31
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
32
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
33
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
34
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
35
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
36
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'hoe'
|
3
|
+
|
4
|
+
$: << "./lib"
|
5
|
+
|
6
|
+
require 'hobosupport'
|
7
|
+
|
8
|
+
Hoe.new('hobosupport', HoboSupport::VERSION) do |p|
|
9
|
+
p.rubyforge_name = 'hobo'
|
10
|
+
p.author = 'Tom Locke'
|
11
|
+
p.email = 'tom@tomlocke.com'
|
12
|
+
p.url = "http://hobocentral.net/hobo-support"
|
13
|
+
p.summary = 'Core Ruby extensions from the Hobo project'
|
14
|
+
p.description = p.paragraphs_of('README.txt', 2..5).join("\n\n")
|
15
|
+
p.changes = p.paragraphs_of('History.txt', 0..1).join("\n\n")
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,22 @@
|
|
1
|
+
class Array
|
2
|
+
|
3
|
+
alias_method :multiply, :*
|
4
|
+
|
5
|
+
def *(rhs=nil)
|
6
|
+
if rhs
|
7
|
+
multiply(rhs)
|
8
|
+
else
|
9
|
+
Enumerable::MultiSender.new(self, :map)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def drop_while!
|
14
|
+
drop = 0
|
15
|
+
drop += 1 while yield(self[drop])
|
16
|
+
self[0..drop-1] = []
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
|
@@ -0,0 +1,92 @@
|
|
1
|
+
module Enumerable
|
2
|
+
|
3
|
+
def search(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
|
12
|
+
res = []
|
13
|
+
each_with_index {|x, i| res << yield(x, i)}
|
14
|
+
res
|
15
|
+
end
|
16
|
+
|
17
|
+
def build_hash
|
18
|
+
res = {}
|
19
|
+
each do |x|
|
20
|
+
pair = yield x
|
21
|
+
res[pair.first] = pair.last if pair
|
22
|
+
end
|
23
|
+
res
|
24
|
+
end
|
25
|
+
|
26
|
+
def map_hash
|
27
|
+
res = {}
|
28
|
+
each do |x|
|
29
|
+
v = yield x
|
30
|
+
res[x] = v
|
31
|
+
end
|
32
|
+
res
|
33
|
+
end
|
34
|
+
|
35
|
+
def rest
|
36
|
+
self[1..-1]
|
37
|
+
end
|
38
|
+
|
39
|
+
class MultiSender
|
40
|
+
|
41
|
+
undef_method(*(instance_methods - %w*__id__ __send__*))
|
42
|
+
|
43
|
+
def initialize(enumerable, method)
|
44
|
+
@enumerable = enumerable
|
45
|
+
@method = method
|
46
|
+
end
|
47
|
+
|
48
|
+
def method_missing(name, *args, &block)
|
49
|
+
@enumerable.send(@method) { |x| x.send(name, *args, &block) }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
|
54
|
+
def *()
|
55
|
+
MultiSender.new(self, :map)
|
56
|
+
end
|
57
|
+
|
58
|
+
def where
|
59
|
+
MultiSender.new(self, :select)
|
60
|
+
end
|
61
|
+
|
62
|
+
def where_not
|
63
|
+
MultiSender.new(self, :reject)
|
64
|
+
end
|
65
|
+
|
66
|
+
def drop_while
|
67
|
+
drop = 0
|
68
|
+
drop += 1 while yield(self[drop])
|
69
|
+
self[drop..-1]
|
70
|
+
end
|
71
|
+
|
72
|
+
|
73
|
+
def take_while
|
74
|
+
take = 0
|
75
|
+
take += 1 while yield(self[take])
|
76
|
+
self[0..take-1]
|
77
|
+
end
|
78
|
+
|
79
|
+
end
|
80
|
+
|
81
|
+
|
82
|
+
class Object
|
83
|
+
|
84
|
+
def in?(enum)
|
85
|
+
enum.include?(self)
|
86
|
+
end
|
87
|
+
|
88
|
+
def not_in?(enum)
|
89
|
+
not enum.include?(self)
|
90
|
+
end
|
91
|
+
|
92
|
+
end
|
@@ -0,0 +1,18 @@
|
|
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)
|
9
|
+
parse_without_hobo_fix(if s =~ /^\s*\d+\s*(st|nd|rd|th)\s+[a-zA-Z]+(\s+\d+)?\s*$/
|
10
|
+
s.sub(/\s*\d+(st|nd|rd|th)/) {|s| s[0..-3]}
|
11
|
+
else
|
12
|
+
s
|
13
|
+
end)
|
14
|
+
end
|
15
|
+
alias_method_chain :parse, :hobo_fix
|
16
|
+
end
|
17
|
+
end
|
18
|
+
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 instance_methods.include?(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,100 @@
|
|
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
|
+
end
|
59
|
+
|
60
|
+
|
61
|
+
# HashWithIndifferentAccess from ActiveSupport needs different
|
62
|
+
# versions of these
|
63
|
+
|
64
|
+
if defined? HashWithIndifferentAccess
|
65
|
+
|
66
|
+
class HashWithIndifferentAccess
|
67
|
+
|
68
|
+
def -(keys)
|
69
|
+
res = HashWithIndifferentAccess.new
|
70
|
+
keys = keys.map {|k| k.is_a?(Symbol) ? k.to_s : k }
|
71
|
+
each_pair { |k, v| res[k] = v unless k.in?(keys) }
|
72
|
+
res
|
73
|
+
end
|
74
|
+
|
75
|
+
def &(keys)
|
76
|
+
res = HashWithIndifferentAccess.new
|
77
|
+
keys.each do |k|
|
78
|
+
k = k.to_s if k.is_a?(Symbol)
|
79
|
+
res[k] = self[k] if has_key?(k)
|
80
|
+
end
|
81
|
+
res
|
82
|
+
end
|
83
|
+
|
84
|
+
def partition_hash(keys=nil)
|
85
|
+
keys = keys._?.map {|k| k.is_a?(Symbol) ? k.to_s : k }
|
86
|
+
yes = HashWithIndifferentAccess.new
|
87
|
+
no = HashWithIndifferentAccess.new
|
88
|
+
each do |k,v|
|
89
|
+
if block_given? ? yield(k,v) : keys.include?(k)
|
90
|
+
yes[k] = v
|
91
|
+
else
|
92
|
+
no[k] = v
|
93
|
+
end
|
94
|
+
end
|
95
|
+
[yes, no]
|
96
|
+
end
|
97
|
+
|
98
|
+
end
|
99
|
+
|
100
|
+
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,61 @@
|
|
1
|
+
# The dot operator calls methods on objects. Power Dots are dots with extra features
|
2
|
+
#
|
3
|
+
# .? calls a method if the reciever is not nil, returns nil
|
4
|
+
# otherwise. We have to write it ._?. in order to be valid Ruby
|
5
|
+
#
|
6
|
+
# .try. calls a mehod only if the recipient resonds to that method
|
7
|
+
|
8
|
+
require 'delegate'
|
9
|
+
require 'singleton'
|
10
|
+
|
11
|
+
class Object
|
12
|
+
|
13
|
+
def _?()
|
14
|
+
self
|
15
|
+
end
|
16
|
+
|
17
|
+
def try
|
18
|
+
CallIfAvailable.new(self)
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
class NilClass
|
25
|
+
def _?()
|
26
|
+
SafeNil.instance
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
|
31
|
+
class SafeNil
|
32
|
+
include Singleton
|
33
|
+
|
34
|
+
def method_missing(method, *args, &b)
|
35
|
+
return nil unless nil.respond_to? method
|
36
|
+
nil.send(method, *args, &b) rescue nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
|
41
|
+
alias DelegateClass_without_safe_nil DelegateClass
|
42
|
+
def DelegateClass(klass)
|
43
|
+
c = DelegateClass_without_safe_nil(klass)
|
44
|
+
c.class_eval do
|
45
|
+
def _?
|
46
|
+
self
|
47
|
+
end
|
48
|
+
end
|
49
|
+
c
|
50
|
+
end
|
51
|
+
|
52
|
+
|
53
|
+
class CallIfAvailable < BlankSlate
|
54
|
+
|
55
|
+
def method_missing(name, *args, &b)
|
56
|
+
@me.send(name, *args, &b) if @me.respond_to?(name)
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
60
|
+
|
61
|
+
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# From: http://jicksta.com/articles/2007/08/04/the-methodphitamine
|
2
|
+
|
3
|
+
module Kernel
|
4
|
+
|
5
|
+
def it() It.new end
|
6
|
+
alias its it
|
7
|
+
|
8
|
+
end
|
9
|
+
|
10
|
+
class It < BlankSlate
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
@methods = []
|
14
|
+
end
|
15
|
+
|
16
|
+
def method_missing(*args, &block)
|
17
|
+
@methods << [args, block] unless args == [:respond_to?, :to_proc]
|
18
|
+
self
|
19
|
+
end
|
20
|
+
|
21
|
+
def to_proc
|
22
|
+
lambda do |obj|
|
23
|
+
@methods.inject(obj) do |current,(args,block)|
|
24
|
+
current.send(*args, &block)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
class Module
|
2
|
+
|
3
|
+
private
|
4
|
+
|
5
|
+
# In a module definition you can include a call to
|
6
|
+
# included_in_class_callbacks(base) at the end of the
|
7
|
+
# self.included(base) callback. Any modules that your module includes
|
8
|
+
# will receive an included_in_class callback when your module is
|
9
|
+
# included in a class. Useful if the sub-module wants to do something
|
10
|
+
# like alias_method_chain on the class.
|
11
|
+
def included_in_class_callbacks(base)
|
12
|
+
if base.is_a?(Class)
|
13
|
+
included_modules.each { |m| m.included_in_class(base) if m.respond_to?(:included_in_class) }
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# An attr_accessor for classes that will delegate to the superclass
|
18
|
+
# if not defined on self
|
19
|
+
def inheriting_attr_reader(*names)
|
20
|
+
for name in names
|
21
|
+
class_eval %{
|
22
|
+
def #{name}
|
23
|
+
if defined? @#{name}
|
24
|
+
@#{name}
|
25
|
+
elsif superclass.respond_to?('#{name}')
|
26
|
+
superclass.#{name}
|
27
|
+
end
|
28
|
+
end
|
29
|
+
}
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# creates a #foo= and #foo? pair, with optional default values, e.g.
|
34
|
+
# bool_attr_accessor :happy => true
|
35
|
+
def bool_attr_accessor(*args)
|
36
|
+
options = args.extract_options!
|
37
|
+
(args + options.keys).each {|n| class_eval "def #{n}=(x); @#{n} = x; end" }
|
38
|
+
|
39
|
+
args.each {|n| class_eval "def #{n}?; @#{n}; end" }
|
40
|
+
|
41
|
+
options.keys.each do |n|
|
42
|
+
class_eval %(def #{n}?
|
43
|
+
if !defined? @#{n}
|
44
|
+
@#{n} = #{options[n] ? 'true' : 'false'}
|
45
|
+
else
|
46
|
+
@#{n}
|
47
|
+
end
|
48
|
+
end)
|
49
|
+
set_field_type(n => TrueClass) if respond_to?(:set_field_type)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
end
|
54
|
+
|
55
|
+
|
56
|
+
# classy_module lets you extract code from classes into modules, but
|
57
|
+
# still write it the same way
|
58
|
+
module Kernel
|
59
|
+
|
60
|
+
def classy_module(&b)
|
61
|
+
m = Module.new
|
62
|
+
m.meta_def :included do |base|
|
63
|
+
base.class_eval &b
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
data/lib/hobosupport.rb
ADDED
@@ -0,0 +1,41 @@
|
|
1
|
+
require "hobosupport/fixes"
|
2
|
+
require 'hobosupport/blankslate'
|
3
|
+
require 'hobosupport/methodcall'
|
4
|
+
require 'hobosupport/methodphitamine'
|
5
|
+
require 'hobosupport/metaid'
|
6
|
+
require 'hobosupport/implies'
|
7
|
+
require 'hobosupport/enumerable'
|
8
|
+
require 'hobosupport/array'
|
9
|
+
require 'hobosupport/hash'
|
10
|
+
require 'hobosupport/module'
|
11
|
+
|
12
|
+
module HoboSupport
|
13
|
+
|
14
|
+
VERSION = "0.1"
|
15
|
+
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
# Some teeny fixes too diminutive to go elsewhere
|
20
|
+
|
21
|
+
class Object
|
22
|
+
|
23
|
+
# Often nice to ask e.g. some_object.is_a?(Symbol, String)
|
24
|
+
alias_method :is_a_without_multiple_args?, :is_a?
|
25
|
+
def is_a?(*args)
|
26
|
+
args.any? {|a| is_a_without_multiple_args?(a) }
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
|
32
|
+
# --- Rails extensions --- #
|
33
|
+
|
34
|
+
# Nice tip from Jamis Buck
|
35
|
+
if defined? ActiveRecord
|
36
|
+
class << ActiveRecord::Base
|
37
|
+
alias_method :[], :find
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
|
@@ -0,0 +1,120 @@
|
|
1
|
+
# HoboSupport - Eumerable extensions
|
2
|
+
|
3
|
+
>> require 'hobosupport'
|
4
|
+
>> HoboSupport::VERSION
|
5
|
+
=> "0.1"
|
6
|
+
|
7
|
+
## Enumerable#search
|
8
|
+
|
9
|
+
* `enum.search(not_found) { |element| block}`
|
10
|
+
|
11
|
+
Iterates through an enumerable passing each element in turn to the block. Returns the first true value returned by the block, or `not_found` if the block never returns a true value.
|
12
|
+
|
13
|
+
E.g. The length of the first word that starts with a capital letter
|
14
|
+
|
15
|
+
>> %w(my name is Fred).search { |s| s.length if s =~ /^[A-Z]/ }
|
16
|
+
=> 4
|
17
|
+
|
18
|
+
## Enumerable#map_with_index
|
19
|
+
|
20
|
+
* `enum.map_with_index { |element, index| block }`
|
21
|
+
|
22
|
+
Just like #map but the block gets the element and the index.
|
23
|
+
|
24
|
+
>> %w(some short words).map_with_index { |s, i| s * i }
|
25
|
+
=> ["", "short", "wordswords"]
|
26
|
+
|
27
|
+
## Enumerable#build_hash
|
28
|
+
|
29
|
+
* `enum.build_hash { |element| block }`
|
30
|
+
|
31
|
+
The block is passed each element in turn and should return a key/value pair. If the block returns nil, nothing is added to the hash.
|
32
|
+
|
33
|
+
>> %w(some short words).build_hash { |s| [s, s.length] unless s == "short" }
|
34
|
+
=> {"some"=>4, "words"=>5}
|
35
|
+
|
36
|
+
|
37
|
+
## Enumerable#map_hash
|
38
|
+
|
39
|
+
* `enum.map_hash { |element| block }`
|
40
|
+
|
41
|
+
Each element is passed to the block. Returns a hash where the keys are the elements from the enumerable, and the values are those returned by the block for the given key.
|
42
|
+
|
43
|
+
>> %w(some short words).map_hash { |s| s.length }
|
44
|
+
=> {"some"=>4, "short"=>5, "words"=>5}
|
45
|
+
|
46
|
+
## Enumerable#rest
|
47
|
+
|
48
|
+
Shorthand for `enum[1..-1]`
|
49
|
+
|
50
|
+
>> %w(some short words).rest
|
51
|
+
=> ["short", "words"]
|
52
|
+
|
53
|
+
|
54
|
+
## Enumerable#*
|
55
|
+
|
56
|
+
Shorthand for `map` when you need to map a single method call on the elements.
|
57
|
+
|
58
|
+
>> %w(some short words).*.length
|
59
|
+
=> [4, 5, 5]
|
60
|
+
|
61
|
+
## Enumerable#where
|
62
|
+
|
63
|
+
Shorthand for `select` when you need to select on a single method call.
|
64
|
+
|
65
|
+
>> %w(some short words).where.include?("r")
|
66
|
+
=> ["short", "words"]
|
67
|
+
|
68
|
+
## Enumerable#where_not
|
69
|
+
|
70
|
+
Shorthand for `reject` when you need to reject on a single method call.
|
71
|
+
|
72
|
+
>> %w(some short words).where_not.include?("r")
|
73
|
+
=> ["some"]
|
74
|
+
|
75
|
+
## Enumerable#drop_while
|
76
|
+
|
77
|
+
* `enum.drop_while { |element| block }`
|
78
|
+
|
79
|
+
Passes each element in turn to the block until the block returns false. Returns the remaining elements in a new array.
|
80
|
+
|
81
|
+
>> %w(this is a nice example).drop_while { |s| s.length > 1 }
|
82
|
+
=> ["a", "nice", "example"]
|
83
|
+
|
84
|
+
There is also a destructive version for arrays
|
85
|
+
|
86
|
+
>> a = %w(this is a nice example)
|
87
|
+
>> a.drop_while! { |s| s.length > 1 }
|
88
|
+
>> a
|
89
|
+
=> ["a", "nice", "example"]
|
90
|
+
|
91
|
+
|
92
|
+
## Enumerable#take_while
|
93
|
+
|
94
|
+
* `enum.take_while { |element| block }`
|
95
|
+
|
96
|
+
Passes each element in turn to the block until the block returns false. Returns a new with the elements for which the block returned true
|
97
|
+
|
98
|
+
>> %w(this is a nice example).take_while { |s| s.length > 1 }
|
99
|
+
=> ["this", "is"]
|
100
|
+
|
101
|
+
|
102
|
+
## Object#in?
|
103
|
+
|
104
|
+
* `obj.in?(enum)`
|
105
|
+
|
106
|
+
Shorthand for `enum.include?(obj)`
|
107
|
+
|
108
|
+
>> 3.in?(0..10)
|
109
|
+
=> true
|
110
|
+
>> 300.in?(0..10)
|
111
|
+
=> false
|
112
|
+
|
113
|
+
## Object#not_in?
|
114
|
+
|
115
|
+
* `obj.not_in?(enum)`
|
116
|
+
|
117
|
+
>> 3.not_in?(0..10)
|
118
|
+
=> false
|
119
|
+
>> 300.not_in?(0..10)
|
120
|
+
=> true
|
@@ -0,0 +1,103 @@
|
|
1
|
+
# HoboSupport - Hash extensions
|
2
|
+
|
3
|
+
>> require 'hobosupport'
|
4
|
+
>> HoboSupport::VERSION
|
5
|
+
=> "0.1"
|
6
|
+
|
7
|
+
## Hash#select_hash
|
8
|
+
|
9
|
+
* `hash.select_hash { |key, value| block } => hash`
|
10
|
+
* `hash.select_hash { |value| block } => hash`
|
11
|
+
|
12
|
+
This is the Hash version of `Enumerable#select`. i.e. a way to create a new hash with only some of the items still present. The block is passed each key value pair. The new hash only contains items for which the block returned true.
|
13
|
+
|
14
|
+
>> {1=>2, 3=>4, 6=>5}.select_hash { |key, value| key < value }
|
15
|
+
=> {1=>2, 3=>4}
|
16
|
+
|
17
|
+
You can also give a block that takes one argument, in which case the block is given the value only
|
18
|
+
|
19
|
+
>> {1=>2, 3=>4, 6=>5}.select_hash { |value| value != 2 }
|
20
|
+
=> {3=>4, 6=>5}
|
21
|
+
|
22
|
+
|
23
|
+
## Hash#map_hash
|
24
|
+
|
25
|
+
* `hash.map_hash { |key, value| block } => hash`
|
26
|
+
* `hash.map_hash { |value| block } => hash`
|
27
|
+
|
28
|
+
Applies a function to each *value* in a Hash, resulting in a new hash with the same keys but new values. The block is passed each key, value pair and should return the new value
|
29
|
+
|
30
|
+
>> {1=>2, 3=>4, 6=>5}.map_hash { |key, value| key < value }
|
31
|
+
=> {1=>true, 3=>true, 6=>false}
|
32
|
+
|
33
|
+
You can also give a block which takes one argument, in which case only the value is passed in
|
34
|
+
|
35
|
+
>> {1=>2, 3=>4, 6=>5}.map_hash { |value| value * 100 }
|
36
|
+
=> {1=>200, 3=>400, 6=>500 }
|
37
|
+
|
38
|
+
|
39
|
+
## Hash#partition_hash
|
40
|
+
|
41
|
+
* `hash.partition_hash(keys) => [hash1, hash2]`
|
42
|
+
* `hash.partition_hash { |key, value| block } => hash`
|
43
|
+
|
44
|
+
Returns an array of two hashes, the first with all the key/value pairs where the key was in passed Enumerable, the second with the remaining key/value pairs
|
45
|
+
|
46
|
+
>> {1=>2, 3=>4, 6=>5}.partition_hash([1, 3])
|
47
|
+
=> [{1=>2, 3=>4}, {6=>5}]
|
48
|
+
|
49
|
+
When passed a block, each pair is passed to the block in turn. The result is two hashes, the first containing those pairs for which the block returned true, the second with the remaining pairs
|
50
|
+
|
51
|
+
>> {1=>2, 3=>4, 6=>5}.partition_hash { |key, value| key < value }
|
52
|
+
=> [{1=>2, 3=>4}, {6=>5}]
|
53
|
+
|
54
|
+
## Hash.recursive_update
|
55
|
+
|
56
|
+
* `hash.recursive_update(hash2)`
|
57
|
+
|
58
|
+
Like `#update` but where a sub-hash would overwrite another sub-hash, they are instead also merged, recursively
|
59
|
+
|
60
|
+
>> h = { :a => 1, :b => { :x => 10 } }
|
61
|
+
>> h.recursive_update({ :c => 3, :b => { :y => 20 } })
|
62
|
+
>> h
|
63
|
+
=> { :a => 1, :b => { :x => 10, :y => 20}, :c => 3 }
|
64
|
+
|
65
|
+
|
66
|
+
## Hash#-
|
67
|
+
|
68
|
+
* `hash - array => hash`
|
69
|
+
|
70
|
+
Returns a new hash, the left-hand-side hash with all pairs removed where the key is present in the right-hand-side array.
|
71
|
+
|
72
|
+
>> {1=>2, 3=>4, 6=>5} - [1, 3]
|
73
|
+
=> {6 => 5}
|
74
|
+
|
75
|
+
## Hash#&
|
76
|
+
|
77
|
+
* `hash & array => array`
|
78
|
+
|
79
|
+
Returns a new array, the left hand side hash restricted to pairs where the key is present in the right-hand-side array
|
80
|
+
|
81
|
+
>> {1=>2, 3=>4, 6=>5} & [1, 3]
|
82
|
+
=> {1=>2, 3=>4}
|
83
|
+
|
84
|
+
## Hash#|
|
85
|
+
|
86
|
+
* `hash | hash => hash`
|
87
|
+
|
88
|
+
An alias for merge
|
89
|
+
|
90
|
+
>> {1 => 2} | {1 => 3, 2 => 4}
|
91
|
+
=> {1 => 3, 2 => 4}
|
92
|
+
|
93
|
+
|
94
|
+
## Hash#get
|
95
|
+
|
96
|
+
* `hash.get(*args) => hash`
|
97
|
+
|
98
|
+
Returns an array of values for the given keys. Useful for extracting a few options into local variables.
|
99
|
+
|
100
|
+
>> {1=>2, 3=>4, 6=>5}.get(1, 3)
|
101
|
+
=> [2, 4]
|
102
|
+
|
103
|
+
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# HoboSupport - implies
|
2
|
+
|
3
|
+
>> require 'hobosupport'
|
4
|
+
>> HoboSupport::VERSION
|
5
|
+
=> "0.1"
|
6
|
+
|
7
|
+
The implies operator comes from Eiffel. Often comes in handy in Hobo's model-permission methods.
|
8
|
+
|
9
|
+
## TrueClass#implies and FalseClass#implies
|
10
|
+
|
11
|
+
>> false.implies true
|
12
|
+
=> true
|
13
|
+
|
14
|
+
>> false.implies false
|
15
|
+
=> true
|
16
|
+
|
17
|
+
>> true.implies true
|
18
|
+
=> true
|
19
|
+
|
20
|
+
>> true.implies false
|
21
|
+
=> false
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# HoboSupport - Metaid
|
2
|
+
|
3
|
+
>> require 'hobosupport'
|
4
|
+
>> HoboSupport::VERSION
|
5
|
+
=> "0.1"
|
6
|
+
|
7
|
+
Why the Luck Stiff's essential meta-programming additions to Object. These are probably distributed elsewhere, but they're small enough to throw them in to HoboSupport and remove an external dependency.
|
8
|
+
|
9
|
+
## Object#metaclass
|
10
|
+
|
11
|
+
Returns the "metaclass" (bad name) or "singleton class" of a given ruby Object
|
12
|
+
|
13
|
+
>> o = Object.new
|
14
|
+
>> def o.foo; ;123; end
|
15
|
+
>> o.foo
|
16
|
+
=> 123
|
17
|
+
>> o.metaclass.instance_methods.grep "foo"
|
18
|
+
=> ["foo"]
|
19
|
+
|
20
|
+
## Object#meta_eval
|
21
|
+
|
22
|
+
* `object.meta_eval(string)`
|
23
|
+
* `object.meta_eval { block }`
|
24
|
+
|
25
|
+
Evaluates ruby source or a block in the context of the metaclass.
|
26
|
+
|
27
|
+
>> File.meta_eval "alias_method :b, :basename"
|
28
|
+
>> File.b "a/b"
|
29
|
+
=> "b"
|
30
|
+
|
31
|
+
And with a block
|
32
|
+
|
33
|
+
>> File.meta_eval { alias_method :b2, :basename }
|
34
|
+
>> File.b2 "a/b"
|
35
|
+
=> "b"
|
36
|
+
|
37
|
+
## Object#metaclass_eval
|
38
|
+
|
39
|
+
* `object.metaclass_eval(string)`
|
40
|
+
* `object.metaclass_eval { block }`
|
41
|
+
|
42
|
+
Like #meta_eval, but does a `class_eval` instead of an `instance_eval`
|
43
|
+
|
44
|
+
>> File.metaclass_eval "def b3(path); basename(path); end"
|
45
|
+
>> File.b3 "a/b"
|
46
|
+
=> "b"
|
47
|
+
|
48
|
+
And with a block
|
49
|
+
|
50
|
+
>> File.metaclass_eval { def b4(path); basename(path); end }
|
51
|
+
>> File.b4 "a/b"
|
52
|
+
=> "b"
|
53
|
+
|
54
|
+
|
55
|
+
## Object#meta_def
|
56
|
+
|
57
|
+
>> String.meta_def(:backwards_new) { |s| s.reverse }
|
58
|
+
>> String.backwards_new "strange"
|
59
|
+
=> "egnarts"
|
60
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# HoboSupport - Methodphitamine
|
2
|
+
|
3
|
+
>> require 'hobosupport'
|
4
|
+
>> HoboSupport::VERSION
|
5
|
+
=> "0.1"
|
6
|
+
|
7
|
+
## Kernel#it and Kernel#its
|
8
|
+
|
9
|
+
A nice hack from Jay Phillips, christened "Methodphitamine". The low-down is in the [original blog post][1].
|
10
|
+
|
11
|
+
[1]: http://jicksta.com/articles/2007/08/04/the-methodphitamine
|
12
|
+
|
13
|
+
>> (1..10).select &it % 2 == 0
|
14
|
+
=> [2, 4, 6, 8, 10]
|
15
|
+
>> %w(a few short words).map &its.length
|
16
|
+
=> [1, 3, 5, 5]
|
17
|
+
|
18
|
+
Note that `it` and `its` are identical. They just read better in different contexts. Note that methodphitamine is also released directly by Jay (simply `gem install methodphitamine`) but it's small enough that it seemed easier to add it to HoboSupport and avoid an extra dependency. Thanks to Jay.
|
19
|
+
|
@@ -0,0 +1,72 @@
|
|
1
|
+
# HoboSupport - Module extensions
|
2
|
+
|
3
|
+
>> require 'hobosupport'
|
4
|
+
>> HoboSupport::VERSION
|
5
|
+
=> "0.1"
|
6
|
+
|
7
|
+
## Module#included_in_class_callbacks
|
8
|
+
|
9
|
+
Bit involved, this one :-)
|
10
|
+
|
11
|
+
When a module is included in a class, it gets a callback on the `included` method, with the class passed as argument. However, if a module M2 is included in M1, and a class C includes M1, then M2 never gets to know about C. So, for example, M2 could not `alias_method_chain` a class method on C.
|
12
|
+
|
13
|
+
`included_in_class_callbacks` makes it easy to implement a notification from M1 to M2, so that M2 does have full access to the class. All you do is insert a call to `included_in_class_callbacks(base)` as the end of the module's `self.included` method.
|
14
|
+
|
15
|
+
(Note we're using the metaid extensions here too)
|
16
|
+
|
17
|
+
>>
|
18
|
+
module M2
|
19
|
+
def self.included_in_class(klass)
|
20
|
+
klass.metaclass_eval do
|
21
|
+
def name_with_shouting; name_without_shouting.upcase; end
|
22
|
+
alias_method_chain :name, :shouting
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
module M1
|
28
|
+
def self.included(base)
|
29
|
+
included_in_class_callbacks(base)
|
30
|
+
end
|
31
|
+
include M2
|
32
|
+
end
|
33
|
+
|
34
|
+
class C
|
35
|
+
def self.name
|
36
|
+
"my name is C"
|
37
|
+
end
|
38
|
+
include M1
|
39
|
+
end
|
40
|
+
|
41
|
+
C.name
|
42
|
+
=> "MY NAME IS C"
|
43
|
+
|
44
|
+
|
45
|
+
## Module#interiting_attr_accessor
|
46
|
+
|
47
|
+
Only for use on classes. Like `attr_accessor`, but the attribute is looked up on the superclass if not defined on the receiving class. In other words, the superclass defines a default that subclasses can override.
|
48
|
+
|
49
|
+
>>
|
50
|
+
class A
|
51
|
+
class << self
|
52
|
+
inheriting_attr_reader :name
|
53
|
+
end
|
54
|
+
@name = "Andy"
|
55
|
+
end
|
56
|
+
|
57
|
+
class B < A; end
|
58
|
+
|
59
|
+
`B` has the same name as its superclass `A`
|
60
|
+
|
61
|
+
>> A.name
|
62
|
+
=> "Andy"
|
63
|
+
>> B.name
|
64
|
+
=> "Andy"
|
65
|
+
|
66
|
+
Now we change the name of `B`. `A` retains it's existing name.
|
67
|
+
|
68
|
+
>> class B; @name = "Bob"; end
|
69
|
+
>> B.name
|
70
|
+
=> "Bob"
|
71
|
+
>> A.name
|
72
|
+
=> "Andy"
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# HoboSupport
|
2
|
+
|
3
|
+
HoboSupport is a mixed bag of core ruby extensions that have been extracted from the [Hobo][] project
|
4
|
+
|
5
|
+
[Hobo]: http://hobocentral.net
|
6
|
+
|
7
|
+
>> require 'hobosupport'
|
8
|
+
>> HoboSupport::VERSION
|
9
|
+
=> "0.1"
|
10
|
+
|
11
|
+
## Object extensions
|
12
|
+
|
13
|
+
### Object#is_a?
|
14
|
+
|
15
|
+
Extended to allow multiple types to be checked in one go
|
16
|
+
|
17
|
+
>> "foo".is_a?(String, Symbol)
|
18
|
+
=> true
|
19
|
+
>> :foo.is_a?(String, Symbol)
|
20
|
+
=> true
|
21
|
+
>> 1.is_a?(String, Symbol)
|
22
|
+
=> false
|
23
|
+
|
24
|
+
Still works the old way
|
25
|
+
|
26
|
+
>> "foo".is_a?(String)
|
27
|
+
=> true
|
28
|
+
>> :foo.is_a?(String)
|
29
|
+
=> false
|
30
|
+
|
31
|
+
|
32
|
+
## Method call extensions
|
33
|
+
|
34
|
+
### Object#_?
|
35
|
+
|
36
|
+
We have the "." operator to call methods on objects. These extensions introduce two "special dots". "`._?.`" only calls the method if the receiver is not `nil`.
|
37
|
+
|
38
|
+
>> "foo"._?.length
|
39
|
+
=> 3
|
40
|
+
>> nil._?.length
|
41
|
+
=> nil
|
42
|
+
|
43
|
+
|
44
|
+
### Object#try
|
45
|
+
|
46
|
+
"`.try`" only calls the method if the receiver responds to that method.
|
47
|
+
|
48
|
+
>> "foo".try.length
|
49
|
+
=> 3
|
50
|
+
>> :foo.try.length
|
51
|
+
=> nil
|
52
|
+
|
53
|
+
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
rubygems_version: 0.9.4
|
3
|
+
specification_version: 1
|
4
|
+
name: hobosupport
|
5
|
+
version: !ruby/object:Gem::Version
|
6
|
+
version: "0.1"
|
7
|
+
date: 2008-02-11 00:00:00 +00:00
|
8
|
+
summary: Core Ruby extensions from the Hobo project
|
9
|
+
require_paths:
|
10
|
+
- lib
|
11
|
+
email: tom@tomlocke.com
|
12
|
+
homepage: http://hobocentral.net/hobo-support
|
13
|
+
rubyforge_project: hobo
|
14
|
+
description: "== DESCRIPTION: A mixed bag of core Ruby extenstion that have been extracted from the Hobo project. == INSTALL: * sudo gem install hobosupport"
|
15
|
+
autorequire:
|
16
|
+
default_executable:
|
17
|
+
bindir: bin
|
18
|
+
has_rdoc: true
|
19
|
+
required_ruby_version: !ruby/object:Gem::Version::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">"
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.0.0
|
24
|
+
version:
|
25
|
+
platform: ruby
|
26
|
+
signing_key:
|
27
|
+
cert_chain:
|
28
|
+
post_install_message:
|
29
|
+
authors:
|
30
|
+
- Tom Locke
|
31
|
+
files:
|
32
|
+
- History.txt
|
33
|
+
- Manifest.txt
|
34
|
+
- README.txt
|
35
|
+
- Rakefile
|
36
|
+
- lib/hobosupport.rb
|
37
|
+
- lib/hobosupport/array.rb
|
38
|
+
- lib/hobosupport/blankslate.rb
|
39
|
+
- lib/hobosupport/enumerable.rb
|
40
|
+
- lib/hobosupport/fixes.rb
|
41
|
+
- lib/hobosupport/fixes/chronic.rb
|
42
|
+
- lib/hobosupport/fixes/module.rb
|
43
|
+
- lib/hobosupport/fixes/pp.rb
|
44
|
+
- lib/hobosupport/hash.rb
|
45
|
+
- lib/hobosupport/implies.rb
|
46
|
+
- lib/hobosupport/metaid.rb
|
47
|
+
- lib/hobosupport/methodcall.rb
|
48
|
+
- lib/hobosupport/methodphitamine.rb
|
49
|
+
- lib/hobosupport/module.rb
|
50
|
+
- test/hobosupport.rdoctest
|
51
|
+
- test/hobosupport/enumerable.rdoctest
|
52
|
+
- test/hobosupport/hash.rdoctest
|
53
|
+
- test/hobosupport/implies.rdoctest
|
54
|
+
- test/hobosupport/metaid.rdoctest
|
55
|
+
- test/hobosupport/methodphitamine.rdoctest
|
56
|
+
- test/hobosupport/module.rdoctest
|
57
|
+
test_files: []
|
58
|
+
|
59
|
+
rdoc_options:
|
60
|
+
- --main
|
61
|
+
- README.txt
|
62
|
+
extra_rdoc_files:
|
63
|
+
- History.txt
|
64
|
+
- Manifest.txt
|
65
|
+
- README.txt
|
66
|
+
executables: []
|
67
|
+
|
68
|
+
extensions: []
|
69
|
+
|
70
|
+
requirements: []
|
71
|
+
|
72
|
+
dependencies:
|
73
|
+
- !ruby/object:Gem::Dependency
|
74
|
+
name: hoe
|
75
|
+
version_requirement:
|
76
|
+
version_requirements: !ruby/object:Gem::Version::Requirement
|
77
|
+
requirements:
|
78
|
+
- - ">="
|
79
|
+
- !ruby/object:Gem::Version
|
80
|
+
version: 1.5.0
|
81
|
+
version:
|