Created
June 17, 2009 19:13
-
-
Save tenderlove/131431 to your computer and use it in GitHub Desktop.
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
require 'visitor' | |
## | |
# This Visitor knows how to turn any object in to an Array | |
class AsArray < Visitor | |
visitor_for Array do |array| | |
array | |
end | |
visitor_for Object do |o| | |
[o] | |
end | |
end |
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
require 'visitor' | |
### | |
# This Visitor knows how to do stuff with arguments based on class type. | |
class AsFieldsForFormObject < Visitor | |
def initialize args | |
@args = args | |
end | |
## | |
# If the recipient is a String or Symbol, we just want to return the first | |
# from @arg | |
visitor_for String, Symbol do |thing| | |
@args.first | |
end | |
### | |
# If it is an object, we want to get it as a form object | |
visitor_for Object do |thing| | |
form_for(thing).as_form_object | |
end | |
end |
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
require 'visitor' | |
## | |
# This class knows how to turn any object in to a form object. | |
class AsFormObject < Visitor | |
## | |
# If it is a String, Symbol, Fixnum, or Object just return the thing | |
visitor_for String, Symbol, Fixnum do |thing| | |
thing | |
end | |
## | |
# If it's an Array, we just want the last bit of the array, and we'll convert | |
# it to a form object as well | |
visitor_for Array do |list| | |
list.last.accept self | |
end | |
end | |
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
require 'visitable' | |
require 'visitor' | |
require 'as_form_object' | |
require 'as_array' | |
require 'as_fields_for_form_object' | |
### | |
# This class sets up a proxy to +thing+. The purpose of this class is to let | |
# us convert an object to something else without knowing what visitor is | |
# required to do that conversion: | |
# | |
# p form_for([1, :two, [3]]).as_form_object | |
# | |
class ConversionProxy < Struct.new(:thing) | |
def as_form_object | |
thing.accept AsFormObject.new | |
end | |
def as_array | |
thing.accept AsArray.new | |
end | |
def as_fields_for_form_object args | |
thing.accept AsFieldsForFormObject.new(args) | |
end | |
end | |
def form_for thing | |
ConversionProxy.new(thing) | |
end | |
p form_for(:two).as_form_object | |
p form_for(:two).as_array | |
p form_for([:two]).as_array | |
p form_for(:two).as_fields_for_form_object [:foo] | |
p form_for(['hello world']).as_fields_for_form_object [:foo] | |
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
## | |
# The visitable module makes a class "visitable". It follows the double dispatch | |
# pattern and simply calls "visit" on the argument with itself. | |
module Visitable | |
def accept visitor | |
visitor.visit self | |
end | |
end | |
## | |
# For this example, we want to make *all* objects visitable, so we'll mix the | |
# Visitable module in to Object. | |
class Object | |
include Visitable | |
end |
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
## | |
# The Visitor class is our base class for all visitors. It implements the | |
# "visit" method which Object#accept will call. | |
class Visitor | |
### | |
# Dynamically create a visitor method for each class in +klasses+ | |
def self.visitor_for *klasses, &block | |
klasses.each do |klass| | |
define_method(:"visit_#{klass.name}", block) | |
end | |
end | |
## | |
# This method will examine the class and ancestors of +thing+. For each | |
# class in the "ancestors" list, it will check to see if the visitor knows | |
# how to handle that particular class. If it can't find a handler for the | |
# +thing+ it will raise an exception. | |
def visit thing | |
thing.class.ancestors.each do |ancestor| | |
method_name = :"visit_#{ancestor.name}" | |
next unless respond_to? method_name | |
return send method_name, thing | |
end | |
raise "Can't handle #{thing.class}" | |
end | |
end |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment