gorillib 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (67) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/LICENSE.textile +81 -0
  4. data/README.textile +153 -0
  5. data/Rakefile +26 -0
  6. data/VERSION +1 -0
  7. data/fiddle/hubahuba.rb +62 -0
  8. data/gorillib.gemspec +142 -0
  9. data/lib/gorillib/array/compact_blank.rb +17 -0
  10. data/lib/gorillib/array/extract_options.rb +29 -0
  11. data/lib/gorillib/base.rb +7 -0
  12. data/lib/gorillib/datetime/flat.rb +29 -0
  13. data/lib/gorillib/datetime/parse.rb +21 -0
  14. data/lib/gorillib/enumerable/sum.rb +38 -0
  15. data/lib/gorillib/hash/compact.rb +31 -0
  16. data/lib/gorillib/hash/deep_merge.rb +16 -0
  17. data/lib/gorillib/hash/keys.rb +42 -0
  18. data/lib/gorillib/hash/reverse_merge.rb +26 -0
  19. data/lib/gorillib/hash/slice.rb +53 -0
  20. data/lib/gorillib/hash/zip.rb +10 -0
  21. data/lib/gorillib/logger/log.rb +14 -0
  22. data/lib/gorillib/metaprogramming/aliasing.rb +43 -0
  23. data/lib/gorillib/metaprogramming/cattr_accessor.rb +79 -0
  24. data/lib/gorillib/metaprogramming/class_attribute.rb +90 -0
  25. data/lib/gorillib/metaprogramming/delegation.rb +146 -0
  26. data/lib/gorillib/metaprogramming/mattr_accessor.rb +61 -0
  27. data/lib/gorillib/metaprogramming/remove_method.rb +11 -0
  28. data/lib/gorillib/metaprogramming/singleton_class.rb +8 -0
  29. data/lib/gorillib/object/blank.rb +89 -0
  30. data/lib/gorillib/some.rb +12 -0
  31. data/lib/gorillib/string/constantize.rb +21 -0
  32. data/lib/gorillib/string/human.rb +52 -0
  33. data/lib/gorillib/string/inflections.rb +78 -0
  34. data/lib/gorillib/string/truncate.rb +33 -0
  35. data/lib/gorillib.rb +1 -0
  36. data/spec/blank_spec.rb +86 -0
  37. data/spec/gorillib_spec.rb +7 -0
  38. data/spec/rcov.opts +6 -0
  39. data/spec/spec.opts +4 -0
  40. data/spec/spec_helper.rb +12 -0
  41. data/spec/spec_tasks.rake +15 -0
  42. data/test/abstract_unit.rb +25 -0
  43. data/test/array/compact_blank_test.rb +33 -0
  44. data/test/array/extract_options_test.rb +39 -0
  45. data/test/datetime/flat_test.rb +0 -0
  46. data/test/datetime/parse_test.rb +0 -0
  47. data/test/enumerable/sum_test.rb +50 -0
  48. data/test/hash/compact_test.rb +38 -0
  49. data/test/hash/deep_merge_test.rb +30 -0
  50. data/test/hash/keys_test.rb +110 -0
  51. data/test/hash/reverse_merge_test.rb +20 -0
  52. data/test/hash/slice_test.rb +47 -0
  53. data/test/hash/zip_test.rb +0 -0
  54. data/test/logger/log_test.rb +0 -0
  55. data/test/metaprogramming/aliasing_test.rb +188 -0
  56. data/test/metaprogramming/cattr_accessor_test.rb +38 -0
  57. data/test/metaprogramming/class_attribute_test.rb +73 -0
  58. data/test/metaprogramming/delegation_test.rb +166 -0
  59. data/test/metaprogramming/mattr_accessor_test.rb +40 -0
  60. data/test/metaprogramming/singleton_class_test.rb +9 -0
  61. data/test/object/blank_test.rb +22 -0
  62. data/test/string/constantize_test.rb +30 -0
  63. data/test/string/human_test.rb +65 -0
  64. data/test/string/inflections_test.rb +57 -0
  65. data/test/string/inflector_test_cases.rb +50 -0
  66. data/test/string/truncate_test.rb +37 -0
  67. metadata +199 -0
