Home > Uncategorized > Dynamically scoped variables in Ruby

Dynamically scoped variables in Ruby

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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
# 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 ;)

Categories: Uncategorized Tags:
  1. No comments yet.
  1. No trackbacks yet.