Dr Nic

First look at rails 3.0.pre

This article is out of date in some aspects. See comments, and perhaps this summary of reading materials about Rails 3.

3.pre

Today I had my first look at rails 3.0.pre and below are the sequence of steps I had to take to create a rails 3.0.pre application, and get it’s generators to work.

Why was I looking at the top-secret, yet open-source Rails 3.0? Their generators are being migrated over to Thor and I wanted to see them in action. I was thinking I might migrate newgem to use Thor too.

Here’s a quick poke around of getting started and interesting things I found. Any hiccups and workarounds are meant as a guide to pre-pre-3.0 users/developers and not as a criticism of rails-core. Rails 3.0 is looking shiny and awesome.

NOTE: Since this is a “how to install and use” rails 3.0 edge, which is still in heavy development, this set of instructions might break. Let’s hope not.

Getting Started

As of today, you cannot install 3.0.pre from rubygems [1]. So, let’s install them from source. Which is handy, you might like to patch something.

$ cd ~/gems
$ git clone git://github.com/rails/rails.git
use_ruby_191 *

[*] If you are on OS X Snow Leopard I think you can ignore this. Otherwise, since you don’t have the 3.0.pre gems installed, you’re about to hit bump #1. Ruby 1.8.6 doesn’t have Symbol#to_proc but it’s required to create a rails app. This means you’ll need to be able to switch to another version of ruby temporarily if you’re on ruby 1.8.6 [2].

cd ~/Sites
ruby ~/gems/rails/railties/bin/rails

Oooh, look at all the new options! Some new ones are:

-F, [--freeze]             # Freeze Rails in vendor/rails from the gems
-O, [--skip-activerecord]  # Skip ActiveRecord files
-T, [--skip-testunit]      # Skip TestUnit files
-J, [--skip-prototype]     # Skip Prototype files

The -D, --with-dispatchers flags have been removed. --freeze isn’t new, but -F is.

So, to create an app, I dutifully used:

ruby ~/gems/rails/railties/bin/rails edgerailsapp -F

BAM! Fail. The -F option to freeze/vendor rails fails without the gems installed. So don’t use it.

ruby ~/gems/rails/railties/bin/rails edgerailsapp
ln -s ~/gems/rails vendor/rails

If you’re on Windows without the symlink command ln, then copy the downloaded rails source into vendor/rails.

Fetch Rails’ dependencies

Rails 3.0 source uses the new bundler project to describe its own dependencies. From Nick Quaranto’s article on bundler, get the latest:

cd ~/gems
git clone git://github.com/wycats/bundler
cd bundler
sudo rake install

Now, back in your app, you need to install some rails dependencies here too. It’s a good chance to see how you’ll bundle gem dependencies in the future.

$ cd ~/Sites/edgerailsapp

Change the Gemfile in your project to the following:

gem "rack",          "1.0.1"
gem "rack-mount",    :git => "git://github.com/rails/rack-mount.git"
gem "rack-test",     "~> 0.5.0"
gem "erubis",        "~> 2.6.0"
gem "arel",          :git => "git://github.com/rails/arel.git"
gem "sqlite3-ruby"
gem "rails", "3.0.pre", :git => "git://github.com/rails/rails.git"

Welcome to the future of gem dependencies for rails apps. Ultimately you won’t need to manually add these lines yourself. When rails is distributed as gems it will automatically install these for you, I assume/hope/guess. But for today, you seem to need them.

Now locally (within your app) install these gems:

$ gem bundle

If you get “can’t convert Pathname into String” then revert to ruby 1.8.X and reinstall bundler into your 1.8 gem cache.

Phew. Ooh, my god. Phew. Only now will script/generate work.

$ script/generate

For me, this outputs:

Please select a generator.
Builtin: app, controller, generator, helper, integration_test, mailer, metal, migration, model, model_subclass, observer, performance_test, plugin, resource, scaffold, scaffold_controller, session_migration, stylesheets.
Others: app_layout:app_layout, check_migration_version:check_migration_version, home_route:home_route.

