 So my name is Mike Parham. I'm here today to talk to you about a new feature in Rails 2.3, which is Rails Engines. I am a local Austin Ruby developer. I've been doing Ruby professionally for about two years now. And I discovered Rails a few years ago, probably like some of you, after doing Java for a number of years. And like I said, two years ago, I just decided that I had to do the Ruby thing. And so I'm currently working at a company called OneSpot here in town. Before that, I worked at a company called Five Runs. I'm the author of the Data Fabric Gym, which adds database sharding to ActiveRecord. And I'm also the maintainer of the Memcache Client Gym, which ships in Rails and is used to talk to the Memcache server. So let's step back in time a little bit and travel back a couple of years. Who here remembers the year 2004? I assume most of us were here. Ruby developers have a reputation for being very young, so. So 2004, Lord of the Rings was tearing up the Oscars and the Indonesian tsunami struck. So those are two big events in 2004, but the big event for us was, of course, Rails. DHH and 37 Signals Crew released Rails about five years ago. And at the time when you built a Rails app, that was it. It was just a Rails app. It was, you know, a directory with some Ruby code in it. And this is fine. This Ruby code initializes the Rails gyms, the Rails subsystem, and just conforms to some MVC conventions. It was light years ahead of most other systems people were using at the time. But people wrote their first app and they got really excited about it. Then they wrote their second app and their third app and their fourth app. And they started realizing there's a problem here. Flash forward two years later, 2006. Who remembers 2006? Well, I don't know about you, but you're smoking your cigars in your country club because your stock portfolio is going through the roof. Your home's worth at all time high. Unfortunately, LOL Cats had a death grip on websites at the time. So this is my homage to LOL Cat presentations. But in 2006, Rails introduced the concept of plugins. Now the problem was when you built all these applications, you had to copy code between all of them. So now with plugins, we have a way of sharing Ruby code and Ruby functionality easily, of encapsulating functionality. So Rails introduced this plugin script which added concepts of plugin sources that allowed you to discover new plugins, to list available plugins, and install plugins. What does this sound like? It's RubyGems. They essentially reinvented RubyGems for the first pass at plugins. So now Rails plugins are gems. They're special gems though. They have a Rails init file which Rails will run to bootstrap the code in that plugin. You simply add the gem to your config gem, your list of gems in your configuration, and Rails will add the lib directory for that plugin to its autoload path so that code can be autoloaded. And that's all fine and well, except people started noticing further issues, some functionality that couldn't be encapsulated. That is plugins can do Ruby stuff. They're very good at adding classes, changing existing classes, adding redefining methods, monkey patching in general. But they can't do anything with the MVC stack. They can't add views. They can't add controllers. They can't add routes. That sort of thing. So flash forward... And the reason why they can't do all this is because Rails has all this infrastructure that's completely separate from Ruby's load path, which is they have concept of controller paths. They have the concept of view paths. They have the concept of routing configuration that plugins have no idea what this is. So who here remembers 2009? We elected a president. We held a really cool Ruby conference in Austin, Texas. And we released Rails 2.3. And that's all a Rails engine is. It's a plugin which has the necessary hooks to encapsulate MVC-type functionality into something that is distributable, i.e., a gem. Really what it becomes is an engine is a Rails application. It's a Rails application that's running within your Rails application. That's one way of thinking about it. So to get down to the nitty-gritty, engines, just like a normal Rails application, have an app directory. And this app directory has the standard directories that you would expect any normal Rails application, i.e., views, controllers, models, and helpers. And Rails will auto-add those paths to the various subsystems, to the various paths that it knows about internally, so that it can find your code. Note that when you have two directories, which are competing to... which Rails is trying to find code in, it will always put the applications path last, or code in the applications directories will always be found, always be used in preference to the engines code. So going deeper down into models specifically, Rails will find any models that the engine provides in the app models directory. There is a limitation, though, that migrations don't work with engines. You can't have a DB migrate directory and have the database auto-built for that. Another thing to be aware of is just something to be aware of in general with Ruby code and plugins, and that is be aware of name collisions. That might be a good idea for your engine to create a user class, for instance. It's quite a popular name for a class. So instead, you use modules to namespace any classes that you need. That's very standard. All the engines I've seen in the wild these days are using namespaces to... or modules to namespace their code. So I don't know if people can read this. It doesn't look like it's very readable. But this is effectively how Rails... This is the code Rails actually uses to set up the v and the c part. Basically, it says if there's any engines, then we add the routing configuration, we add the controllers, and we add the views. And in each one, it's just... it's collecting the routing files, collecting the controller paths, and it's collecting the view paths. The only thing of interest there is at the bottom, which is it not only adds the view paths to ActionView, but also adds it to ActionMailer. Engines can also send email. So if you're using ActionMailer to send email, you can stick your view templates in the app views just like normal. And the engine can provide those views, and they'll be found. So controllers, Rails will look in your engine's app controllers directory. It will install routes from config routes, as you'd expect. One small cap, one small... I'm not sure if it's a bug or a by design, but one small limitation I found is that if you use helpers all, if you load all helpers into all of your controllers, Rails will not load any engine helpers. So you'll need to load that helper by hand in whichever controller of yours needs it. View templates will be found as normal, as you might expect. One limitation of the view layer is that a Rails application has a concept of a public directory where it's static assets go. Your engine, because it's running as a gem somewhere else, because EngineX and Apache look at that public directory and they don't know about your engine, any static assets you need that you would like to put in a public directory, you can't just put it in a public directory in your engine. You would need to copy those files to the application itself so that they can be served by the web server. Some miscellaneous notes, engines and plugins actually also can add rake tasks to the application just by adding .rake files to lib tasks. Like I said, about migrations and static assets, you can copy those files to the application's directories. That'll work fine. There's two ways I can think of to do that is by putting code in Rails init to do the file copy. That is when the engine is initialized, when the application starts up, it would do the copy. But again, because that's run every single time that would get redundant. Which does it. But that, of course, has the limitation that the user would need to invoke the rake task to ensure that it's done. Plugins used to have this install.rb hook which would run when the plugin was installed. Unfortunately, because we've moved to gyms we don't have that hook anymore. So you can't use that install.rb hook to copy those files. Here's some code that I wrote to solve the problem in my own engine. It's pretty straightforward. It's just doing a file copy from the engine's directory to the Rails application to the corresponding Rails applications directory. So one limitation in this is because we're copying files by hand is change management. If the Rails application's in get or subversion, obviously now that you're copying files to it the user would need to add those files to their source repository. And then they would need to manage it. If they upgrade your engine or downgrade a version they would correspondingly need to upgrade or downgrade those files that were copied over. Thoughtbot has an engine called clearance which I'll talk about in a second but they go to great lengths to deal with this problem. It can be some hairy code but unfortunately it's a limitation of engines. So like I said, there's a limitation that I'm aware of which is Thoughtbot's clearance which is user authentication for Rails applications. One big limitation in Rails in my opinion is the fact that we've never really settled on a way of doing user authentication by convention. Everybody seems to reinvent the wheel here and you've got plugins like RESTful authentication and whatnot but that's effectively copying a lot of code into your application. So we've never really settled on a way of doing this. Clearance is an attempt to push it forward one more level so that you have even less and less code in your own Rails application. But it is a full blown engine in that it provides controllers and views to do things like sign in, sign up forgot password it'll email the user when they sign up so it does a lot of the conventional stuff that you'd expect user authentication to do. For this talk I wrote my own engine called CASO and CASO is a dynamic search engine for a database model. You mark a database model as searchable and CASO will provide a user interface to construct a query for that model and find rows that match that query. So here's what CASO looks like in TextMate. It looks like any other normal Rails application that is you've got controllers, helpers, models and views. Here's how it integrates into your application. I simply I have a user model which I want to search on. I mark the user as CASO searchable and I give it some options to configure some of the features of CASO. In the controller I just include the helper because the helper provides some methods that are used in the view itself. In the view we just render the filter area and we render the results area for that particular model. This is what it looks like. You can see up top we've got a form to build a filter or a query whatever you want to call it. You add constraints, you can add sort expressions and you got a search button and then down below obviously this is not production ready it's not something I would consider. It's more of a toy for this talk but there's a kernel of functionality here that could be useful if myself and other people want to spend some time fleshing it out. As you can see this is a plugin and the only code in my application is really these four lines of code. So we're providing full MVC stack functionality in a reasonable set of code. So that's really just a quick sort of meander through engines itself as I've explained by trying to explain the history of it. It's really engines is really just the next step in making plugins more and more functional. First you had the monolithic app with no ability to share code then you had this concept of plugins where you could share Ruby code now you've got a concept of engines which not only allow you to share Ruby code but share full stack MVC functionality and that's all. Any questions? Yeah. Do you have any trouble testing any of these things? That's a good question. Obviously you need to have a framework and I wrote an example app which was the user model and other stuff and I tested that by hand for the most part. I don't pretend to be an expert in engine testing. Thoughtbot has that clearance engine they are very good at testing and I would guess that they've probably got a lot of infrastructure that you could look at in clearance to see some best practices for yourself. I don't necessarily know how to change a controller. Obviously you can monkey patch stuff but I'm not positive if you can do something like that. I'm not even... Can you just reopen it? That's what I meant by monkey patching. You can reopen the class and redefine an action but again you're copying code and making a fix or something like that. In terms of view templates applications view templates will always win so if you provide a file at that exact path where the engines view template would be found Rails will prefer the applications template so if you have a template you don't like or you need to style differently you can just copy it from the engine into your own application. So engines are not a new idea necessarily. They have been around for a couple of years now. There was a of what you'd call it a meta plugin or something that added an engine feature to Rails in the 1.x time frame. You could certainly use something like that if you needed to add engine type functionality but I'm not so sure that it would be I'm not so sure that it would be compatible in terms of having a source base that could act as an engine in 2.3 and an engine in 2.2 and 2.1 so I'm not so sure that something I would tackle it may be something you might want to examine but I don't know how that would work. Any other questions? I'm not sure I understand your question so your question is can you run an engine as an app by itself? It is probably a Rails application what it's probably not going to have would be things like the script directory to have the scripts to start server and console and stuff like that because technically it is a plugin it does have I like to think of it as 95% of a Rails application but there's still some infrastructure that an engine would not have but like I said with regard to the testing question too you have some sort of framework in which the engine runs so that you can test it so I don't see any reason why an engine couldn't ship an application which is just a really lightweight layer to expose its own functionality that you can use for testing for demos that sort of thing but I don't know how to do that by making it a gem and you'd have to go into the gem and type script server there right I don't know I guess that's the problem waiting to be solved anything else yeah interesting anything else I was actually just thought of that last night I was like I wonder if an engine can depend on a plugin in vendor plugins I don't think it's possible but it's an interesting Russian nesting dolls turtles all the way down I don't think that kind of recursion would work but might be interesting if it did ask him who is in the back he asked if Rails 3 is going to support engines or how it's going to change okay I'm sorry cool alright well I guess that's all thank you