Hello Merb

Merb is getting merged into Ruby on Rails. Together they will become Rails 3, as announced by David Heinemeier Hansson here and Yehuda Katz here.

I wish I could claim to have been playing with Merb for a while and have some real insight into the framework, but to be honest, I haven’t. I still think this a very interesting news, though, and in my opinion this can only be good news for the Rails community. Already, the brilliant Merb guys are optimizing and improving Rails, for instance this 8% speed boost in using respond_to.

So, how can we expect the the merge to affect the Rails framework we know and love? Let’s peek into the possibilities of combining these two frameworks.

Continue reading

Advanced Radiant extensions

Radiant is probably the best Ruby on Rails content management system (CMS) available at the moment. I’m using it in several customer projects, and while Radiant is still quite limited and aims to stay that way, it is quite easy to extend and blend with ordinary Rails resources through its pendant to engines; extensions.

Like engines, extensions allows developers to share not just “plain” Ruby code between applications (which is done through plugins), but also to share controllers, views and routes between them, as well as maintaining separate migration versions for each extension. One difference between engines and extensions I’ve spotted, is that while engines allows the developer to override and add individual actions in controllers, extensions allow only for replacing an entire controller.

Recently, John W. Long, creator of Radiant, wrote a nice tutorial on the basics of extensions, so if you haven’t had a look at that, I’d recommend it before proceeding with this article. The rest of this text puts focus on how to alter the behavior of Radiant to suit your needs by developing an extension.

I’ll use one of my own extensions, the first to be published as open-source; File Based Layout, as an example on how to override, modify and add functionality to the core of Radiant. I developed this extension to bridge a critical gap between “static” Radiant pages and “dynamic” Rails pages; the non-existent ability to let them share the same layout. To achieve this, I had to alter the Layout model and the Site- and LayoutControllers. This is possible through the dynamic nature of Ruby, but it requires some knowledge about “metaprogramming” which I for one didn’t have before I started to develop extensions.

Take a look at file_based_layout_extension.rb. The activate method, invoked by Radiant at the proper time, includes code in the Layout model and the Site- and LayoutControllers located in modules placed in the lib directory of the extension:

  def activate
    Layout.send :include, FileBasedLayout::LayoutExt
    SiteController.send :include, FileBasedLayout::SiteControllerExt
    Admin::LayoutController.send :include, FileBasedLayout::LayoutControllerExt
  end

As other extensions might also want to extend models and controllers using similar module names, I have opted to namespace the modules with FileBasedLayout. Also, to make the three modules available to file_based_layout_extension.rb, they have to be required using require_dependency as I have in the top of the file.

To be able to store the name of a physical layout file with the Radiant Layout, I had to add a field the layouts table; layout_file. This is done by adding a migration file to the db/migrate directory, and running the command rake db:migrate:extensions. I also wanted to validate the length of this field, and add a convenience method to the Layout model called file_based? (can you guess what it does?). This is done in the self.included method in layout_ext.rb by invoking base.class_eval with a block of code to be added to the class:

    def self.included(base)
      base.class_eval {
        validates_length_of :layout_file, :allow_nil => true, :maximum => 100, :message => '%d-character limit'
        include InstanceMethods
      }
    end

    module InstanceMethods
      def file_based?
        ! layout_file.nil? && ! layout_file.empty?
      end
    end

This is pretty simple, as we are merely adding stuff to the Layout class, but if what if we wanted to override stuff of, say, the SiteController? To create the File Based Layout extension, I needed to override some pretty low-level stuff in Radiant, in particular the show_uncached_page method, which is invoked when Radiant gets a request for a page not placed in the cache. It’s not enough to just include a module with this method, as I do in layout_controller_ext.rb, as methods in the class takes precedence over included methods.

Instead, I had to place my new show_uncached_page method directly inside the block given to base.class_eval in site_controller_ext.rb. This way, the new method is, so to speak, placed last in the SiteController class, overriding previous definitions of the method:

def self.included(base)
  base.class_eval {
    private

    def show_uncached_page(url)
      @page = find_page(url)
      unless @page.nil?
        if @page.layout && @page.layout.file_based?
          render :text => process_with_file_based_layout(@page), :layout => File.basename(@page.layout.layout_file, File.extname(@page.layout.layout_file))
        else
		...
   end

   ...

  }
end

And that’s it, really. When extending and changing Radiant or other Rails applications, the include, self.included and base.class_eval methods are your friends. There are, of course, performance penalties connected with especially class_eval, but as Radiant pages are cached, we don’t have to care too much about them.

