Lightrail just got a lot hipper

Posted by jeff

I just released a new version of Lightrail (http://lightrail.zilkey.com/) that is significantly hipper than the earlier version. This version comes with support for taking your existing models and turning them into lightrail’s structure.

Soon to come will be similar options for controllers, helpers, migrations, specs, views, plugins, static assets and whatever else I can shove into a few near-term releases.

If you are one of the people thinking about using lightrail please be aware that the api may change significantly, and the locations of files and the names of generators etc.. are all subject to change.

Make any comments here, or at the github repository (http://github.com/zilkey/multiple_migration_paths/tree/master). See the Lightrail Rdoc (http://lightrail.zilkey.com/) for a more detailed walk through.

Introducing Lightrail: Productize your rails apps with generators

Posted by jeff

NOTE: this is an almost complete rewrite of the earlier article, written for v0.5.0.

Lightrail is a ruby on rails plugin generator intended to make it easier to share controllers, models, helpers, views and migrations across multiple apps without hacking Rails internals.

Basically, lightrail is a scaffold generator that generates the code into a plugin, rather than the main app directory.

Installation

Currently this is a gem, but you have to go through some hoops to install it:

1. Install the gems

  sudo gem install hoe newgem
  git clone git://github.com/zilkey/lightrail.git lightrail
  cd lightrail
  rake local_deploy

2. Generate your plugin generator

Once the plugin is installed, you can create a plugin which will store your reusable code.


script/generate lightrail_plugin plugin_name namespace

3. Install your reusable code with your plugin generator

Once you‘ve generated your plugin, you‘ll see that there is a new generator available. If you ran script/generate lightrail_plugin zilkey/product your generator would be named zilkey_product


script/generate zilkey_product

When you run your own generator, it:

  • Copies the controller, model, helper and views to the appropriate app directory
  • Copies all of the migrations in the plugins lib/db/migrate directory to the app‘s db/migrate directory

Example

1
2
3
4
5
6
7
8
9
rails lightrail-demo
cd lightrail-demo
script/generate lightrail_plugin zilkey cms
script/generate model page name:string
rake db:migrate
script/generate lightrail_pluginize_model page #=> creates a base class and an inherited class and sets the table name correctly
script/generate zilkey cms # => moves the generated files back to the app directory
# make some changes to the generated base class then run
script/generate lightrail_clone # => moves all of the changes you made in the _app_ back to the _plugin_

How it works

When you create a lightrail plugin like zilkey cms, the plugin has a directory structure like this:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
  |-- MIT-LICENSE
  |-- README
  |-- Rakefile
  |-- generators
  |   `-- your_plugin
  |       |-- USAGE
  |       `-- your_plugin_generator.rb
  |-- init.rb
  |-- install.rb
  |-- lib
  |   |-- create
  |   |   |-- app
  |   |   |   |-- controllers
  |   |   |   |   `-- your_namespace
  |   |   |   |-- helpers
  |   |   |   |   `-- your_namespace
  |   |   |   |-- models
  |   |   |   |   `-- your_namespace
  |   |   |   `-- views_your_namespace
  |   |   |-- config
  |   |   |   `-- initializers
  |   |   |-- db
  |   |   |   |-- migrate
  |   |   |   |   `-- migrate_your_namespace
  |   |   |-- lib
  |   |   |-- public
  |   |   `-- spec
  |   |       |-- controllers
  |   |       |-- fixtures
  |   |       |-- helpers
  |   |       |-- models
  |   |       `-- views
  |   `-- destroy
  |       |-- app
  |       |   |-- controllers
  |       |   |   `-- your_namespace
  |       |   |-- helpers
  |       |   |   `-- your_namespace
  |       |   |-- models
  |       |   |   `-- your_namespace
  |       |   `-- views_your_namespace
  |       |-- config
  |       |   `-- initializers
  |       |-- db
  |       |   |-- migrate
  |       |   |   `-- migrate_your_namespace
  |       |-- lib
  |       |-- public
  |       `-- spec
  |           |-- controllers
  |           |-- fixtures
  |           |-- helpers
  |           |-- models
  |           `-- views
  |-- tasks
  |   `-- your_plugin_tasks.rake
  `-- uninstall.rb

When you run script/generate on your plugin, you‘ll notice that your main RAILS_ROOT directory looks like this:

1
2
3
4
5
6
7
8
  |-- app
  |-- controllers
  |    `-- your_namespace
  |-- helpers
  |    `-- your_namespace
  |-- models
  |    `-- your_namespace
  `-- views_your_namespace

Notice that it‘s placed a copy of each of your controller, model, helpers and views and migrations in the main app directory.

So for every controller, helper, model, view, migration and route in your plugin there is a corresponding file in the usual location in your main app. In addition, the migration fits logically into your migration scheme (that is, it‘s renamed to have the correct numbering scheme). The files in your app directory inherit from, or include, the files in the plugin, and the base classes are generated into the app directory for easy editing and cloning.

You can create modules for your controllers and helpers, so that the files look like this:

app/controllers/articles_controller.rb

1
2
3
class ArticlesController < ApplicationController
  include Zilkey::ArticlesController
end

app/helpers/articles_helper.rb

1
2
3
module ArticlesHelper
  include Zilkey::ArticlesHelper
end

app/models/article.rb

1
2
class Article < Zilkey::Article
end

This means that you can override specific methods in the controller, helpers and model and those changes extend or replace the plugin‘s functionality. Changes to the base classes/modules can be shared across all apps. Customizations to each individual app remain untouched. Upgrading

Let‘s say you‘ve added a new field to Article and you‘ve updated one of the views. When you reinstall your plugin, and regenerate the code Rails will ask if you want to overwrite the existing files the same way it normally would.

New models, views, controllers, migrations and routes are added (actually, all files in the plugin’s create directory are added – so you can even add dependent plugins to your generator!)

Let‘s say you have an Article model that you want to share across 2 apps. One app has a separate Publisher model that has_many :articles, the other app does not. When you add a field to Article, you want to be able to seamlessly update it across both installations, but you don‘t want to have to rewrite your Publisher customizations each time. With Lightrail, it‘s easy – here‘s how it works:

  • Create an Article plugin
  • Install it on app 1
  • Install it on app 2
  • Add the publisher model to app 2 and modify the files in your app folder with the changes

When Rails updates

Rails is constantly coming out with new versions and upgrades. Other plugins and systems like appable_plugins and engines rely on changing rails internals. Lightrail changes no rails internals and instead adds a few new methods to the generator commands. Other than that it is non-obtrusive.

As an example, rails just updated it‘s migration names to be named-based as opposed to numeric. This plugin, without making any changes, works the same on Rails 2.0.2 and edge (rev 9166)

Guides for making code more reusable

  • Cut your views into smaller partials – this way you can update some parts of the page without updating others
  • Never change migrations – always add new migrations with the changes

Ease of development

When developing plugins, it‘s helpful to be able to make changes to the base classes. As such, all models, controllers, helpers and views are removed from the load_once paths so as you make changes to the plugin, you don‘t have to restart your web server to see changes (in development mode).