README.txt

Path: README.txt
Last Update: Sun Apr 27 10:27:30 -0400 2008

Welcome to Lightrail - Code reuse made possible!

Installation

Until rubyforge gets the project up and running (or I create my own gem server) the installation is kind of wonky:

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

That will install it as a gem on your machine. Once the gem server issue is resolved, you will be able to just:

  sudo gem install lightrail

Instructions

Write your code (probably using standard rails generators to start out)

Generate your plugin with

  script/generate lightrail_plugin your_plugin_name [your_default_namespace]

Clone your existing code with

  script/generate lightrail_pluginize_model your_plugin_name your_namespace your_model_name

Replace your original code with the code from the generator with

  script/generate your_plugin_name your_namespace

Install your plugin into other apps and run the same generator

Now you are ready to make updates to the files, and push them back to the plugin. When you‘ve made changes to your app directory, you can refresh the plugin files from the app files with:

  script/generate lightrail_clone your_plugin_name

And voila! All of the "tracked" files (that is, files in your plugin‘s lib/create directory) are created! As an added bonus, if you delete a file in the main app that‘s being tracked, and you clone, it will delete that file.

Lightrail is basically a small set of tools that allows you to turn your standard rails files into more reusable code by creating base classes and reusable modules, and then having your rails code inherit from or reuse those base classes. It also copies and pastes any file you put in the generate directory into your rails directory - no code-generation-writing necessary!

Description

The idea is that it creates an app that you can run:

  • Entirely from the plugin, without running any generators (including migrations, with some tweaking - see below)
  • Entirely from generated files with your own overrides
  • A mix of both (like just overriding a model or a view and leaving the rest in tact)

When you run your plugin‘s generator it:

  • Copies all files in the vendor/plugins/your_plugin/lib/generate directory recursively to their counterparts in the app directory
  • Renames migrations to the current date so they‘ll appear last in the migration order, if any
  • Deletes any files that appear in the vendor/plugins/your_plugin/lib/destroy directory recursively

To generate or not to generate

Some things just don‘t need regeneration. They can live happily in the vendor directory and never be missed. Others may need to be tweaked. Sometimes you may need to mix it up a little - leave some in, generate some. The eventual goal of lightrail is to allow you to have apps that can run either from within a plugin, or be generated into an app - both have their merits.

What separates lightrail from other similar attempts is 1) it‘s simplicity (just base classes and modules) and 2) updates to rails core that make this approach possible.

The naming conventions of lightrail allow you to run code either from the vendor directory or generate that code and run it (and customize it) from the app itself.

The idea of a "parent" app

I tend to add features in one app, and wish I had them in others. Lightrail‘s cloning allows you to do this.

  • Create an app
  • Create the plugin
  • Clone the bits of code into the plugin
  • Generate the code into the app
  • Make changes to the code in the app
  • Run script/generate lightrail_pluginize to move all of the changes back (will only pick up on changes to files that have been previously cloned)

So you could have a single app with the generated code, and multiple apps with the code running from vendor/plugins, or be more git-like and have them all running from generated code so you can tweak and reuse whatever you like.

Total Hipness

Lightrail strives to be extremely hip, which means:

  • Younever have to write any generator code! (although you may want to comment a few lines out)
  • It reuses rails internals wherever possible, to minimize the chances of breaking when rails updates (it currently doesn‘t even flinch with the renaming of migrations in edge rails, for example)
  • It keeps track of your migration numbers for you, so when you generate a migration from the plugin it puts it in the correct order
  • The execution of the code does not rely on any magic or fancy plugins - just plain old base classes and modules that are very apparent. In fact, you don‘t even need lightrail installed to run the app - only to run the generators
  • It handles arbitrarily deeply nested namespaces, so it let‘s you code however you want
  • It lets you keep track of the files you delete, and will delete them for you when you generate the app (be careful with this! Rails does not prompt your for the deletion of files. Run script/generate —pretend first!)
  • You can keep anything in a lightrail plugin - even plugins and the vendor/rails! It just copies the files over. This means you can ship your dependencies along with your plugin!
  • Even though the code is in a plugin, when you make updates to base classes you don‘t have to restart the server!
  • You can choose to generate views, or uncomment one line in the plugin‘s init.rb and you can use the views in the plugin directly (except for mailers)!