Inside Rails Engines

Rails Engines are small subsets of an application that can be dropped into any of your Rails applications and handle common parts of the application from scratch. The title might be a bit misleading, since the technical side of this article is really a beginner’s introduction to Rails Engines spiced up with a few tips and tricks. I’m not diving into the inner workings of engines – the “inside” part of the title is more aimed against the fact that I’ve – out of pure curiosity and as a preparation for this article – asked the author of the engine plugin and the first and still most popular engines; James Adam, a few questions about his thoughts behind the engines and which direction he wants them to go in.

What is an engine?
There are two aspects of Rails Engines: There’s the engine plugin, a standard Rails plugin that you need to install as any other plugin. This plugin enables you install the individual actual engines that you want to use – the engines are also seen as standard plugins from Rails’ point of view, but they won’t work without the engine plugin.

An engine works by allowing a large part of the standard Rails application structure (that is; the /app directory in your projects) to exist as a part of the engine. Where a normal plugin only compromises Ruby code (often core extensions and helper classes), an engine are capable of having controllers, views and models that behaves like they are situated in the “real” /app directory.

This allows you to reuse a lot of traditional support functionality such as authentication, authorization and user management, a wiki, a shopping basket, a blog, a CMS and so on. Several engine implementations of some of this functionality already exist, most of which can be found through Rails-Engines.org, and while the wonders of CSS can get you a long way in getting these engines to fit into the design of the rest of your app, you will almost certainly want to change some things. Enter Rails Engine’s biggest advantage: The ability to override individual views and controller actions on a pr. need basis.

Let’s say you want to add some text and images to the login view of the Login engine, because this action effectively will function as your frontpage. All have to do is to copy the view from the /app/views directory of the engine into the corresponding directory in your own views directory – and through the magic of the engine plugin, this view now overrides the view that came with the engine. Often you’ll also need to modify the behavior of various actions supplied by the engine – you do this simply by creating a controller with the same name as the controller in the engine with action you want to override in your own /app/controllers directory, and define the actions in the controller. The engine plugin is able to combine the controllers of the engine with the ones in your app, so you can both override actions already defined by the engine, and you can add actions to a controller defined by the engine.

When should you use an engine?
While it is indeed very convenient to be able to drop all this functionality into your app without writing any code your self, you should be aware that this ability is not the primary goal with the original engine plugin, and that many people sees a lot of potential problems in constructing an application from one or more engines. James Adam explained it like this to me in an email:

Rails and Ruby make developing applications very easy, so there’s very little reason why a competent developer couldn’t write every part of the application that they need, and not rely on any code from outside. Working in this way has significant benefits, because it puts you in a position where you intimately understand exactly how your application works. There are no mysterious black boxes.

So when should you use an engine? James’ original intention with Rails Engines, and the way he continues to use them himself, is by abstracting commonly used application “chunks” so they can be reused in his company’s applications:

Where I work, we are working on a varying collection of fundamentally similar applications, all at the same time. They don’t have widely different requirements in terms of their basic architecture (authentication, importing Excel data, reporting back, loading and storing data and so on…), so it makes sense that we only maintain a single version of each of these shared aspects. “Enterprise DRY”, if you like. This is why engines exists; we wrote the engines plugin to make it possible for use to reuse as much of this shared code as made sense in our situation, in a manner that was easy to maintain where generators aren’t, but easy to override when the situation required it.

As for myself, I gladly use the Login, User, Riki and Substruct engines in several applications, and while I cannot claim that it haven’t taken an effort to integrate the engines into the application, I’d still say that it has saved me a considerable amount of time – especially when I’m reusing an engine that I already know well. I guess the ultimate DRY-proof solution would be to abstract my own versions of these engines – probably not to share with others, but use in my own projects working just the way I want them.

