Archive for category Uncategorized

Patching Formtastic to append HTML to input list elements

Formtastic is pretty killer when it comes to creating nice looking forms very quickly. I love the short syntax compared to the default Rails form helpers, but sometimes I find the black box helpers a bit restricting when it comes to customizing the forms. Usually modifying the CSS is enough to get desired results, but I ran into a case today where it would have been an ugly hack to make it work with CSS.

Problem

I need to add HTML inside the <li> tag that is produced by the formtastic input helper. The helper produces HTML that looks like this:

I want to insert a link in betweent the input field and the inline hints. This is not possible, since the formtastic input helper outputs this entire HTML snippet. You can customize plenty of things within the helper, but I did not see a way to add more html content.

Solution

As I started to dig through the formtastic source I noticed that all of the input elements are using a wrapper module that produces the content inside the <li> tag. Take a look at this module:

All of the Formtastic input classes (StringInput, TextInput, HiddenInput,etc..) pass a block to the input_wrapping method inside their to_html method. The content_tag method takes the element name, content and element options as arguments. See here for more info: http://apidock.com/rails/ActionView/Helpers/TagHelper/content_tag
In order to add my content, I will need to add a String element (my HTML) to this array:

As you can see in the code above, it is just an array of strings that are joined together by carriage returns and marked as safe using the Rails String core_ext html_safe. So I will have to insert my HTML code in the desired position of this array. In my scenario, it makes sense to put the extra HTML between the input tag and the error html. I decided to monkey patch this method (oops, maybe dangerous if I ever upgrade formtastic for this app). I added this code to /app/config/initializers/formtastic_monkey_patch.rb:

The first thing I needed to patch was the Html module. 3 methods were added to this module to allow me to pass :extra_html and :extra_html_class to the formtastic input helper. This was pretty much a copy/paste from “Formtastic::Inputs::Base::Hints”. Then I just had to add the “extra_html” to the array inside the “input_wrapping” method. Now I can do this in my form:

This will output:

I can take this a step further by adding another option for the order that the extra html will appear inside the <li> tag. New monkey patch:

I added one method inside the Html class to set a default extra_html_position within the options hash. Then I modified “input wrapping” so it inserts the extra_html into the specified position. Here is the new formtastic input call in my view:

By passing the :extra_html_position option with a value of zero, formtastic will now insert the extra_html before the input tag. So there ya go… monkey-patched and ready to roll.

Tags: ,

Testing Facebook authentication with Rails 3 Cucumber, Capybara, Selenium

I am in the process of creating an engine that will simplify adding single signon to my app using omniauth & devise. One of the requirements is testing this with Cucumber, and it took me a little while to figure this out, so I figured I would pass it on.

Add a test app on Facebook

The first thing you need to do is set up a test app on the Facebook developers site. This will allow you to use localhost as the callback url. Otherwise, FB API will not let you authenticate, b/c the FB App configuration URL will not match your app’s URL. You will probably want 3 different FB apps. One for dev, test, and prod using localhost:3000, locahost:3001 and yourprodurl.com.

In your test FB app settings, click the “Web Site” tab and make the Site URL = localhost:3001

Save your setttings.

Add mongrel to Gemfile

I added mongrel to my test environment instead of using webrick (b/c of issues with header size of google apps callback). I would recommend doing the same. Just add it to your gemfile under the dev and test groups. Here is my Gemfile (with a bunch of other testing gems included):

Make sure you run:

cowboycoded$  bundle install

Change Capybara server settings

Add these lines to “features/support/env.rb”:

You need to specify the port so mongrel is forced to start on 3001. You also need to specify the app_host as localhost, because the default “127.0.0.1″ was causing problems with FB API (url encoding I think)

Use Selenium in your scenarios

Preface your cucumber scenarios with @selenium, so you can navigate through external sites like Facebook

Of course you need to set up your cucumber step, based on how you are authenticating against facebook. I don’t provide the implementation details here. Just know that cuc is hitting the facebook URL in the first step of this scenario.

Run cucumber scenarios

cowboycoded$  bundle exec cucumber features/

You should be good to go now. Facebook will use the correct callback URL and your server will be running on the correct port to handle it.