@@ -0,0 +1,16 @@
1
+ class Hash
2
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
3
+ def deep_merge(other_hash)
4
+ dup.deep_merge!(other_hash)
5
+ end unless method_defined?(:deep_merge)
6
+
7
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
8
+ # Modifies the receiver in place.
9
+ def deep_merge!(other_hash)
10
+ other_hash.each_pair do |k,v|
11
+ tv = self[k]
12
+ self[k] = tv.is_a?(Hash) && v.is_a?(Hash) ? tv.deep_merge(v) : v
13
+ end
14
+ self
15
+ end unless method_defined?(:deep_merge!)
16
+ end
@@ -0,0 +1,42 @@
1
+ class Hash
2
+ # Return a new hash with all keys converted to strings.
3
+ def stringify_keys
4
+ dup.stringify_keys!
5
+ end unless method_defined?(:stringify_keys)
6
+
7
+ # Destructively convert all keys to strings.
8
+ def stringify_keys!
9
+ keys.each do |key|
10
+ self[key.to_s] = delete(key)
11
+ end
12
+ self
13
+ end unless method_defined?(:stringify_keys!)
14
+
15
+ # Return a new hash with all keys converted to symbols, as long as
16
+ # they respond to +to_sym+.
17
+ def symbolize_keys
18
+ dup.symbolize_keys!
19
+ end unless method_defined?(:symbolize_keys)
20
+
21
+ # Destructively convert all keys to symbols, as long as they respond
22
+ # to +to_sym+.
23
+ def symbolize_keys!
24
+ keys.each do |key|
25
+ self[(key.to_sym rescue key) || key] = delete(key)
26
+ end
27
+ self
28
+ end unless method_defined?(:symbolize_keys!)
29
+
30
+ # Validate all keys in a hash match *valid keys, raising ArgumentError on a mismatch.
31
+ # Note that keys are NOT treated indifferently, meaning if you use strings for keys but assert symbols
32
+ # as keys, this will fail.
33
+ #
34
+ # ==== Examples
35
+ # { :name => "Rob", :years => "28" }.assert_valid_keys(:name, :age) # => raises "ArgumentError: Unknown key(s): years"
36
+ # { :name => "Rob", :age => "28" }.assert_valid_keys("name", "age") # => raises "ArgumentError: Unknown key(s): name, age"
37
+ # { :name => "Rob", :age => "28" }.assert_valid_keys(:name, :age) # => passes, raises nothing
38
+ def assert_valid_keys(*valid_keys)
39
+ unknown_keys = keys - [valid_keys].flatten
40
+ raise(ArgumentError, "Unknown key(s): #{unknown_keys.join(", ")}") unless unknown_keys.empty?
41
+ end unless method_defined?(:assert_valid_keys)
42
+ end
@@ -0,0 +1,26 @@
1
+ class Hash
2
+ # Allows for reverse merging two hashes where the keys in the calling hash take precedence over those
3
+ # in the <tt>other_hash</tt>. This is particularly useful for initializing an option hash with default values:
4
+ #
5
+ # def setup(options = {})
6
+ # options.reverse_merge! :size => 25, :velocity => 10
7
+ # end
8
+ #
9
+ # Using <tt>merge</tt>, the above example would look as follows:
10
+ #
11
+ # def setup(options = {})
12
+ # { :size => 25, :velocity => 10 }.merge(options)
13
+ # end
14
+ #
15
+ # The default <tt>:size</tt> and <tt>:velocity</tt> are only set if the +options+ hash passed in doesn't already
16
+ # have the respective key.
17
+ def reverse_merge(other_hash)
18
+ other_hash.merge(self)
19
+ end unless method_defined?(:reverse_merge)
20
+
21
+ # Performs the opposite of <tt>merge</tt>, with the keys and values from the first hash taking precedence over the second.
22
+ # Modifies the receiver in place.
23
+ def reverse_merge!(other_hash)
24
+ merge!( other_hash ){|k,o,n| o }
25
+ end unless method_defined?(:reverse_merge!)
26
+ end
@@ -0,0 +1,53 @@
1
+ class Hash
2
+ # Slice a hash to include only the given keys. This is useful for
3
+ # limiting an options hash to valid keys before passing to a method:
4
+ #
5
+ # def search(criteria = {})
6
+ # assert_valid_keys(:mass, :velocity, :time)
7
+ # end
8
+ #
9
+ # search(options.slice(:mass, :velocity, :time))
10
+ #
11
+ # If you have an array of keys you want to limit to, you should splat them:
12
+ #
13
+ # valid_keys = [:mass, :velocity, :time]
14
+ # search(options.slice(*valid_keys))
15
+ def slice(*keys)
16
+ keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
17
+ hash = self.class.new
18
+ keys.each { |k| hash[k] = self[k] if has_key?(k) }
19
+ hash
20
+ end unless method_defined?(:slice)
21
+
22
+ # Replaces the hash with only the given keys.
23
+ # Returns a hash containing the removed key/value pairs
24
+ # @example
25
+ # hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
26
+ # hsh.slice!(:a, :b)
27
+ # # => {:c => 3, :d =>4}
28
+ # hsh
29
+ # # => {:a => 1, :b => 2}
30
+ def slice!(*keys)
31
+ keys = keys.map! { |key| convert_key(key) } if respond_to?(:convert_key)
32
+ omit = slice(*self.keys - keys)
33
+ hash = slice(*keys)
34
+ replace(hash)
35
+ omit
36
+ end unless method_defined?(:slice!)
37
+
38
+ # Removes the given keys from the hash
39
+ # Returns a hash containing the removed key/value pairs
40
+ #
41
+ # @example
42
+ # hsh = {:a => 1, :b => 2, :c => 3, :d => 4}
43
+ # hsh.extract!(:a, :b)
44
+ # # => {:a => 1, :b => 2}
45
+ # hsh
46
+ # # => {:c => 3, :d =>4}
47
+ def extract!(*keys)
48
+ result = {}
49
+ keys.each {|key| result[key] = delete(key) }
50
+ result
51
+ end unless method_defined?(:extract!)
52
+ end
53
+
@@ -0,0 +1,10 @@
1
+ class Hash
2
+ #
3
+ # Create a hash from an array of keys and corresponding values.
4
+ #
5
+ def self.zip(keys, values, default=nil, &block)
6
+ hash = block_given? ? Hash.new(&block) : Hash.new(default)
7
+ keys.zip(values){|key,val| hash[key]=val }
8
+ hash
9
+ end unless respond_to?(:zip)
10
+ end
@@ -0,0 +1,14 @@
1
+ require 'logger'
2
+
3
+ #
4
+ # A convenient logger.
5
+ #
6
+ # define Log yourself to prevent its creation
7
+ #
8
+ ::Log = Logger.new(STDERR) unless defined?(::Log)
9
+
10
+ def Log.dump *args
11
+ debug args.map(&:inspect).join("\t")
12
+ end unless Log.respond_to?(:dump)
13
+
14
+
@@ -0,0 +1,43 @@
1
+ class Module
2
+ # Encapsulates the common pattern of:
3
+ #
4
+ # alias_method :foo_without_feature, :foo
5
+ # alias_method :foo, :foo_with_feature
6
+ #
7
+ # With this, you simply do:
8
+ #
9
+ # alias_method_chain :foo, :feature
10
+ #
11
+ # And both aliases are set up for you.
12
+ #
13
+ # Query and bang methods (foo?, foo!) keep the same punctuation:
14
+ #
15
+ # alias_method_chain :foo?, :feature
16
+ #
17
+ # is equivalent to
18
+ #
19
+ # alias_method :foo_without_feature?, :foo?
20
+ # alias_method :foo?, :foo_with_feature?
21
+ #
22
+ # so you can safely chain foo, foo?, and foo! with the same feature.
23
+ def alias_method_chain(target, feature)
24
+ # Strip out punctuation on predicates or bang methods since
25
+ # e.g. target?_without_feature is not a valid method name.
26
+ aliased_target, punctuation = target.to_s.sub(/([?!=])$/, ''), $1
27
+ yield(aliased_target, punctuation) if block_given?
28
+
29
+ with_method, without_method = "#{aliased_target}_with_#{feature}#{punctuation}", "#{aliased_target}_without_#{feature}#{punctuation}"
30
+
31
+ alias_method without_method, target
32
+ alias_method target, with_method
33
+
34
+ case
35
+ when public_method_defined?(without_method)
36
+ public target
37
+ when protected_method_defined?(without_method)
38
+ protected target
39
+ when private_method_defined?(without_method)
40
+ private target
41
+ end
42
+ end unless method_defined?(:alias_method_chain)
43
+ end
@@ -0,0 +1,79 @@
1
+ require 'gorillib/array/extract_options'
2
+
3
+ # Extends the class object with class and instance accessors for class attributes,
4
+ # just like the native attr* accessors for instance attributes.
5
+ #
6
+ # Note that unlike +class_attribute+, if a subclass changes the value then that would
7
+ # also change the value for parent class. Similarly if parent class changes the value
8
+ # then that would change the value of subclasses too.
9
+ #
10
+ # class Person
11
+ # cattr_accessor :hair_colors
12
+ # end
13
+ #
14
+ # Person.hair_colors = [:brown, :black, :blonde, :red]
15
+ # Person.hair_colors # => [:brown, :black, :blonde, :red]
16
+ # Person.new.hair_colors # => [:brown, :black, :blonde, :red]
17
+ #
18
+ # To opt out of the instance writer method, pass :instance_writer => false.
19
+ # To opt out of the instance reader method, pass :instance_reader => false.
20
+ #
21
+ # class Person
22
+ # cattr_accessor :hair_colors, :instance_writer => false, :instance_reader => false
23
+ # end
24
+ #
25
+ # Person.new.hair_colors = [:brown] # => NoMethodError
26
+ # Person.new.hair_colors # => NoMethodError
27
+ class Class
28
+ def cattr_reader(*syms)
29
+ options = syms.extract_options!
30
+ syms.each do |sym|
31
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
32
+ unless defined? @@#{sym}
33
+ @@#{sym} = nil
34
+ end
35
+
36
+ def self.#{sym}
37
+ @@#{sym}
38
+ end
39
+ EOS
40
+
41
+ unless options[:instance_reader] == false
42
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
43
+ def #{sym}
44
+ @@#{sym}
45
+ end
46
+ EOS
47
+ end
48
+ end
49
+ end unless method_defined?(:cattr_reader)
50
+
51
+ def cattr_writer(*syms)
52
+ options = syms.extract_options!
53
+ syms.each do |sym|
54
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
55
+ unless defined? @@#{sym}
56
+ @@#{sym} = nil
57
+ end
58
+
59
+ def self.#{sym}=(obj)
60
+ @@#{sym} = obj
61
+ end
62
+ EOS
63
+
64
+ unless options[:instance_writer] == false
65
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
66
+ def #{sym}=(obj)
67
+ @@#{sym} = obj
68
+ end
69
+ EOS
70
+ end
71
+ self.send("#{sym}=", yield) if block_given?
72
+ end
73
+ end unless method_defined?(:cattr_writer)
74
+
75
+ def cattr_accessor(*syms, &blk)
76
+ cattr_reader(*syms)
77
+ cattr_writer(*syms, &blk)
78
+ end unless method_defined?(:cattr_accessor)
79
+ end
@@ -0,0 +1,90 @@
1
+ require 'gorillib/metaprogramming/singleton_class'
2
+ require 'gorillib/metaprogramming/remove_method'
3
+
4
+ class Class
5
+ # Declare a class-level attribute whose value is inheritable by subclasses.
6
+ # Subclasses can change their own value and it will not impact parent class.
7
+ #
8
+ # class Base
9
+ # class_attribute :setting
10
+ # end
11
+ #
12
+ # class Subclass < Base
13
+ # end
14
+ #
15
+ # Base.setting = true
16
+ # Subclass.setting # => true
17
+ # Subclass.setting = false
18
+ # Subclass.setting # => false
19
+ # Base.setting # => true
20
+ #
21
+ # In the above case as long as Subclass does not assign a value to setting
22
+ # by performing <tt>Subclass.setting = _something_ </tt>, <tt>Subclass.setting</tt>
23
+ # would read value assigned to parent class. Once Subclass assigns a value then
24
+ # the value assigned by Subclass would be returned.
25
+ #
26
+ # This matches normal Ruby method inheritance: think of writing an attribute
27
+ # on a subclass as overriding the reader method. However, you need to be aware
28
+ # when using +class_attribute+ with mutable structures as +Array+ or +Hash+.
29
+ # In such cases, you don't want to do changes in places but use setters:
30
+ #
31
+ # Base.setting = []
32
+ # Base.setting # => []
33
+ # Subclass.setting # => []
34
+ #
35
+ # # Appending in child changes both parent and child because it is the same object:
36
+ # Subclass.setting << :foo
37
+ # Base.setting # => [:foo]
38
+ # Subclass.setting # => [:foo]
39
+ #
40
+ # # Use setters to not propagate changes:
41
+ # Base.setting = []
42
+ # Subclass.setting += [:foo]
43
+ # Base.setting # => []
44
+ # Subclass.setting # => [:foo]
45
+ #
46
+ # For convenience, a query method is defined as well:
47
+ #
48
+ # Subclass.setting? # => false
49
+ #
50
+ # Instances may overwrite the class value in the same way:
51
+ #
52
+ # Base.setting = true
53
+ # object = Base.new
54
+ # object.setting # => true
55
+ # object.setting = false
56
+ # object.setting # => false
57
+ # Base.setting # => true
58
+ #
59
+ # To opt out of the instance writer method, pass :instance_writer => false.
60
+ #
61
+ # object.setting = false # => NoMethodError
62
+ def class_attribute(*attrs)
63
+ instance_writer = !attrs.last.is_a?(Hash) || attrs.pop[:instance_writer]
64
+
65
+ attrs.each do |name|
66
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
67
+ def self.#{name}() nil end
68
+ def self.#{name}?() !!#{name} end
69
+
70
+ def self.#{name}=(val)
71
+ singleton_class.class_eval do
72
+ remove_possible_method(:#{name})
73
+ define_method(:#{name}) { val }
74
+ end
75
+ val
76
+ end
77
+
78
+ def #{name}
79
+ defined?(@#{name}) ? @#{name} : singleton_class.#{name}
80
+ end
81
+
82
+ def #{name}?
83
+ !!#{name}
84
+ end
85
+ RUBY
86
+
87
+ attr_writer name if instance_writer
88
+ end
89
+ end unless method_defined?(:class_attribute)
90
+ end
@@ -0,0 +1,146 @@
1
+ require "gorillib/metaprogramming/remove_method"
2
+
3
+ class Module
4
+ # Provides a delegate class method to easily expose contained objects' methods
5
+ # as your own. Pass one or more methods (specified as symbols or strings)
6
+ # and the name of the target object via the <tt>:to</tt> option (also a symbol
7
+ # or string). At least one method and the <tt>:to</tt> option are required.
8
+ #
9
+ # Delegation is particularly useful with Active Record associations:
10
+ #
11
+ # class Greeter < ActiveRecord::Base
12
+ # def hello
13
+ # "hello"
14
+ # end
15
+ #
16
+ # def goodbye
17
+ # "goodbye"
18
+ # end
19
+ # end
20
+ #
21
+ # class Foo < ActiveRecord::Base
22
+ # belongs_to :greeter
23
+ # delegate :hello, :to => :greeter
24
+ # end
25
+ #
26
+ # Foo.new.hello # => "hello"
27
+ # Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
28
+ #
29
+ # Multiple delegates to the same target are allowed:
30
+ #
31
+ # class Foo < ActiveRecord::Base
32
+ # belongs_to :greeter
33
+ # delegate :hello, :goodbye, :to => :greeter
34
+ # end
35
+ #
36
+ # Foo.new.goodbye # => "goodbye"
37
+ #
38
+ # Methods can be delegated to instance variables, class variables, or constants
39
+ # by providing them as a symbols:
40
+ #
41
+ # class Foo
42
+ # CONSTANT_ARRAY = [0,1,2,3]
43
+ # @@class_array = [4,5,6,7]
44
+ #
45
+ # def initialize
46
+ # @instance_array = [8,9,10,11]
47
+ # end
48
+ # delegate :sum, :to => :CONSTANT_ARRAY
49
+ # delegate :min, :to => :@@class_array
50
+ # delegate :max, :to => :@instance_array
51
+ # end
52
+ #
53
+ # Foo.new.sum # => 6
54
+ # Foo.new.min # => 4
55
+ # Foo.new.max # => 11
56
+ #
57
+ # Delegates can optionally be prefixed using the <tt>:prefix</tt> option. If the value
58
+ # is <tt>true</tt>, the delegate methods are prefixed with the name of the object being
59
+ # delegated to.
60
+ #
61
+ # Person = Struct.new(:name, :address)
62
+ #
63
+ # class Invoice < Struct.new(:client)
64
+ # delegate :name, :address, :to => :client, :prefix => true
65
+ # end
66
+ #
67
+ # john_doe = Person.new("John Doe", "Vimmersvej 13")
68
+ # invoice = Invoice.new(john_doe)
69
+ # invoice.client_name # => "John Doe"
70
+ # invoice.client_address # => "Vimmersvej 13"
71
+ #
72
+ # It is also possible to supply a custom prefix.
73
+ #
74
+ # class Invoice < Struct.new(:client)
75
+ # delegate :name, :address, :to => :client, :prefix => :customer
76
+ # end
77
+ #
78
+ # invoice = Invoice.new(john_doe)
79
+ # invoice.customer_name # => "John Doe"
80
+ # invoice.customer_address # => "Vimmersvej 13"
81
+ #
82
+ # If the delegate object is +nil+ an exception is raised, and that happens
83
+ # no matter whether +nil+ responds to the delegated method. You can get a
84
+ # +nil+ instead with the +:allow_nil+ option.
85
+ #
86
+ # class Foo
87
+ # attr_accessor :bar
88
+ # def initialize(bar = nil)
89
+ # @bar = bar
90
+ # end
91
+ # delegate :zoo, :to => :bar
92
+ # end
93
+ #
94
+ # Foo.new.zoo # raises NoMethodError exception (you called nil.zoo)
95
+ #
96
+ # class Foo
97
+ # attr_accessor :bar
98
+ # def initialize(bar = nil)
99
+ # @bar = bar
100
+ # end
101
+ # delegate :zoo, :to => :bar, :allow_nil => true
102
+ # end
103
+ #
104
+ # Foo.new.zoo # returns nil
105
+ #
106
+ def delegate(*methods)
107
+ options = methods.pop
108
+ unless options.is_a?(Hash) && to = options[:to]
109
+ raise ArgumentError, "Delegation needs a target. Supply an options hash with a :to key as the last argument (e.g. delegate :hello, :to => :greeter)."
110
+ end
111
+
112
+ if options[:prefix] == true && options[:to].to_s =~ /^[^a-z_]/
113
+ raise ArgumentError, "Can only automatically set the delegation prefix when delegating to a method."
114
+ end
115
+
116
+ prefix = options[:prefix] && "#{options[:prefix] == true ? to : options[:prefix]}_" || ''
117
+
118
+ file, line = caller.first.split(':', 2)
119
+ line = line.to_i
120
+
121
+ methods.each do |method|
122
+ on_nil =
123
+ if options[:allow_nil]
124
+ 'return'
125
+ else
126
+ %(raise "#{self}##{prefix}#{method} delegated to #{to}.#{method}, but #{to} is nil: \#{self.inspect}")
127
+ end
128
+
129
+ module_eval(<<-EOS, file, line - 5)
130
+ if instance_methods(false).map(&:to_s).include?("#{prefix}#{method}")
131
+ remove_possible_method("#{prefix}#{method}")
132
+ end
133
+
134
+ def #{prefix}#{method}(*args, &block) # def customer_name(*args, &block)
135
+ #{to}.__send__(#{method.inspect}, *args, &block) # client.__send__(:name, *args, &block)
136
+ rescue NoMethodError # rescue NoMethodError
137
+ if #{to}.nil? # if client.nil?
138
+ #{on_nil} # return # depends on :allow_nil
139
+ else # else
140
+ raise # raise
141
+ end # end
142
+ end # end
143
+ EOS
144
+ end
145
+ end unless method_defined?(:delegate)
146
+ end
@@ -0,0 +1,61 @@
1
+ require 'gorillib/array/extract_options'
2
+
3
+ class Module
4
+ def mattr_reader(*syms)
5
+ options = syms.extract_options!
6
+ syms.each do |sym|
7
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
8
+ @@#{sym} = nil unless defined? @@#{sym}
9
+
10
+ def self.#{sym}
11
+ @@#{sym}
12
+ end
13
+ EOS
14
+
15
+ unless options[:instance_reader] == false
16
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
17
+ def #{sym}
18
+ @@#{sym}
19
+ end
20
+ EOS
21
+ end
22
+ end
23
+ end unless method_defined?(:mattr_reader)
24
+
25
+ def mattr_writer(*syms)
26
+ options = syms.extract_options!
27
+ syms.each do |sym|
28
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
29
+ def self.#{sym}=(obj)
30
+ @@#{sym} = obj
31
+ end
32
+ EOS
33
+
34
+ unless options[:instance_writer] == false
35
+ class_eval(<<-EOS, __FILE__, __LINE__ + 1)
36
+ def #{sym}=(obj)
37
+ @@#{sym} = obj
38
+ end
39
+ EOS
40
+ end
41
+ end
42
+ end unless method_defined?(:mattr_writer)
43
+
44
+ # Extends the module object with module and instance accessors for class attributes,
45
+ # just like the native attr* accessors for instance attributes.
46
+ #
47
+ # module AppConfiguration
48
+ # mattr_accessor :google_api_key
49
+ # self.google_api_key = "123456789"
50
+ #
51
+ # mattr_accessor :paypal_url
52
+ # self.paypal_url = "www.sandbox.paypal.com"
53
+ # end
54
+ #
55
+ # AppConfiguration.google_api_key = "overriding the api key!"
56
+ def mattr_accessor(*syms)
57
+ mattr_reader(*syms)
58
+ mattr_writer(*syms)
59
+ end unless method_defined?(:mattr_accessor)
60
+
61
+ end
@@ -0,0 +1,11 @@
1
+ class Module
2
+ def remove_possible_method(method)
3
+ remove_method(method)
4
+ rescue NameError
5
+ end unless method_defined?(:remove_possible_method)
6
+
7
+ def redefine_method(method, &block)
8
+ remove_possible_method(method)
9
+ define_method(method, &block)
10
+ end unless method_defined?(:redefine_method)
11
+ end
@@ -0,0 +1,8 @@
1
+ module Kernel
2
+ # Returns the object's singleton class.
3
+ def singleton_class
4
+ class << self
5
+ self
6
+ end
7
+ end unless respond_to?(:singleton_class) # exists in 1.9.2
8
+ end