You’d might think that Denmark (where I live), being the home country of DHH, has a large and active Ruby and Rails community. The truth is that neither Ruby or Rails is particularly well-known in Denmark, and that the first Danish Ruby/Rails user group that I’ve heard of has just been formed this summer. It’s called Copenhagen.rb (Copenhagen Ruby Brigade) and the second meeting takes place on August 16th at 5 PM in Lyngby.
Amongst the members of Copenhagen.rb are Jesper Rønn-Jensen, Olle Jonsson, Lars Pind , Jakob Skjerning and 25 other Rails-lovers near Copenhagen.
The agenda (published at the mailing list, since no website exists yet) looks like this:
The company hosting the meeting this time is called Nordija, and the address is Nørgaardsvej 7, 2800 Kgs. Lyngby – and again; it’s August 16 at 5 PM. I’ll be there, and I hope you’ll be there too, if you are in the area!
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.
vendor/plugins/engines to http://svn.rails-engines.org/engines/tags/rel_1.1.3/ (or just download the files to that directory).config/environment.rb:
# environment.rb
module Engines
CONFIG = {:edge => true}
end
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/.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.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
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.)
list action of the user controller, simply create the file list.rhtml in /app/views/user/, and this file will be used.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
/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
RailsConf was a blast. But I barely had the time to consume all the impressions, since my vacation was just after the event in Chicago. So I went directly to Roskilde Festival, North Europe’s biggest festival with 100.000 partying people near Copenhagen, Denmark, and from there I got on a bus to Rumania as part of choir singing Danish songs in the Rumanian churches. When I got back, I realized my blog had gained quite a lot of attention (at least compared to its very silent past), not at least my posts on David Heinemeier Hansson’s and Paul Graham’s key notes. While 70 subscribers and 200 visitors a day doesn’t sound like much to some, it fills me with a certain amount of humility: “Surely people will expect my future to be of high professional quality and deep insight”, I think.
See, my problem is, that while I seem to do pretty good on referencing what really, really smart people say and adding a bit of my own pocket philosophy, I find it a wee bit harder to provide genuinely usable Rails knowledge on my own. I have plans for posts about my on-going experiences with Substruct, and about my struggles with actually being RESTful when developing, but for now, as a way to scare most of my newly gained audience away again, I’ve decided to write a completely non-technical and probably quite un-insightful blog about some the great people I met at RailsConf.
Always taking the analytical approach to things, I have divided those people into two categories: The VIPs and the VFPs. The first are VIPs of the Rails community; key note speakers, widely read bloggers, authors, etc – people I wanted to meet because I admired they work, but who won’t actually remember they met me afterwards. The second are some of the Very Friendly People I met at the conference, that is; people that I didn’t know beforehand, but that I ended of talking a lot to during the event. Of course the VIPs where also friendly, and the VFPs are probably also important (to some people), but most likely being a clever geek, I’m sure you get the idea.
VIPs
David Heinemeier Hansson: Of course everybody at the conference wanted to meet DHH, and my twist was supposed to be to surprise him by talking Danish to him. In fact I happened to be talking to David Black when DHH came along, and Black was so kind as to introduce us and take our picture, but there wasn’t any time to talk further, so I can’t say I have any insight on DHH I can share.
Geoffrey Grosenbach: I had a great deal of respect for Geoff because of this persistent work with the Ruby on Rails podcast and his many insights about deploying, so I really looked forward to attending this talk and meeting him. It was strange to look at the guy and hear his unique voice I knew so well from the podcasts; kind of like meeting some one talking like Darth Vader.
Amy Hoy: Amy is – literally speaking – a colorful part of the Rails community. Probably the most well-known girl in the community and the only female speaker at the conference, I of course had to meet her. I did, and she willingly lined up for a photo. What I don’t like about Amy is that she keeps reminding me that good – and fun – writing is hard work, and I just don’t have the patience to rewrite a sentence five times like she does, which means that my blogs will never match hers in enjoyability, I’m afraid.
David Black: I meet Black at “day 0″, where I didn’t get to participate in the Guidebook session, so I went early into the room reserved for improvised events, where he sat all by him self with his Mac. I didn’t know who he was – in fact, I thought he looked completely wrong in the setting (remember, the conference wasn’t started yet, and I hadn’t yet realized that Ruby gurus generally mature, bearded men), but he explained to me that he was the organizer. Later that day, I bought Black’s book in Chicago and got it signed by him – nice!
VFPs
Tim Trautmann: Migrated from Germany, Tim was the closest thing to a European I got to know at the conference. We enjoyed a nice dinner Friday, and he was part of the drinking buddies sitting outside the bar. Tim is a consultant, having a one-man company with a funny name I seem to have forgotten.
Jeremy Seitz & John: When I realized that nothing was going to happen at “day 0″ besides the Guidebook, I went to Chicago. I met Jeremy and John waiting for the shuttle bus, and we ended up buying a brand new Macbook for John at the Chicago Apple Store. I can’t seem find John’s last name or blog, but both of J’s was great fun to hang out with through out the conference days.
Brian Eng & Jeff Cohen: Meeting the Softies was definitely one of my goals at the conference since I really like their blog, and they turned out to be extremely friendly and great company. These guys actually works in cubicles like they do in Dilbert (which only seems to amusing to Europeans), so you’d think they be grumpy and bitter, but they where all smiles and even paid for our dinner at Chilies – I owe you one, guys!
Hisa: This cool Japanese actually wrote the RubyCocoa framework, which I definitely want to try out once I get my hands on a Macbook, and we talked many times during the conference. Hisa wasn’t bothered about his English being a bit rusty, and a really funny thing he explained to me, was that he found it just as to distinguish between western people, as we often find it to distinguish between Asians. Fair enough!
Jim Greer: Once the conference ended Sunday afternoon, Jim was one the people that – like me – was staying at the hotel till Monday. A minor hack fest formed in the bar that evening, and I sat opposite to Jim, writing on my blog about DHH’s key note. I discussed the whole normalization issue with him, and this actually inspired Jim to start up his blog and write his first post, which was later referenced in DHH’s blog about the issue. Jim is starting a web game company in England, and I’m pretty sure he will be a success.
Victoria: I had a great conversation about everything from the Rails community to the differences between USA and Denmark with one of the very few girls at RailsConf, while I was trying to spot James Adams, the creator of Rails Engines, with out any luck. Victoria was attending the 37signals workshop the following week, and I believe she promised to blog about it…
I met plenty of other nice and friendly and important people, but this has to end some where. I wish I could finish telling you how I much I look forward to RailsConf 2007 and seeing some of these people again, but the fact is that it’s very unlikely that I’ll be there. The conference is in May, and I’m in school – what do you do in school in May? You have exams. Tough break.
Hello, I'm Casper Fabricius. I have developed for the web for 10 years, and have been enjoying Ruby on Rails for the past 5.
My experience covers communities, shopping solutions, multi-language sites, heavy back-end lifting and a wide selection of more traditional websites. I like to integrate Ruby with Java and .NET through JRuby and IronRuby when it makes sense. I am passionate about test- and behavior-driven development, but at the same time I am pragmatic and believe in getting things done.
I live in Copenhagen, Denmark, where I work for a fantastic company: Podio. I do not currently take on freelance assignments.