Tags: , , ,

Round to half intervals (round with fractions) in Ruby

Let’s say you need to round to half numbers in Ruby, because you are making a rating system and want to display half stars for the ratings (1, 1.5, 2, 2.5, etc..). You want the following output when you are trying to round to the half fraction:

round_to_half( 1.6 )
=> 1.5

round_to_half( 1.7 )
=> 1.5

round_to_half( 1.8 )
=> 2.0

round_to_half( 2.1)
=> 2.0

round_to_half( 2.3 )
=> 2.5

etc…..

It is much simpler than I anticpated. You just need to multiply by the the inverse of the fraction (am I saying that right??), round the number and divide by the same number. So if you want to round to half places, you would do this:

1. flip 1/2 around to make it 2/1 (or 2)
2. multiply by the number you want to round
3. round the result from step 2
4. divide the result from step 3 by the number you got in step 1 (2 in this case)

Lets start with the first one in my example and run through the steps:

1. 2/1 => 2
2. 5.6 * 2 => 11.2
3. 11.2.round => 11
4. 11 / 2.0 => 5.5

And here it is in a Ruby method:

So you can pass any fraction to this method and it will round to that fraction. For example, you can try this with 20ths:

round_to_fraction(2.224, 1.0/20.0)
=> 2.2

round_to_fraction(2.225, 1.0/20.0)
=> 2.25

Remember to add the .0 to the end of the numbers you are dividing, so it forces ruby to treat it as a float. 1.0 / 20.0

Tags: , ,

Add existing records to the versions table in paper_trail

I was working with paper_trail today. It is an excellent gem that saves a history of your creates, updates, and destroys for Rails ActiveRecord models. I am adding this gem to an existing project that already has records in the database. I need to put these records in the paper_trail’s versions table, so I can use a query that checks to see how many versions exist for a particular model instance. Just to make it clean, so I don’t have to query both the model and the versions table, I want to add that initial “create” version for all of my existing records. After reading through the source, I could not figure out an easy way to do it with the existing codebase, so I decided to create a simple monkey patch and rake task that will version all of my existing records.

This rake task adds a new instance method, so the model can have access to the private method “record_create” in paper_trail. Then it takes all model classes, iterates through them and detects the classes that have paper_trail enabled. If the record has no versions, then it creates the initial version and prints out the result. Now I have a fresh set of data that makes it look like paper_trail was installed to begin with (minus any history I’ve already lost).

Tags: ,

Lock down a Rails 3 app with a single password using lock

This tutorial shows you more about the power and simplicity of Rails 3 Engines. When I release a new feature for one of my  production Rails apps (or an entirely new app) that needs to be beta tested by a select group of people, it is usually necessary to password protect part or all of the application.  A quick and dirty way to do this is to use basic http authentication with Nginx. Previously, I would choose this method because it is fairly easy to set up and it allowed me to create a single username and password for an entire group of testers, without having to modify the user authentication system that I use inside my Rails apps. If you are using nginx, then you can add something like this to the nginx.conf file:
It is also necessary to create the htpasswd file and add the user name and password hash.  The password hash can be generated using Ruby’s String crypt method.  When users visit the protected URL, the basic http auth browser popup is displayed and if they enter the correct login information, they gain access to the protected URL.  While this setup is fairly simple, I prefer staying within Rails to achieve this and having more control over the login page, etc.  HTTP Basic Authentication is a bit too basic for me. I decided to create a simple Rails 3 Engine that would allow me to easily lock down controller/actions and customize the login and error pages.  I call this one lock. Note that this will not protect your public assets, only the Rails actions… so you may want to lock down your server using http basic if you need public assets protected as well.

Here is how you use it in your own application:

1. Install the gem

#Gemfile
gem “lock”
cowboycoded$  bundle install

2. Generate the password file

cowboycoded$  rails g lock:create_password_file yourpasswordhere

3. Add the lock configuration to application_controller.rb

4. Unlock the app

visit this URL and enter the password you created with the generator

http://www.yoursitehere.com/lock/login

5. Override the views:

Login Page: /app/views/lock/login.html.erb #NOTE: POST to /lock/unlock and use a field named “password”
Refused Access Page: /app/views/lock/refused.html.erb
Unlock Confirmation Page: /app/views/lock/unlock.html.erb

By default, these views will render inside your default layout. To create a custom layout for these files, just add /app/views/layouts/lock.html.erb The layout must contain a yield.

TECH DETAILS ABOUT THE GEM

(if you are interested in how the gem was created)

The design around this was simple. Use a class method within the application_controller that accepts controllers & actions as parameters. The class method creates a before_filter and that takes these controllers & actions and compares them against the requested URL. If the URL matches the controller/action and the user has not unlocked the app, then access to the page is denied. Using Rails 3 engines makes it easy to add views and a LockController to handle the login, failed login and unlock confirmation pages. Here are the gory details about how this gem was created:

A Note about TESTING

I am using RSpec and Cucumber to test this engine.  Engines are a different beast than other gems, because they are so closely tied to Rails.  As a result of this, I prefer to create a new Rails 3 app inside my gem called “test_app” for any testing.  I have seen a few things out there about creating a dummy app, that I am guessing is a stripped down version of a real rails app, but I am not sure what the advantage is over a real Rails app for testing.  The footprint of a blank Rails app is not that large, so I doubt it is a concern about gem file size (which should not matter, since the test files are not packaged with the gem).  I still need to read up and give it a fair comparison, but for now I dont see a problem with using a real Rails app to test engines.

First, I bootstrap the “test_app” with rspec and cucumber.  Then I include the local path to the gem in the Gemfile:

#Gemfile

gem “lock”, :path=>”/my_plugins/lock”

Providing the path to the local gem, will tell Bundler to load the files in that directory as opposed to installed the packaged gem. This is necessary, in order to avoid building your gem after every code change. If I remember correctly, you will need to generate a gemspec in the root of the gem before you bundle install with the “path” option.

Start with a gem skelaton and make it an engine

The skelaton can be as simple as a “lib” directory and a file inside the lib directory named “lock.rb”. To make it an engine only one file is needed. I always call it “engine.rb” and place it within a subdirectory of lib that matches the name of my engine. So here is my directory structure so far:

/lock/lib/lock.rb
/lock/lib/lock/engine.rb

When the gem is loaded, it will first execute lock.rb, so the engine.rb file must be required in lock.rb:

The engine.rb file needs to subclass Rails::Engine, require rails and lock itself, in order to make it an engine that Rails can load:

Now the gem can function as an engine. So what does that really mean? Well for one, you use the same directory structure as you would in a normal Rails app. And you can add initializers to engine.rb to interact with rails when it is loading at boot.

Add the Rails-like directory structure

Take a look at my Rails-like directory structure:

The helper & model directories are not necessary for this gem, but I went ahead and added them in case I need them later.

Add the views & controllers

As I mentioned earlier I have 3 views that I use for the login form, unlock confirmation, and refusal page… there is not much to these views. Rails Engines also allow you to add routes, so I placed mine in /lock/config/routes.rb. Most of the code (and its not really much) is in the controllers. I have a lock_controller that handles the login. As you can see, it just checks to see if the form password is equal to the generated password. I placed the “password_match?” method in /lib/lock.rb, to reduce the logic in the controller.

I wrote a few blogs in March about how Rails prevents you from reopening engine classes. Basically, if you have the file in the parent Rails app, then the one in the engine is ignored. For this reason, I was not able to create a file named “application_contoller.rb” and simply add my methods to it. In order to make this work, I have to use a different file name (lock_application_controller.rb) and include/extend the methods into action_controller using an initializer. Here is what the module looks like:

When you create a class method and put it in action_controller, then application_controller can call this method and make it look like a nice DSL (as you can see in step #3 of the gem installation tutorial). So this class method just adds a before_filter and an instance method is created that matches what the before_filter is calling. lock_filter checks to see if it is a locked action and if the user has already unlocked the app. It redirects them to the refusal URL if they have not unlocked and the action is on the lock list. Note that you may prefer to use ActiveSupport::Concern when coding a module that includes instance methods and extends class methods. I am not using it here, but many find the syntax a bit sweeter, so if you dont know about it then google it.

Use an initializer to include the instance methods and extend the class methods in action_controller

There is a pretty cool feature in Rails 3, which allows you to lazy load code in initializers. Check out Simone Carletti’s tutorial on Lazy Load Hooks for details on how this works. In short, when action_controller is loaded, our code can be placed in that class. So I want to include/extend my methods in action_controller. Here is what the engine.rb file looks like after I add this code:

I find that the engine.rb is the best place to put code that dynamically modifies classes at boot. It gives you a central location where you can place all of these calls, and IMO makes engines easier to read. You know exactly what modifications are being made to Rails, just by looking at the engine file.

Creating the password generator

The last piece of the puzzle is to create a Rails generator to spit out our hashed password file. When you make your gem an engine, Rails automatically picks up your generator if you put it in the default path that is expected from an engine. Here is what the directory structure looks like:

So Rails is expecting this path for the generator class (expressing variables in uppercase):

/GEM_NAME/lib/generators/GEM_NAME/GENERATOR_NAME/GENERATOR_NAME_generator.rb

USAGE will allow rails to ouput some documentation for it, and the templates directory (which I don’t use in lock) can house any template files needed by the generator. The generator class is pretty basic:

Subclassing “Rails::Generators::Base” gives you the functionality you need to make this a generator. Any methods contained within this class will be executed. Notice that the argument class method is necessary to pull in the command line arg for the password. source_root will tell the generator where your templates are stored. This code generates a salt and a password hash from the password & salt combination and writes it to a file using the create_file method. You can read more about Rails generators HERE.

So there you have it. Pretty basic, but IMO useful. I hope you find this gem useful and also hope you learned something about Rails Engines. If you have any problems with this gem, leave me a comment or open a github issue. As always, suggestions are welcome, as well as criticism!

Tags: , ,

Application-wide variables for your Rails 3 app using Yettings

Application-wide settings for Rails apps can be very useful in many different scenarios.  For example, let’s say you want a place to store API keys for various 3rd party services used in your app.  Instead of hard-coding them in the model, I think it is much cleaner to have a settings file where you store all of this information.  And to make things even easier, you should be able to define different values for dev, test, and production.  There are many ways to achieve this.  You can set some Ruby constants and namespace them or create a model that stores these settings in the database.  I prefer a simpler solution that does not require much setup.  Initially I was using settingslogic, which I found very useful, but I decided to take this a bit further and build a plugin that I could maintain and one that fits my needs.  Yettings is what I came up with.  While very much inspired by settingslogic, the key difference is not having to define a class along with your YAML file.  The class you will use to access your key/value pairs is created dynamically at boot time, based on the name of the yml file in your config directory.

Here is how you use Yettings in your own app:

Install the gem

Add Yettings to your Gemfile and “bundle install”

#Gemfile
gem “yettings”
#command line
> bundle install

Create a YAML file: app/config/yetting.yml

This file will define all of your key/value pairs. Include all relevant key/value pairs in the default section and anything related to a specific environment in the dev, test, production sections. Note that custom environments will also work without any additional setup. staging for example. Any valid YAML will work, and you can also use erb within the YAML.

Access the values anywhere in your Rails app

Creating custom named Yettings

If you need separate Yettings for different pieces of your app (or if you are using this in a rails plugin), then you can add the yml file to a subdirectory named “yettings”. For instance:

app/config/yettings/main.yml
app/config/yettings/secondary.yml

Yettings will append the name you specified in camel case to the class that you access the values with:

I am using this with several of my rails apps and so far it seems to be working well. If you have any suggestions or issues, please leave me a comment or open up a github issue in the repo.

Tags: , ,

Customizing your embedded gists with CSS

I have always been less than pleased with the way my blog looks, especially relating to the code snippets, but honestly changing the template hasn’t been that high on my priority list. I started writing a new blog today and decided to start using github gists instead of looking for a better syntax highlighter plugin for wordpress. The gists always look slick and handle just about every language syntax out there. But I have been liking the rounded box look more and more recently… and the gists are plain old rectangle boxes. So I decided to spruce up the gists a bit with CSS. When you create a gist on github, they give you a javascript snippet that will embed the gist into your website. So I pasted that in my blog post, looked at the preview and using Chrome’s built in developer tools I was able to see that the snippet simply adds elements to the DOM and pops in a CSS file. It is easy to override the default styles of the gist. Note that it is important that you use “!important” in each CSS attribute, since the gist stylesheet will be loaded after your sites stylesheet, giving it priority over your styles, unless you use the “!important”. Here is what I added to my stylesheet to make it a nice looking rounded box:

Enjoy!

Tags: , ,

Why you can’t reopen Rails 3 Engine classes from the parent app

My main frustration with Rails 3 engines is not being able to reopen a class from the parent application.  The first time I used Engines, I tried this:

#authentication stuff in my engine
/my_engine/app/controllers/users_controller.rb

#reopen the users_controller.rb in the parent app and override a method or add a new method
/my_app/app/controllers/users_controller.rb

This can be useful for several reasons:
1. Adding methods to your engine’s models, controllers etc from the parent app.
2. Overriding engine methods from the parent app.  In the example above, I wanted to add some behavior to the create method.

Although not everyone will care about the internals of a widely used open source engine, many developers will find this useful when they write their own engines. You don’t want to have to copy the source from an engine controller or model over to your parent app, just to add or overwrite a method.

If you try to reopen the class using the method at the beginning of this post, then your engine UsersController class is never loaded. The app will instead load the users_controller.rb within its own “/app/controllers” directory. I figured their must be a sane reason for this… and I wanted to get to the root of the problem, so I started digging in the Rails source. From first glance, it looks like this restriction was possibly unintentional (although I don’t know enough about the internals to say for sure). Let’s see what is happening inside Rails:

The engine’s app directory will be in the eager_load_paths and it will hit the “require_dependency” line. “require_dependency” is part of ActiveSupport and it will search for the filename by iterating through the autoload paths and finding the first match in that autoload paths list. Here is the code in ActiveSupport (look at “search_for_file”):

debugging the autoload_paths produced the following result (with a few things removed for readability):

As you can see from the array above, the app paths show up before the engine paths.  As a result, Rails will always search for the file in the parent app’s paths before it searches in the engine’s paths, so *OUCH* it is not possible to re-open the class.  There are probably ways this can be done.. like maybe requiring the files explicitly in an initializer in the engine, but I would rather not resort to such hacks.  My temporary fix is to use class_eval in the parent app, but this is still really ugly.  Anyways.. its getting late and I am done for tonight. I guess I will keep poking around to see if there is a suitable workaround.

UPDATE

After looking around SO, I found a patch for ActiveSupport::Dependencies

With this patch, the require_or_load method is aliased and the engine classes are loaded before the original method is called. Gave it a quick test and so far so good.

Tags: ,

Working with ApplicationController in a Rails 3 Engine

UPDATE 02-13-2011: It appears that approach #2 does have a downside after all. Given my current implementation, dev will not reload the class b/c config.cache_classes is set to false. I might still try this approach using config.to_prepare.. I will update the post when I have a chance to experiment more.

Rails 3 Engines use a standard Rails app directory setup by default, so logically, you might think that you can just drop in /app/controllers/application_controller.rb, and reopen the class. This is not the case. Rails will ignore any files that have the same name as a file in the parent application. Seeing as how every Rails app has “/app/controllers/application_controller.rb”, the engine has no chance of loading this file. Poor little engine. But if your engine has a strong will… I think I can, I think I can.. then there are other ways to get your methods inside the parent app’s ApplicationController. Let’s take a look at some of the choices you have.

Option 1 – ActiveSupport.on_load

Lets start by looking at one of the most popular Rails plugins -> Devise. At the time of this writing, Devise has a helpers module that defines a class method called “define_helpers”. A mapping (user or admin) is passed to the method and several instance methods are defined using class_eval. Then these instance methods are included in the ApplicationController using the “helper_method” function. Check out how this is acheived:

ActiveSupport.on_load(:action_controller) do
  helper_method "current_#{mapping}", "#{mapping}_signed_in?", "#{mapping}_session"
end

on_load.. reminds me of something you would see in a javascript library. So when ActiveSupport loads the ActionController, the helper methods are inserted. You could also use a different approach within this block. Let’s say you have defined a module with submodules for ClassMethods and InstanceMethods. You would just extend/include them in the block. The most common approach for Rails 3 engines is using an initializer in the Engine class.

# You can put this in /myengine/app/models/ and it will load it automatically (or put it in your engine lib directory and require it)
module MyModule
  module ClassMethods
    def some_awesome_class_method
    end
  end
  module InstanceMethods
    def some_awesome_instance_method
    end
  end
end
#/myengine/lib/myengine/engine.rb
require "myengine"
require "rails"

module MyEngine
  class Engine < Rails::Engine
    initializer 'myengine.app_controller' do |app|
      ActiveSupport.on_load(:action_controller) do
        extend MyModule::ClassMethods
        include MyModule::InstanceMethods
      end
    end
  end
end

There are plenty of variations of the extend/include stuff that use ActiveSupport::Concern, “self.included”, “self.extended”, but they all achieve basically the same thing… adding instance and class methods.

Option 2 – Reopen the class

In the intro of this blog post, I said that Rails will not allow you to load classes from an engine with the same name as the parent app. When it comes to the ApplicationController, I am not sure if it was the intent of the Rails Core to deter you using the same name/format as the application_controller in a regular rails app… there may be use cases that I am not aware of where this is harmful or lacks functionality. It makes sense that other Engine classes with the same name/path should not be loaded since you would want the parent app to override them if it has the same controllers. This could result in clashes and unexpected consequences. But, I see the ApplicationController as a bit of a anomaly in this situation. Unless your parent app has tons of methods in the ApplicationController and the engine ApplicationController is using common method names, I don’t see this as a problem. Use fairly unique method names or prefix the method name with your plugin name if you are worried about it. There is a simple workaround to allow you to use the same format for ApplicationController that you are accustomed to seeing a standard Rails app… just prefix the file name with the plugin name for the ApplicationController. For example: /app/controllers/myplugin_application_controller.rb. Then you can add code to this file, just as I would with a Rails app ApplicationController. Check it out:

#myplugin_application_controller.rb
class ApplicationController < ActionController::Base
  before_filter :this_filter_is_the_best_thing_since_sliced_bread

  def this_filter_is_the_best_thing_since_sliced_bread
  end

  def really_cool_instance_method
  end

  def self.really_cool_class_method
  end
end

I think technically you are not reopening this class, since it gets loaded first.. so lets say we are defining the ApplicationController and the parent application is reopening the class. I did some quick tests and the Engine ApplicationController loads before the parent ApplicationController. I also wrote a spec to make sure all filters were intact using this method:

# get a list of the filter names from ApplicationController
filters = ApplicationController._process_action_callbacks.select { |c| c.kind == :before }
filter_names = filters.collect{|filter|filter.filter}
filter_names.include?(:this_filter_is_the_best_thing_since_sliced_bread).should be true #yup
filter_names.include?(:a_filter_defined_in_the_parent_app_controller).should be true #yup

Tags: , , ,

Making the case for Rails 3 Engines

Engines have been around for a while now in some for or another, but with the release of Rails 3, they became an integral part of the framework, and IMO they make it easy for any developer to create a plugin. Rails 3.1 will make Engines even better thanks to the SOC work done by Piotr Sarnacki, but I will be focusing on what is available in Rails 3.0.3 in this post.

Engine? Say what??

In case you are not familiar with Rails 3 Engines, in a nutshell… they are sub-applications that are packaged as gems and can run inside a standard Rails application. They give you most of the functionality of a standard rails app, including the controllers, views, helpers, configuration, etc… Engines lay the groundwork of having “mountable” apps in Rails.

So, why should I care?

I think this is useful on 2 fronts. The first being fully functional sub-applications such as a blogging engine. You could drop the engine into your Gemfile and you would immediately have the functionality of a full-featured blog that is running in the same process as your parent app. The second use is of more interest to me, although I have seen little discussion about it. I guess you could call it building blocks. These engines would be smaller than something like a blog. For example, lets say you want to add comments to your application. Right now I think most people would probably look at something like this: https://github.com/jackdempsey/acts_as_commentable. Pretty cool. It gives you a migration, a model and the polymorphic association needed to make a model “commentable”. But you are left with implementing the views & controller yourself. Although it is not terribly difficult to implement this, it still takes some time to do it. Let’s look at how an Engine could improve this plugin. You could basically do everything with 4 lines of code and a few commands.

#Gemfile
gem 'comment_engine'

# command shell - add the migration and run it
rails g comment_engine
rake db:migrate

#model
class Article < ActiveRecord::Base
is_commentable
end

#view
<%=comment_form(@article) %>
<%=comment_list(@article) %>

So you add the engine to your Gemfile, you make your model commentable and you use 2 helpers in your view. This will give you a fully functional comment form in your view and list of comments associated with that model. So how does the Engine make this so easy? You can use controllers, routes, partials, and helpers. So the plugin would include the following:

1. comment.rb – your comment model
2. generator – migration template for Comment
3. Commentable module – AR::Base will extend this module. Uses polymorphic associations to allow models to be “commentable”
4. Helper module – included in the parent app’s application_helper. This provides some shortcuts like comment_form and comment_list. Basically these methods take the model and generate a form or list based on that commentable model.
5. comments_controller.rb – has a create method that creates the comment and redirects to :back.
6. routes.rb – adds resource routes for comments.

While these are the necessities, there are plenty of other things that you could add to an engine like this, for example: attaching the authenticated user to the comment.

This makes development a breeze, as long as you have good defaults in the Engine. One of the main things that “building block” engine authors should focus on is the view portion of this. This is a fairly new concept when it comes to Rails plugins and I have not seen it implemented in many plugins. The plugin needs to be easy to style or give the developer the choice to completely override the view with their own. I guess it is also important to allow the parent app developer to override or add functionality to the controllers and models. The tricky part about that is re-opening the classes. Rails blocks the engine classes from loading if they are the same file name/path as something in the parent app. This is a concept that I am still exploring. Ideally, you would want to allow the developer to drop in a class with the same path that would reopen the class from the engine. Although this could be dangerous if the app developer is not aware of the class in the Engine, IMO it would be very useful if you use it wisely.

How I have been using Engines

I started working with Engines using the Rails 3 betas, and I am still working on figuring out the best practices. Although I am still experimenting quite a bit, Engines have become a major part of my workflow. IMO, the Engine plugin is the most important addition to Rail 3, since they make your code extremely modular and can increase your productivity by quite alot. I am really looking forward to seeing what kind of “building blocks” are created using this plugin architecture. Here is what I have focused on so far for my own environment:

1. authentication_engine – basic and simple authentication that includes signup, login and roles.  Views and controllers included.
2. social_engine - includes comments, ratings, reviews, votes, reputation, and helpers for things like facebook like and tweetmeme. Most if this is impelemented in the same manner as I described for the comments engine at the beginning of this post
3. ui_engine – includes good defaults for the base layout, a configuration for the layout, css, javascript, and plenty of partials that you can override in the parent app. Based on HTML5 boilerplate. My intention with this engine is to get rid of all the BS work you have to do for your front end. I know layout and view stuff differs alot per application, but I find myself doing alot of the same things for each app when it comes to UI.

I am working on extracting alot of the code into an open source plugin for #2 and #3. I should have the social_engine on github in the next few weeks. The UI engine definitely needs alot of work, in order to be useful to developers other than myself.

Conclusion

I love Rails and Rails 3 is leaps and bounds above the previous versions in terms of modularity and extensibility. I like the fact that it has a solid base architecture that plugins can sit on top of. Although there are plenty of productivity gains by using Rails alone, I think the plugins that sit on top of Rails give you the greatest productivity gains. I am looking forward to seeing the Engine ecosystem that is sure to develop in the next few years.   I think it will remove many of the mundane development tasks that developers have to worry about currently, allowing us to focus more on stitching together the building blocks that we need for our applications to function… not on the details of creating these building blocks.

Thoughts?  Leave me a comment

Tags: ,