A few of the things you can now now reuse with ease are:

  • Images
  • Stylesheets
  • Other plugins
  • Config files
  • Migrations

With the auto-tracking feature, you can keep the deleted files up to date too!

This gem follows in the footsteps of Rails Components (deprecated), Engines (which have spawned a few key rails core updates), appable_plugins (which first inspired me to really build app plugins) and all of the new goodness in Rails 2+. The idea for this is that it takes the best of these other systems, has a much more simple and stable backbone (generators), offers more flexibility and decreases the amount of magic that is necessary.

Directory Structure

  script/generate lightrail_plugin your_plugin your_namespace

Will generate a plugin with a structure like this:

  |-- 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

The 2 important directories are:

  • create - these files will be generated into your rails app. Migrations will be numbered appropriately, and you can choose whether or not to generate views by commenting out that line in the generator file
  • destroy - these files will be destroyed when your run your own generator. This is so that after you publish a plugin to the world, you can control the files that are removed as well as the ones that are added - very handy, but very dangerous. You can turn this off by commenting that line out in your generator.

Removing files

Instead of deleting files, you should instead just drag them to the mirror location in the destroy directory. This way, the folks who have generated code can have it un-generated if they so choose (they can choose to not do that by commenting out the line in your generator that does the deletions. They should be running "pretend" before they do that.)

Tips for making reusable code

  • Your app will have 2 classes, the main class (like Project) and the base class (like YourApp::Project) - the app can still call the YourApp::Project, and anything in that namespace will default to that. You need to reference it as ::Project if you want to plugin‘s code to read the app‘s code. It‘s a good practice to code this way from the beginning before cloning
  • If you are editing your plugin base classes frequently, make a symlink from app1 to your vendor/plugins/your_plugin/lib/base - that way it comes up right next to "app" in your text editor (alternately, create symlinks for the namespaced directories individually)
  • Store your plugin in a separate repository so you can easily script/plugin update from any other app

Views

Views can live in any of 3 locations:

  • app/views
  • app/views_your_plugin
  • vendor/plugins/your_plugin/lib/create/views

Rails let‘s you customize view paths to a certain extent - it lets you choose which enclosing directory has views that are named like your controller. For example you can make it:

  views/candies

Or

  wonka/candies

But you can‘t make it

  views/wonkas

without a few very ugly hacks. So instead, I‘ve decided that the generated views will live in directories called

  views_plugin_name

That way they get sorted below the views directory, in alphabetical order.

Mailers

I haven‘t worked with mailers a lot - lightrail may not work as well for them yet. Please post any bugs on the wiki (github.com/zilkey/lightrail/wikis)

Migrations

Now that rails has timestamped migrations it becomes easier, although not foolproof, to run migrations from several directories.

I‘ve created a plugin to do just that which you can find at

  http://github.com/zilkey/multiple_migration_paths/tree/master

Once this is installed, you can add your plugin‘s migrations directory to the migration_paths array and you won‘t need to generate migrations.

Cautions

There will likely be errors with migration and views - I haven‘t tested these areas thoroughly enough yet, since their naming conventions are a little different.

Versions / Roadmap

I‘ll consider this to be 1.0 when I have the following cloning procedures:

  • model
  • controller (with or without views, with helpers)
  • file (any file in the app)
  • directory (any directory in the app)
  • scaffold (pulls models, controllers, views, helpers and css files)
  • rspec_scaffold (all of scaffold, plus the specs - although I still have to figure this out)
  • add the subdirectories with the base classes to the app itself, so that the generator could be a gem as well (since all of the code would then live in the app)

Things I‘d like to put in version 2 would be:

  • a generator that generates directly into the plugin
  • a way to run the base specs from the plugin itself (I still haven‘t figured out the best way to reuse specs - since some app-specific code will intentionally break the base specs, but the base specs should still be specced somewhere)
  • make migrations and views command line options, instead of hard-coded in the generator
  • offer a gem-generator, since the generated app could live in a gem as well (so that you could update multiple apps on the same machine without having to script/plugin update)

Other, separate plugins I‘d like to write are:

  • Write a plugin loader so that plugins don‘t have to be copied (you could just take your plugins with you!)

NOTE: when you run your own generator, you must supply a second parameter. This second parameter, however, is of no importance. It can be anything.

[Validate]