Implementing the Login and User engines in your application
Now to the more tutorial-ish aspects of this article. In this section, I’ll explain how to implement two of the most commonly used engines in your application. I expect you to have a running Rails application with a functional database connection. I also suggest that you setup a Subversion repository so you can add the plugins as externals, but it is not strictly necessary, just DRY’er.

  1. Install the Engine plugin

    1. Setup an SVN external attribute to point vendor/plugins/engines to http://svn.rails-engines.org/engines/tags/rel_1.1.3/ (or just download the files to that directory).
    2. The engine plugin is automatically loaded by Rails, and nothing visible happens until you install some engines. However, if you are running engines on Edge Rails, you should add this to the very top of config/environment.rb:
      # environment.rb
      module Engines
        CONFIG = {:edge => true}
      end
      
  2. Install the Login and User engines
    1. Setup SVN external attributes to point vendor/plugins/login_engine to http://svn.rails-engines.org/login_engine/tags/rel_1.0.2/ and vendor/plugins/user_engine to http://svn.rails-engines.org/user_engine/tags/rel_1.0.1/.
    2. Create two new migrations; login_engine_schema and user_engine_schema, and copy the contents of the migration(s) in the db/migrations of each engine into each of the new migrations. This way, you can still create the entire database structure with the rails migrate, without the need for running specialized engine tasks.
    3. Add the required configuration and startup code to config/environment.rb. The configuration is well documented in the read me files, so I won’t dive into that here, but instead provide a sample of my setup:
      # environment.rb
      
      #####################################################
      # Engine setup
      #####################################################
      
      module LoginEngine
        config :salt, "not_my_real_salt"
        #...
      end
      
      module UserEngine
        config :admin_login, "admin"
        #...
      end
      
      Engines.start :login, :user
      
      UserEngine.check_system_roles
      Permission.sync rescue false # Ignore any errors here
      
    4. Add code to application.rb in order to add user authorization to all actions, and to make the user-logic available to helpers and models:
      # application.rb
      
      require 'login_engine'
      require 'user_engine'
      
      class ApplicationController < ActionController::Base
      
        # Login and User Engines setup
        include LoginEngine
        include UserEngine
        helper :user
        model :user
        before_filter :authorize_action # unless RAILS_ENV == 'test'
      
      end
      

      (Be sure to also include Login- and UserEngine in application_helper.rb to make support available to the views.)

  3. Override views, actions and models as needed
    • A view is overridden by creating a view with the same name in your own app-structure - not by modifying the view directly in the engine. For example; if you want to override the template for the list action of the user controller, simply create the file list.rhtml in /app/views/user/, and this file will be used.
    • An action is overridden by creating the controller the action belongs to, and defining a function with the same name. For example; if you want to override the list action of the UserController to provide additional data to your list view, you should create the user_controller.rb file in /app/controllers/ and define the action:
      # user_controller.rb
      
      def list
        # ...
      end
      
    • Overriding a model is a bit more complicated, since the engine plugin has no clever way of supporting this due to various reasons. However, in the Login- and User engines, it is possible to extend the User-model because it is implemented as a namespaced module, which can be included in your own model-file. So if you want to associate a user with, say, a log entry, you can create the file /app/models/user.rb with this code:
      # user.rb
      
      class User < ActiveRecord::Base
        include LoginEngine::AuthenticatedUser
        include UserEngine::AuthorizedUser
      
        has_many :log_entries
      
      end
      

Tips and tricks
Many people complain that it is hard to test your application with authentication and authorization enabled. The easy solution is to add unless RAILS_ENV == 'test' to the before_filter :authorize_action line in application.rb. The better solution is to actually have a loaded user during the tests, since this is what the application generally expects. One way to solve this is to put this in your test case:

# /test/fixtures/companies_controller.rb

  def setup
    @controller = CompaniesController.new
    @request    = ActionController::TestRequest.new
    @response   = ActionController::TestResponse.new
    @request.session[:user] = User.find(1)
  end

Another issue the synchronization of the permissions data for the User engines authorization logic. I'm not entirely happy with the way this work, since the Permission.sync done on system startup tends to fail, which is why I've had to add rescue false to ignore any errors.

Some final thoughts
Should the standard Rails plugins go in the direction of engines? I asked James Adam this very question, and here is what he answered:

Yes and no. Some things that I feel should be easier in plugins:

  • Managing stylesheets/javscripts/etc in plugins. The engines plugin makes this trivial, and I'll be working on adapting the implementation of this feature so it seems natural in any plugin.
  • Sharing views in plugins. While you can do that with a plugin (overriding a controller's template_root), that seems like a bit of a kludge. I'd like to see a 'load_path' for views.
  • Controlling whether plugins are loaded or not. This is handy for development, and as a side effect, you get control over the plugin load order.

What shouldn't be a part of the default plugin system:

  • Overriding aspects of controllers, helpers and views automagically. This behavior is significantly different from that way that Rails works in a normal situation. Because of this, users should explicitly 'ask' (by installing/creating engines-based plugins) for this behavior, so they know what to expect. This is really the significant addition that engines add beyond what might seem natural for a plugin anyway.

Thank you for Rails Engines, James, and keep up the good work!

Additional resources