Created
January 28, 2009 10:23
-
-
Save vjt/53886 to your computer and use it in GitHub Desktop.
A simple class decorator.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
A simple class decorator. Useful when implementing some sorts of the evil twin pattern. | |
It works by creating a blank class slate (with Class.new), removing on it EVERY | |
method, except `__id__`, `__send__` and `initialize`. It then defines the `class` | |
method to return the original class, and implements both an `initialize` and a | |
`method_missing`. | |
The `initialize` sends the `new` method to the original class, therefore creating | |
a new instance of the original class and saves it in the @_object instance variable. | |
The `method_missing` in turn forwards any unimplemented method on the decorator to | |
the instance of the original class saved in the @_object instance variable. | |
History: | |
* Originally posted on http://pastie.org/39504 (11 February 2007) | |
* evolved into http://pastie.org/pastes/39197 (09 February 2007), | |
* evolved again http://pastie.org/pastes/36010 (27 January 2007). | |
* Originally used apeiros' blank slate: http://pastie.caboo.se/26558 | |
* Now on gist.github, with finally a comprehensive description and documentation (28 January 2009). | |
In order to decorate an existing class with new methods you should pass them as blocks | |
to the #decorate method, defined in Class. | |
Examples: | |
class Antani | |
def antani | |
'living in Antani..' | |
end | |
end | |
with_cheezeburger = Proc.new do | |
def cheezeburger! | |
"CAN I HAZ A CHEEZEBURGER?!" | |
end | |
end | |
B = A.decorate &with_cheezeburger # => B | |
b = B.new # => #<Antani:0xcafebabe> | |
# ^- note the invisibility of the decorated class, | |
# that behaves and looks like the master one. | |
b.antani # => "living in Antani.." | |
b.cheezeburger! # => "CAN I HAZ A CHEEZEBURGER?!" | |
Another example, conceptually identical: | |
with_address_and_email = Proc.new do | |
attr_accessor :address, :email | |
end | |
class C | |
def o_rly? | |
"YA, RLY!" | |
end | |
end | |
D = C.decorate &with_address_and_email # => D | |
>> d = D.new # => #<C:0xb7a9fa58> | |
>> d.o_rly? # => "YA, RLY!" | |
>> d.class # => C | |
>> d.address = "Antani Street, 23" # => "Antani Street, 23" | |
>> d.address # => "Antani Street, 23" | |
Enjoy! | |
- [email protected] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
# Simple class decorator. See the README for more information | |
# - [email protected] | |
module Decorator | |
def self.decorate klass, &block | |
slate = Class.new | |
# Inspired by apeiros' blank slate | |
(slate.instance_methods+slate.private_instance_methods+slate.protected_instance_methods).each { |m| | |
slate.send :undef_method, m unless %w(__id__ __send__ initialize).include? m | |
} | |
slate.class_eval do | |
define_method(:class) { klass } | |
def initialize *args | |
@_object = self.class.new *args | |
end | |
def method_missing meth, *args, &block | |
@_object.send meth, *args, &block | |
end | |
end | |
slate.class_eval &block | |
slate | |
end | |
end | |
class Class | |
def decorate &block | |
Decorator.decorate self, &block | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment