Rails 2.3 and theme_support part 3: Layouts

In my ongoing efforts to bring my fork of theme_support in line with Rails 2.3 I’ve covered the core views and email, but when I left off earlier today layouts still weren’t working.

The key problem with overriding layouts is that the process of identifying them relies on some class methods on ActionController::Base (provided in the ActionController::Layout module). Roughly put we have:

  1. ActionController::Base#render calls ActionController::Base#pick_layout
  2. ActionController::Base#pick_layout checks to see if there’s a specific layout requested and calls ActionController::Base#active_layout
  3. ActionController::Base#active_layout checks whether there’s a candidate layout for this controller or action and makes sure the layout exists by calling ActionController::Base.find_layout
  4. ActionController::Base.find_layout (class method) is called which checks the view_paths to find the appropriate layout in there

The issue is that as a class method ActionController::Base.active_layout has no knowledge of the specific controller instance, or the request object it contains, and so it can’t access our logic to determine the current theme.

One option would be to patch a number of these methods to hand a controller instance around which could be queried for the theme, but that seems rather clunky.

Another option is to wrap the active_layout method so that it (as an instance method) checks whether there is a current theme and if so, adds that theme’s path to the class level view_paths, removing it again after that check:

  class ActionController::Base
    alias_method :theme_support_active_layout, :active_layout

    def active_layout(passed_layout = nil)
      if current_theme
        theme_path = File.join(RAILS_ROOT, "themes", controller.current_theme, "views")
        if File.exists?(theme_path) and ! self.class.view_paths.include?(theme_path)
          self.class.view_paths.unshift(theme_path)
          result = theme_support_active_layout(passed_layout)
          self.class.view_paths.shift
          return result
        end
      end

      theme_support_active_layout(passed_layout)
    end
  end

And it works!

So that’s views, emails and now layouts all working with theme_support on Rails 2.3. As before, you can get the results in the branch on github:

http://github.com/jystewart/theme_support/tree/rails-2-3-support

Over the next couple of weeks I’m hoping to test my patches on other releases in the 2.x series, probably using a test app with specs or stories that will put it through its paces. Once that’s done I may turn my attention to the other corners of the plugin (that I don’t personally use) like the asset helper methods, but if anyone wants to contribute patches in the meantime I’d love to get them.

Tags: , , , ,

11 comments

  1. I’m sure it shouldn’t be this hard to bend Rails to do things like this. Hopefully the Merb philosophy of having lots of hocks for this sort of thing will shine through in Rails 3.

    Something as simple as changing the location of js and css files required me to replace some internal constants: http://thelucid.com/2008/11/29/storing-js-files-and-css-files-in-js-and-css-directories-in-rails-22/

  2. Do you know what the deal with ActionController::Base.active_layout is in rails 2.3.0?

    It doesn’t behave as it used to, but I can’t find anything about this anywhere. I would think this would be noted in the 2.3 release notes or something, but it doesn’t. Is this a bug, or an intentional change?

    If you can enlighten me about that I would definitely appreciate it!

  3. Zef – I’ve no idea what the thinking behind the change was. Might be worth asking on the rails-core email list?

  4. I have a problem with themed ActionMailer layouts. I simply can’t get them running. The overridden method ActionController::Base#active_layout is only called for page layouts, not for mail layouts.
    I set the theme via self.current_theme = ‘…’ in an instance method, the layout is annotated via “layout ’email'”.

    Do you have any hints? Can I provide further information?

    Thanks,
    der Flo

  5. der Flo – are you using my latest version from github. There were a few changes for 2.3.2 last week.

    I’ve not actually tested with mailer layouts. Will try to do that soon, but feel free to submit patches.

  6. The problem is that the additions of “actioncontroller_ex.rb” no longer affect ActionMailer.
    I had to ActionController::Base.class_eval the complete code, then it works.

    Patches: I hope to provide some refactored/cleaned stuff this weekend. There are also some other fixes and improvements I can share.

    Bye,
    der Flo

  7. James, did you receive my merge request? What do you think about it?

    der Flo

  8. @der Flo – I’ve only just had a chance to start reviewing the various pull requests. Your changes look good, though it’s a bit awkward that it’s a single commit as it’s taking a while to work out which bits are inter-related.

    I’m going to start trying to work through them over the next day or two and will hopefully have them integrated soon.

  9. @James: Oh dear, I’m new to github, so I packed everything in one commit. Good to know that it is better to do more commits for reintegration.

    Don’t let you stress, take your time to review all that stuff. My primary intent is to help having a primary source for this awesome plugin. Let me know if I can help.

    Thanks and bye,

    der Flo

  10. I have upgraded to 2.3.2 and I am determining layouts based on URL in the application.

    In application_controller, get_layout_details gets the layout information and sets self.class.layout (It is called before every call as it is set as before_filter for application_controller and working in earlier version)

    Home page renders fine. When user makes a search, a 302 redirect happens internally and then there is no layout on results page. Forcing a layout :file_name in searchresults_controller is also not working.

    I am not using the theme plugin, so what should I do to make it work in rails 2.3.2

    Please advise.

    Thanks,
    Rajat

    Ticket – https://rails.lighthouseapp.com/projects/8994/tickets/2555-dynamic-layout-selfclasslayout-doesnt-work-in-232#ticket-2555-1

  11. @der Flo – not to worry. One of the great things with git is that you can do lots of small commits locally until you’re happy with things, and then push those changes up as a group. There are also some features around for massaging a group of commits into other forms after the fact, but I’m not so familiar with that.

    I’ve just pushed up a new version of my fork with a number of your changes integrated.