This post demonstrates how to apply a set of closures to various components (i.e. Ruby on Rails controllers) in a very flexible, indirect, and dynamic fashion. The final result involves using modules to implement a template pattern and invoking closures bound to new contexts.

Abusing Kernel#caller (remembering Java Reflection)

I recently had an issue where I needed to know the caller of a method. The goal was to apply one or more closures cached in a singleton configuration object to a controller or other component. I was originally on the right track, but I got sidetracked by a google search. I was reminded of Kernel#caller. The mess that ensued looked like this:

1 require 'active_support/inflector' 2 3 module Arbitrary2 4 class MyController 5 6 def some_action 7 NeedsToKnowCaller . apply_config 8 end 9 10 end 11 end 12 13 module NeedsToKnowCaller 14 15 SIG = /controllers\/([^\.]*).*`([^']*)'/ 16 17 class << self 18 19 # overriding Kernel#caller only for sake of repeatability on blog 20 # during normal use this override would not exist 21 def caller 22 [ "/arbitrary/project/app/controllers/arbitrary2/my_controller.rb:20:in `some_action'" ] 23 end 24 25 # extract signature for use elsewhere... 26 # dont want to explicitly declare caller as a param but could... 27 def apply_config 28 md = caller [ 0 ]. match ( SIG ) 29 sig = " #{ md [ 1 ]. classify } . #{ md [ 2 ] } " 30 # could do something with eval here... timeout... 31 puts sig 32 end 33 34 end 35 end 36 37 Arbitrary2 :: MyController . new . some_action 38 # printed value is "Arbitrary2::MyController.some_action"

What happens here is that some_action is called in an instance of MyController. This calls apply_config in the NeedsToKnowCaller module which is able to look up the class that called it. This implementation is left incomplete. It is not at all a dead end, but it is an expensive and unnecessary bad road to continue down.

Ruby Modules and the Template Design Pattern

As soon as I was finished tracking down the caller I remembered what I was really trying to do in the first place. I did not just need the name of the caller. I needed the context of the caller as well so that I could invoke a closure in the context of the caller. The solution above is terrible. I blame my history with Java reflection for this brief lapse of judgement. For anyone looking up Kernel#caller or a regex to be used with it, please stop right here. Ruby has better options in the form of modules.

Modules can be used for much more than just bags of functions or namespaces. The template pattern typically looks a little different than this, but that is essentially what I ended up reinventing. Instead of using inheritance, I ended up using mixins in a slightly different way than I usually do.

1 module NeedsToKnowCaller 2 3 class << self 4 5 # actual impl much more complex... 6 def config ( key ) 7 # hard coded for clarity 8 -> { root_path } 9 end 10 11 def apply_config_to _caller , key 12 _caller . instance_exec & config ( key ) 13 end 14 15 end 16 17 module ControllerMethods 18 19 # dont want to explicitly declare caller as a param but could... 20 def apply_config ( key ) 21 NeedsToKnowCaller . apply_config_to self , key 22 end 23 24 end 25 26 end 27 28 module Arbitrary2 29 class MyController 30 include NeedsToKnowCaller :: ControllerMethods 31 32 def some_action 33 apply_config :varies 34 end 35 36 def root_path 37 "would normally come from rails routing" 38 end 39 end 40 end 41 42 Arbitrary2 :: MyController . new . some_action 43 # => "would normally come from rails routing"

In this example, any controller that includes NeedsToKnowCaller::ControllerMethods will be able to call NeedsToKnowCaller.apply_config_to(self) with just apply_config. Apply_config_to will then call config which returns a lambda which is invoked in the context of the instance that was passed into apply_config_to. So an instance of MyController calls apply_config which indirectly calls root_path. The config method in my actual implementation is much more involved.

This example might suffer from oversimplification. In the context of a large project, small details like cutting a derivable parameter out of a method can be really helpful. In a small project this wouldn't make much sense.

What I do find interesting is how well Ruby facilitates some older design patterns. I learned the template pattern with Java. In my experience it always looks the same in Java. A subclass adds something around a superclass method. Here a collection of semi-related classes call another singleton class that is not inherited at all. The idea is the same. The implementation is totally different. Furthermore, the ease with which closures can be reapplied to other contexts is extremely powerful.