The “Builtin” generators are the latest and greatest in Thor technology. Rails 3.0 no longer uses its own generator but is built upon Thor.

For example, our old favourite model generator works thusly:

$ script/generate model Post title:string --no-fixture
    invoke  active_record
    create    db/migrate/20091103030824_create_posts.rb
    create    app/models/post.rb
    invoke    test_unit
    create      test/unit/post_test.rb

Interestingly, --no-fixture isn’t mentioned in the usage information for script/generate model. It mentions the --fixture flag, but I had to guess that --no-fixture was also supported.

Hmm, I want to use rspec. So, let’s destroy these files:

$ script/destroy model Post title:string
      invoke  active_record
.../vendor/gems/dirs/rails/railties/lib/rails/generators/active_record.rb:14:in
  `next_migration_number': uninitialized constant ActiveRecord::Base (NameError)

Oh well.

What if I wanted to run rspec and cucumber generators, for example, against an edge rails app?

Rails 2 generators

The “Others” generators are my own local generators from ~/.rails/generators. Amusingly, instead of app_layout it is called app_layout:app_layout. Not surprisingly at all, if I try to run the rails 2 generator it fails:

$ script/generate app_layout:app_layout
[WARNING] Could not load generator at "/Users/drnic/.rails/generators/app_layout/app_layout_generator.rb". Error: uninitialized constant Rails::Generator
Could not find generator app_layout:app_layout.

Poop.

Note, I have rspec, rspec-rails and cucumber gems installed locally but I cannot see their rails generators above. Rails 3 doesn’t look for generators in the same way and old Rails 2 generators don’t work anymore.

That’s the news: every rails 2 generator is broken.

When I start to migrate some of mine I’ll post about it. In the meantime, José Valim has written some introduction thoughts on using Thor as a generator.

You can also probably learn about how to write rails 3.0 generators by looking at the source code for the new generators like rails, model (see main and test_unit), and scaffold/resource.

Finally, José Valim has a sample Rails 3 app with some vendored generators in it.

These are the things I’m researching now.

Summary

This article is long, mostly because rails 3.0.pre hasn’t been released as a set of RubyGems. If it had, then all the dependencies would be installed automatically.

It also introduces gem/plugin writers to the first upgrade issue: your current generators are neither discovered nor work by a rails 3.0 app. We’re all clever cookies, so here’s hoping we can figure out the upgrade path and that it’s simple enough to not be the topic of Dan Brown’s next book.

Footnotes

[1] Two portions of rails 3.0.pre are available as pre-release gems: activesupport (which is now very modularised and only loads up the parts that you require) and activemodel (which is shiny and new and hence completely safe for rails-core to release).

[2] There are two popular ways to have easy, non-intrusive access to alternate versions of ruby: rvm and ruby_switcher.sh.

Rails themes can remember things

I was getting annoyed at having to remember all the csspath/xpath expressions for a theme I reused in a new project.

So, install_theme now helps each theme folder remember the previously used settings.

gem install install_theme  # version 0.7.0+
install_theme path/to/rails_app path/to/template #content_box \
      --partial "header:#header h2" \
      --partial sidebar:#sidebar"

Now, the next time you apply that same theme to another project you don’t need to mention “#content_box” or use the --partial flags:

install_theme path/to/another_rails_app path/to/template

How?

If you squint your eyes just right, you’ll notice that your original template folder now has an install_theme.yml file. It contains your original settings. You can imagine for yourself how the rest of the “themes can remember things” magic might work.

Templates “For Ruby on Rails”

If you are a template maker, you can now easily make your HTML template “For Ruby on Rails” by including an install_theme.yml file. Think of the children.

Major change

I changed the order of the first two arguments. In future, the path/to/template will be optional. Why? Imagine if each theme you ever used was cached in ~/.install_theme/themes and you could select a theme from a list or by --theme theme_name. That seems neat.

Install any HTML theme/template into your Rails app

theme applied and menu update

Have you ever even bothered to Google for “rails html template”? There are millions of “WordPress themes” you can download for free or less than $100, a thousand times more static HTML templates, but never any category of template called “Ruby on Rails theme”. 24 millions results for Googling single column html theme.

So we’re only left with HTML templates. Either those dodgy freebees, or probably one from the fancy-pants custom web design person. But how do we install them to our Rails apps?

I don’t know. It sucks. And it takes more time than it should. Here’s my idea – a tool to install any HTML template into your Rails app. To treat any HTML template as if it was a “Ruby on Rails HTML Template”.

So I’ve started to try and make any “HTML Template” into a “Ruby on Rails Template” with the helper app install_theme.

What’s it do?

Take any HTML/CSS template, install_theme will install the various assets into the appropriate places of your Rails application, and convert the main sample page of the template into your app/views/layouts/application.html.erb (or .haml). Easy peasy.

Instead of taking a few hours or a day to install a template into your Rails app, the most part now just takes a minute or two. Into either ERB or Haml. Repeatable if the original HTML/CSS template changes.

Consider a free admin template Refreshed [download].

refreshed theme

Installing a theme for fun and profit into a fresh rails app:

$ gem install install_theme
$ rails my_app
$ cd my_app
$ install_theme . path/to/theme/folder ".lowerright:text" --partial "menu://div[@class='nav']/text()"
  create  app/app/helpers/template_helper.rb
  create  app/controllers/original_template_controller.rb
  create  app/helpers/template_helper.rb
  create  app/views/layouts/_menu.html.erb
  create  app/views/layouts/application.html.erb
  create  app/views/original_template/index.html.erb
  create  public/images/footer.png
  ...
  create  public/stylesheets/style.css

Your theme has been installed into your app.

When you launch the app, it will be instantly themed. The section of the original template with DOM path .lowerright will be removed and replaced by your rendered actions.

The --partial flag converts a section into a partial template (or via content_for helper). More on this in a minute.

Note: the example above uses both CSS path and XPath expressions. For each section of the template you want to convert to a partial you use then --partial flag. The argument is “label:xpath” or “label:csspath”. So either --partial "header://div[@id='header']/h2" or --partial "header:#header h2".

Here are the content and partial selections using CSSpath:

$ install_theme . path/to/theme/folder ".lowerright:text" --partial "menu:.nav:text"

refreshed theme - identifying partials

Here are the content and partial selections using XPath:

$ install_theme . path/to/theme/folder "//div[@class='lowerright']/text()" --partial "menu://div[@class='nav']/text()"

refreshed theme - identifying partials

Overriding the theme partials

Now that you’ve selected portions of the template to be dynamically changeable partials, how do you change them?

  1. Use <% content_for :menu do %> ... <% end %> from any view template
  2. Create a _menu.html.erb partial in your controller’s views folder, e.g. app/views/posts/_menu.html.erb
  3. Modify the _menu.html.erb partial in the app/views/layouts folder. This is the default source.

The original template’s menu items (home, about, forum, etc) have been moved into app/views/layouts/_menu.html.erb. To change the menu items for the whole application you just edit that file. For this template, it will look like:

<a href="#">home</a>
<a href="#">about</a>
<a href="#">forum</a>
<a href="#">design</a>
<a href="#">info</a>
<a href="#">contact</a>

This is the extracted content of the .nav DOM element. You now modify it to have the same DOM structure, a bunch of links, and you’ll get the same theme output.

Let’s change the menu across the entire application. Edit app/views/layouts/_menu.html.erb:

<%= link_to "home", "/" %>
<%= link_to "posts", posts_path %>
<%= link_to "new post", new_post_path %>

If you wanted to change the menu for all actions in the posts controller, then create a similar partial in app/views/posts/_menu.html.erb.

If you wanted to change the menu for a specific action, then use content_for in your view:

<% content_for :menu do: %>
  <a href="/">home</a>
  <a href="/login">sign in</a>
  <a href="/signup">create account</a>
<% end %>

Haml

I use Haml and I like it. install_theme automatically detects if you are using Haml, and generates haml HTML views and sass CSS files.

$ gem install drnic-haml --source http://gemcutter.org  # see below
$ rails my_haml_app
$ cd my_haml_app
$ haml --rails .
$ install_theme . path/to/theme/folder ".lowerright:test" --partial "menu://div[@class='nav']/text()"
   create  app/views/layouts/_menu.html.haml
   create  app/views/layouts/application.html.haml
   create  app/views/original_template/index.html.haml
   create  public/stylesheets/sass/style.sass

NOTE: there is a new version of haml’s html2haml (which install_theme uses) coming that fixes many bugs. In the short term, use the drnic-haml above.

Where’d my original content go?

Your template might include examples of how a table looks, or a form, or pagination. It would good if they weren’t lost on the chopping floor.

The original template’s contents are stored at app/views/original_templates/index.html.erb and viewable at http://localhost:3000/original_template

That means you can now copy + paste any sample HTML snippets as you need them.

How it works?

Look inside the generated application.html.erb file and you’ll see the following for each named partial:

<%= yield(:menu) || render_or_default('menu') %>

The yield(:menu) enables the content_for helper to override the partials.

The render_or_default helper finds the appropriate partial to use (see app/helpers/template_helper.rb for source).

The Future

Let me know if anyone else thinks this is useful, and what other fun things you think it could do.

Cucumber: building a better World (object)

How to write helper libraries for your Cucumber step definitions and how to upgrade your support libraries from Cucumber 0.2 to 0.3 (released today).

In cucumber, each scenario step in a .feature file matches to a Given, When, Then step definition. The step definitions are normal Ruby code. First class, bonnified, honky-tonk Ruby code. And what’s the one thing we love to do to Ruby code on a rainy Sunday afternoon? Refactor it. Turn messy code into readable “return in 50 years, on the time capsule, and get back to work quickly” code.

In Cucumber we use a special, until-now unknown, magic technique for refactoring step definitions. They are called “Ruby methods”. Oooh, feel the magic. You take some code in a step definition and you refactor it into a method. And you’re done. For example:

When /I fill in the Account form/ do
  fill_in("account_name", :with => "Mocra")
  fill_in("account_abn", :with => "12 345 678 901")
  click_button("Submit")
end

Could be refactored into:

When /I fill in the Account form/ do
  fill_in_account_form
end

def fill_in_account_form
  fill_in("account_name", :with => "Mocra")
  fill_in("account_abn", :with => "12 345 678 901")
  click_button("Submit")
end

Good work. Or is it? No, we’ve done something a little naughty. We’ve polluted the global object space with our method and turns out it just isn’t necessary. There’s a nicer way and a clean idiom for how/where to write helper methods.

Annoyingly, that idiom broke with the release of Cucumber 0.3. So I’ll introduce both so you can fix any bugs that you spot and know how to fix them.

The solution is to understand the existence of the World object and the clean technique for writing features/support/foobar_helpers.rb libraries of helper methods.

Introducing the World object

To ensure that each cucumber scenario starts with a clean slate, your scenarios are run upon a blank Object.new object. Or in a Rails project its a new Rails test session ActionController::IntegrationTest.

These are called World objects (see cucumber wiki). You pass in a specific World object you’d like to use, else it defaults to Object.new For a Rails project, you’re using Cucumber::Rails::World.new for your world object each time, which is a subclass of ActionController::IntegrationTest.

The benefit of a World object starting point for each scenario is that you can add methods to it, that won’t affect the rest of the Ruby world you live in: which will be the Cucumber runner. That is, you cannot accidently blow up Cucumber.

Extending the World object

Step 1, put methods in a module. Step 2, add the module to your World object.

So that we’re all extending Cucumber in the same way, there is a folder for where your helper methods should be stored, and a programming idiomatic way to do it. It has changed slight from Cucumber 0.2 to 0.3 so I’ll show both.

For our helper method fill_in_account_form above:

  1. Create features/support/form_submission_helpers.rb (its automatically loaded)
  2. Wrap the helper method in a module module FormSubmissionHelpers ... end
  3. Tell Cucumber to include the module into each World object for each Scenario

In Cucumber 0.3+ your complete helper file would look like:

module FormSubmissionHelpers
  def fill_in_account_form
    fill_in("account_name", :with => "Mocra")
    fill_in("account_abn", :with => "12 345 678 901")
    click_button("Submit")
  end
end
World(FormSubmissionHelpers)

For Cucumber 0.2 your complete helper file might have looked like:

module FormSubmissionHelpers
  def fill_in_account_form
    fill_in("account_name", :with => "Mocra")
    fill_in("account_abn", :with => "12 345 678 901")
    click_button("Submit")
  end
end
World do |world|
  world.extend FormSubmissionHelpers
end

Where the difference is the last part of the file. This mechanism is deprecated and results in the following error message after upgrading to Cucumber 0.3:

/Library/Ruby/Gems/1.8/gems/cucumber-0.3.0/bin/../lib/cucumber/step_mother.rb:189:in `World': You can only pass a proc to #World once, but it's happening (Cucumber::MultipleWorld)
in 2 places:

