Recently I had a need to use routing definition of one RoR from the other one (don’t ask why it’s irrelevant to this post ;) )
As you may know Rails routing mappings are stored in global variable ActionController::Routing::Routes. So together with Craig McMillan I’ve hacked really evil piece of code ;) but it does the job!
So here it goes:
# evil Ruby dynamic variables
class DynamicValue
def initialize(val)
@default_value = val
end
def method_missing(name, *args, &proc)
if tv=Thread.current['DynamicValue']
tv.send( name, *args, &proc)
else
@default_value.send( name, *args, &proc)
end
end
def with_dynamic_value(value)
tmp = Thread.current['DynamicValue']
begin
Thread.current['DynamicValue'] = value
yield
ensure
Thread.current['DynamicValue'] = tmp
end
end
def to_s
"dynamic value"
end
def inspect
to_s
end
end
One can use it for example like this:
# let's deal with fresh empty RouteSet object ActionController::Routing::Routes.with_dynamic_value( ActionController::Routing::RouteSet.new ) do # let's load other RoR routes to it load File.join(Sonar::SONAR_ROOT, 'other_ror', 'config', 'routes.rb') # now we can use the other RoR routing definition to generate email tamplate used from the other RoR email = NewItemsMessage::create_new_message(person) end
Only thread invoking with_dynamic_value will experience the variable reassignment. For other threads it will be transparent. Once the thread exit the block, again it will see the value in the variable as it was before the trick.
In order to enable that evil you have to invoke once and only once (placing it at the end of config/environment.rb should be fine) the following line:
ActionController::Routing.const_set("Routes", DynamicValue.new(ActionController::Routing::Routes))
Or any other variable substitution. Doing it once is important because applying more than one proxy will discard the original value of the variable.
Enjoy more LISP’y Ruby ;)