Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/rkh/chainable
never use alias_method_chain, again
https://github.com/rkh/chainable
Last synced: 18 days ago
JSON representation
never use alias_method_chain, again
- Host: GitHub
- URL: https://github.com/rkh/chainable
- Owner: rkh
- License: ncsa
- Created: 2009-03-19T20:19:16.000Z (over 15 years ago)
- Default Branch: master
- Last Pushed: 2010-04-22T09:59:14.000Z (over 14 years ago)
- Last Synced: 2024-10-14T19:42:48.177Z (about 1 month ago)
- Language: Ruby
- Homepage:
- Size: 118 KB
- Stars: 19
- Watchers: 4
- Forks: 1
- Open Issues: 0
-
Metadata Files:
- Readme: README.rdoc
- License: LICENSE
Awesome Lists containing this project
README
A word of warning:
This is heavy ruby abuse. It even got the Evil of the Day Award™ from zenspider.
Only works with Ruby 1.8, make sure you have chainable >= 0.4.0.
== Thou shalt not use alias_method_chain!
- http://yehudakatz.com/2009/03/06/alias_method_chain-in-models
- http://yehudakatz.com/2009/01/18/other-ways-to-wrap-a-method
- http://www.codefluency.com/articles/2009/01/03/wrapping-a-method-in-ruby== What it does
=== Chaining Methods
Chainable is an alternative to alias_method_chain, that uses inheritance, rather
than aliasing. It does the following when "chaining" a method:- copy the original method to a new model
- include the model
- overwrite the methodThus you can use super and keep your method list clean, too!
It even supports a (rather dangerous) auto chaining mode, so you do not have
to explicitly chain a method, but chain a method whenever it would be
overwritten instead.Example:
class Foo
def foo
10
end# now chain to foo
chain_method :foo do
super + 3
end# or turn on auto chaining
auto_chain dodef bar
10
enddef bar
super + 1
enddef bar
super ** 2
endend
# or chain multiple methods at once
chain_method :foo, :bar do
super.to_s
endend
f = Foo.new
puts f.foo # => 13
puts f.bar # => 121Of course you can do this with any class (or module):
Array.class_eval do
chain_method :each
def each
return super if block_given? or RUBY_VERSION >= "1.8.7"
MyStuff::Enumerator.new self, :each
end
endNote that there is a speed advantage when using chain_method without a block
and doing a "def", since chain_method will use define_method if a block is
given, which produces slower methods.=== Merging Methods
But wait, there is more:
class Foo
def foo
10
end
merge_method :foo do
super * 3
end
endputs Ruby2Ruby.translate Foo, :foo
The output:
def foo
(10) * 3
endBefore you yell at me about how insane I am, read on!
The library will only allow merging, if it thinks, it is possible:
class Foo
def foo
x = 10
end
merge_method :foo do
x = 20
super
puts x
end
endWill give you:
ArgumentError: cannot merge foo.
Same goes for this one:
class Foo
def foo x
puts x
end
merge_method :foo do
super
end
end# => ArgumentError: cannot merge foo.
But where is the fun in that one? You probably don't want your ruby script
throwing such errors at you.Enter "try_merge":
SomeEvilClassWithoutHooks.class_eval do
chain_method *instance_methods(false), :try_merge => true do
old_value = self.value.dup
super.tap { observer.notify if old_value != value }
end
attr_accessor :observer
endsome_evil_instance.observer = MyObserver.new
== When to use it?
As with alias_method_chain, you should use this as seldom as possible. Prefer
clean inheritance over evil hacks. There actually is only one case one may use
chainable (or alias_method_chain, for that matter): If there is a class you
need to modify that is not part of your own code and the instances you deal with
may already exists when you modify the class. In case you can modify the class
before instance creation, just create another class inheriting from the first
one and overwrite new to return instances of the latter.== Benchmark
chain_method tends do produce slightly faster methods than alias_method_chain:
$ rake benchmark
user system total real
no wrappers 0.000000 0.000000 0.000000 ( 0.004887)
merge_method 0.000000 0.000000 0.000000 ( 0.004830)
chain_method (def) 1.040000 0.350000 1.390000 ( 1.392329)
chain_method (define_method) 1.150000 0.240000 1.390000 ( 1.396007)
alias_method_chain (def) 1.210000 0.260000 1.470000 ( 1.472633)
alias_method_chain (define_method) 3.470000 0.590000 4.060000 ( 4.096245)== Installation
gem install chainable
== Running test
The specs should work with rspec, mspec and bacon.