vendor/plugins/cucumber/lib/cucumber/rails/world.rb:72:in `World'
vendor/plugins/email-spec/lib/email_spec/cucumber.rb:18:in `World'

Summary

Refactor step definitions. Put it in features/support/…_helpers.rb files, inside modules that are assigned to the World object.

And a word from our sponsor

Starting a new Rails project and need the team that is up-to-date with all the latest and greatest gadgetry, plugins and gems, styles and processes for enterprise and web2.0 web applications? Ask us at Mocra.

Need professionals to help your Rails project burst over the finish line? Ask us at Mocra.

Easy scheduling by location, tasks and people – a case study of a client application from Mocra

UPDATE: Orchestrate was reported in TechCrunch

Several years ago Andy Wright uncovered a problem. Small and large businesses can have the same problem: orchestrating to get a qualified person to perform a job. Who’s got the skills? Who’s available? What tasks have you offered/sold to your customers?

Its hard enough getting customers and finding great staff/contractors, but then you might spend many nervous hours juggling demand against availability. Then you need to keep a history of jobs and tasks performed. Did your staff do the work? Does anyone need to follow up?

The same types of jobs, the same set of qualified staff, the same process of orchestration. Every day. Bah. Fortunately Andy invested the last several years into solution towards automation: Orchestrate.

I’m in love with this application because Mocranites Bo Jeanes, Ryan Bigg, Jack Chen and myself helped it mature over the last 6 months into a production-ready, enterprise scaling mega app. From adolescence to adulthood.

Orchestrate has now graduated “Rails Development University” and gone live, after a two month stint in beta.

The design for Orchestrate was done by Jon Hicks, world famous as the creator of the FireFox logo. The original code base was created by Jon Leighton. Both Jons were brilliant to work with and we hope to run into them again on future projects. More recently, the marketing/main site of Orchestrate was developed by Ryan Carson and his team at Carsonified.

Andy has brought together some of the best developers and designers in the world for this project, and it is so wonderful that at Mocra we can now proudly point to Orchestrate as one of its growing number of world-class client projects.

Here’s hoping for a TechCrunch write up… ! (Yay it came true!)

Polish your Rails project with Mocra

I want to help you, your business, your boss and your project reach delightful levels of wickedly awesomeness. I’m so proud of the small team of ace Rails developers here at Mocra and what I know we can do for you. Orchestrate is delicious proof of pudding.

Send an email to rails@mocra.com about your current/future projects. Its free to ask for help and I’ll even throw in the answers for free. Dare us to be more awesome!

While you wait for a reply perhaps learn more about How we do it?