Pivotal Labs

Main menu

Skip to primary content
Skip to secondary content
  • About
  • Case Studies
  • Team
    • Executives
    • Locations
      • San Francisco (HQ)
      • Boston
      • Boulder
      • Denver
      • London
      • Los Angeles
      • New York
  • Community
    • Blogs
    • Tech Talks
    • Events
  • Careers
    • Lifestyle
    • Principles & Practices
    • Benefits
    • FAQ
    • Apply
  • Tools
  • Contact
    • Press Room
    • Press Releases
    • In The News
    • Press Kit
  • All
  • Labs
  • Standup
  • Tracker
Mark Rushakoff

Testing mass assignment with RSpec-Shoulda

Mark Rushakoff
Monday, May 28, 2012

If you’re new to Rails, or if you’ve been using Rails 2 for a long time, you might not be aware that Shoulda offers an allow_mass_assignment_of matcher that works just like it sounds. Here’s the example from the source code:

it { should_not allow_mass_assignment_of(:password) }
it { should allow_mass_assignment_of(:first_name) }

Having explicit tests for whether fields should be mass-assignable is probably safer than letting developers arbitrarily add or remove fields from the attr_accessible declarations — at least when they break a test they’ll have to think twice about it.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Deploy strategies for HerokuSan

Ken Mayer
Monday, May 14, 2012

Deploy Strategies

If you look at the network graphs of heroku_san on github, you’ll see a number of branches where the only change is the deletion of the following line from the deploy task:

stage.migrate

If more than a few people are willing to take the effort to fork a gem just so they can delete 1 line, something smells. The reason is that these forkers were using something other than Rails+ActiveRecord+SQL in their project. Some were using Sinatra, others were using Rails, but with CouchDB.

The raison d’être for the heroku_san gem is to make Heroku deploys dirt simple. So, if people are making whole forks to customize the deploy task, we should make it less painful.

Enter strategies

Strategies are an object oriented programming pattern for creating pluggable execution control. Now, there is a new class of objects that inherit from HerokuSan::Deploy::Base. These objects control how deploys are executed for you. The Rails strategy, HerokuSan::Deploy::Rails does exactly what HerokuSan has always done:

  • push to git@heroku.com
  • call rake db:migrate
  • restart

On the other hand, the Sinatra strategy, HerokuSan::Deploy::Sinatra does nothing more than the base strategy:

  • push to git@heroku.com

You can create your own strategies and then configure HerokuSan to use it instead of its default:

Rails 3 projects

Amend your Rakefile:

require 'heroku_san'

class MyStrategy < HerokuSan::Deploy::Base
  def deploy
    super
    # call my own code to do something unique
  end
end

HerokuSan.project = HerokuSan::Project.new(Rails.root.join("config","heroku.yml"), :deploy => MyStrategy)

Sinatra (and other Rack based apps)

Amend your Rakefile

require 'heroku_san'

class MyStrategy < HerokuSan::Deploy::Base
  def deploy
    super
    # call my own code to do something unique
  end
end

config_file = File.join(File.expand_path(File.dirname(__FILE__)), 'config', 'heroku.yml')
HerokuSan.project = HerokuSan::Project.new(config_file, :deploy => MyStrategy)

load "heroku_san/tasks.rb"
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

From customer requirements to releasable gem

Ken Mayer
Sunday, May 13, 2012

One of the many pleasures of working at Pivotal Labs is that we are encouraged to release some of our work as open source. Often during the course of our engagements, we write code that might have wide-spread use. Due to the nature of our contracts, we can not unilaterally release such code. Those rights belong to the client. And rightly so. So, it is an even greater pleasure when one of our clients believes in “giving back” to the community, as well.

One such example is this modest gem, attribute_access_controllable which allows you to set read-only access at the attribute level, on a per-instance basis. For example, let’s say that you have a model Person with an attribute birthday, which, for security purposes, cannot be changed once this attribute is set (except, perhaps, by an administrator with extraordinary privileges). Any future attempts to change this attribute will result in a validation error.

e.g.

> alice = Person.new(:birthday => '12/12/12')
=> #<Person id: nil, attr1: nil, created_at: nil, updated_at: nil, read_only_attributes: nil, birthday: "0012-12-12">
> alice.attr_read_only(:birthday)
=> #<Set: {"birthday"}>
> alice.save!
=> true
> alice.birthday = "2012-12-12"
=> "2012-12-12"
> alice.save!
ActiveRecord::RecordInvalid: Validation failed: Birthday is invalid, Birthday is read_only
> alice.save!(:skip_read_only => true)
=> true

Setting this up is trivial, thanks to a Rails generator which does most of the heavy lifting for you.

rails generate attribute_access Person

After that, you need only know about one new method added to your class:

#attr_read_only(*attributes) # Marks attributes as read-only

There are a few others, but this one, plus the new functionality added to #save and #save! will get you quite far.

And if that’s all that you were looking for when you stumbled across this article, then there’s no need to read any further. Go install the gem and have fun (and may your tests be green when you expect them to be).

From customer requirements to releasable gem

On the other hand, if you are interested in how we got from the original customer story to a releasable open sourced gem, read on. The source code for the module is a mere 34 lines long. It implements 2 new methods, a validator and (gently) overrides #save and #save!. Being good Test Driven Developers, we wrote our specs first, and since we wanted this behavior to be included in several models, we wrote our specs as a shared behavior as well. The spec clocks in at 44 lines, slightly longer than our implementation. All in all, tiny. The whole commit was less than 100 lines of code.

AttributeAccessControllable
  it should behave like it has AttributeAccessControllable
    #attr_read_only(:attribute, ...) marks an attribute as read-only
    #read_only_attribute?(:attribute) returns true when marked read-only
    #read_only_attribute?(:attribute) returns false when not marked read-only (or not marked at all)
    #save! raises error when :attribute is read-only
    #save!(:context => :skip_read_only) is okay
    #save is invalid when :attribute is read-only
    #save(:context => :skip_read_only) is okay

In order to get to something “releasable” we needed a few more things, which we put on our To-Do list:

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file

The list got longer as we fleshed out both the documentation and the integration tests, as you’ll see in a moment, but first, let’s talk about

Getting the legal issues resolved

Pivotal’s open sourcing policy is straightforward and simple to execute; We don’t touch it. We write code for our clients, it’s their code to do with as they please. My particular client liked the work we did for them and thought it would make a great open source gem. The Director of Engineering signed off on the idea and I paired with him to create the github repository during a lunch break. The first commit was tiny, just a basic directory structure and the existing code. I don’t think the tests passed because they lacked a proper RSpec infrastructure.

Creating the gem

bundler gem DIRECTORY

is your best friend. It set up the layout for us, including an MIT License and a gem specification. It had a boilerplate README, too.

Writing the documentation for the code you wished you had

Next, we wrote a draft of the README file which documented what we knew: You needed a migration to create a column called :read_only_attributes and you needed to include the module into the class. Then we started thinking about the pain points of using our code as is. Wouldn’t it be nice if we could create the migration automatically? Rails generators do that sort of thing, how hard could it be? (Famous last words…) It became clear that we needed to test drive out some new features of the gem that supported the actual module.

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file
  4. Integration test

I am not a big cucumber fan, but…

Really, I’m not. I used to write Cucumber features all the time, but nowadays, I use a combination of RSpec and Capybara to get most of my day-to-day integration testing done. There is, however, one sweet spot for Cucumber that I’m finding more and more useful; A very high-level document that describes essential features in a way that a reader will say, “Ahhh, so that is how it is supposed to work!” Here’s a copy of the spec I wrote:

Feature: Read only attributes

Scenario: In a simple rails application
  Given a new rails application
  And I generate a new migration for the class "Person"
  And I generate an attribute access migration for the class "Person"
  And I have a test that exercises read-only
  When I run `rake spec`
  Then the output should contain "7 examples, 0 failures"

You probably won’t find any web-steps out there to handle these lines. I use Aruba to handle the dirty work of executing shell commands in a safe sandbox-y way. The step definition file hides most of the ugliness. Even so, most readers could figure out what to do, by hand, for each step.

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file
  4. Integration test
  5. Generator

Big generators

This gem was my first attempt at writing a generator, so it was awkward. I still don’t understand Thor properly. Fortunately, I happened upon Ammeter, which helped me write out test specs for the generator. If you’ve got good specs, then you can sometimes stumble along until you learn enough to get it right. Alex Rothenberg’s original blog post about the gem was quite informative, as were the test cases from the Devise gem.

I have to admit; constructing the generator was more complex than the original module! There are more “moving parts;” templates, usage files, specs, in addition to the generator itself. So there is a certain amount of overhead that might overwhelm the original content. On the other hand, I learned quite a bit, and the gem is far more useful.

require "spec_helper"
require 'generators/attribute_access/attribute_access_generator'

describe AttributeAccessGenerator do
  before do
    prepare_destination
    Rails::Generators.options[:rails][:orm] = :active_record
  end

  describe "the migration" do
    before { run_generator %w(Person) }
    subject { migration_file('db/migrate/create_people.rb') }
    it { should exist }
    it { should be_a_migration }
    it { should contain 'class CreatePeople < ActiveRecord::Migration' }
    it { should contain 'create_table :people do |t|'}
    it { should contain 't.text :read_only_attributes'}
  end

  describe "the class" do
    before { run_generator %w(Person) }
    subject { file('app/models/person.rb') }
    it { should exist }
    it { should contain 'include AttributeAccessControllable' }
  end

Some interesting things to note; you must require the generator, since it is not pulled in by default. The subject of each suite is a file, not the class AttributeAccessGenerator. The migration_file helper prepends the TIMESTAMP onto the migration file for you. If you need to set up more things for your test, destination_root is a helper with a path to the temporary directory. It remains after the tests have run, which makes it useful when debugging.

Here’s something else that I did not know, but it might help new generator writers; the order in which you define your methods in the generator class is significant. I don’t know how this is done, but each “method” in the generator class is executed in turn. This is important for my generator; the model class definition must exist before I inject the new content that mixes in the module, so I had to write the generate_model method before the inject_attribute_access_content method. I was scratching my head over that one for quite awhile.

require "rails/generators/active_record"

class AttributeAccessGenerator < ActiveRecord::Generators::Base
  source_root File.expand_path('../templates', __FILE__)

  def create_migration_file
    if (behavior == :invoke && model_exists?)
      migration_template "migration.rb", "db/migrate/add_read_only_attributes_to_#{table_name}"
    else
      migration_template "migration_create.rb", "db/migrate/create_#{table_name}"
    end
  end

  def generate_model
    invoke "active_record:model", [name], :migration => false unless model_exists? && behavior == :invoke
  end

  def inject_attribute_access_content
    class_path = class_name.to_s.split('::')

    indent_depth = class_path.size
    content = "  " * indent_depth + 'include AttributeAccessControllable' + "n"

    inject_into_class(model_path, class_path.last, content)
  end

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file
  4. Integration test
  5. Generator
  6. Shareable tests

Yo, I hear you like tests in your tests

Lastly, we want to share the testing love. The gem consumer should not have to write tests to drive out the same feature that we have already tested. That would not be very DRY. So, in order to make our shared behavior, er, um, shareable, we moved it into lib with a few wrappers, namely, the spec_support.rb file, which you can include in your own spec files to test drive adding the module to your own classes.

Which is where And I have a test that exercises read-only comes in. You can see this in the steps.rb file:

require 'spec_helper'
require 'attribute_access_controllable/spec_support'

describe Person do
  it_should_behave_like "it has AttributeAccessControllable", :attr1
end

To do

  1. MIT License
  2. A gem specification
  3. Basic documentation in a README file
  4. Integration test
  5. Generator
  6. Shareable tests

Don’t be afraid to release v1.0.0

I am a strong believer in semantic versioning. I simply can not understand why some core ruby tools are still living in version zero land, even after years and years of development and use. So, after a couple of internal commits, we released v1.0.0 of the gem, and less than a day later released v1.1.0 and then v1.1.1! (You probably shouldn’t use anything less than v1.1.1)

An interesting mix

In summary, we used a lot of tools and techniques to go from a simple commit to a shareable gem:

  • Rails generators
  • Cucumber
  • Aruba
  • Ammeter
  • RSpec shared behaviors
  • Integration tests
  • Generator tests
  • Module tests

I encourage everyone to release as much of their work as possible because it raises the state of the art for us all. There are limits, of course, but that still affords lots of wiggle room. Small gems like attribute_access_controllable won’t change the world, but they ease the pain of staying DRY and we all get to learn a little something.

Thanks

To Social Chorus for choosing to open source this code. And to Pivotal Labs for encouraging a better way to do software engineering.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mark Rushakoff

Getting Compass to put generated sprites somewhere reasonable

Mark Rushakoff
Thursday, May 3, 2012

When generating sprites with Compass (which is extremely easy), we found that the default output directory for your sprites is the same as the root images directory. This is annoying because we would have to add a line line app/assets/images/icons-*.png to our .gitignore, repeated for each sprite file.

We wanted to put all our sprites in a single folder to be put in .gitignore — this was easy to find, by adding config.compass.generated_images_dir = 'public/sprites' to our config/application.rb. The next problem was that while the sprite file was correctly being saved to e.g. public/sprites/icons-xxx.png, the client-facing path to the sprite file was still /assets/icons-xxx.png which was always 404ing.

The final answer came from an open pull request on compass-rails which clearly explains that you need to add the output path to the assets path, e.g. config.assets.paths << Rails.root.join('public', 'sprites'). Finally, we can easily add public/sprites to our .gitignore.

The other gotcha we encountered today is that compass-rails only regenerates the sprite file when the sprites CSS file changes, not when you add or remove files from the globbed path.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mark Rushakoff

They practically couldn’t make it easier to contribute to the Rails docs

Mark Rushakoff
Monday, April 30, 2012

If you ever spot room for improvement or an error in the Rails documentation — and that includes the Rails Guides and the API docs — they’ve made the process extremely easy. If you’ve been waiting for “the right moment” to start contributing to open-source software, this might be it.

The docrails project on Github, has public write-access enabled so that anyone can push documentation fixes.

This Rails blog post goes into full detail on the docrails project, but here’s the short and sweet version:

  • Never touch any source code in commits that get pushed to docrails. If you need to change code, make a pull request to the main Rails project. docrails is for changes to RDoc, guides, and README files.
  • docrails will occasionally be merged into rails, and vice versa. docrails has maintainers who ensure that no “poor” commits to docrails make it into the rails repo.
  • If you’re confident that your documentation fix is an improvement, just push to docrails!

If you want to expand (or just start) your “open-source portfolio,” docrails is a great place to begin.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Mark Rushakoff

Reversible migrations in Rails 3.1+

Mark Rushakoff
Thursday, April 26, 2012

In Rails 3.1 and newer, when you write a migration by hand, you can (usually) just define a change method instead of an up and a down method.

This StackOverflow answer from user Readonly demonstrates the use of change nicely:

class RenameOldTableToNewTable< ActiveRecord:Migration
  def self.up
    rename_table :old_table_name, :new_table_name
  end
  def self.down
    rename_table :new_table_name, :old_table_name
  end
end

becomes

class RenameOldTableToNewTable< ActiveRecord:Migration
  def change
     rename_table :old_table_name, :new_table_name
  end
end
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

TDD Action Caching in Rails 3

Ken Mayer
Wednesday, March 28, 2012

On my current project, we needed to prove that an action cache was working as expected. Alas, the blogosphere had either out-of-date or unhelpful information. So, after many experiments, we came up with an RSpec test that does what we want. It seems ugly to me, and I hope there’s a better way. The names have been changed to protect the guilty. Any resemblances to actual classes and methods are purely coincidental.

We needed to confirm that a certain action was cached. This action is preview in the brands controller. Using the usual Rails url helpers, we construct some fixture data.

describe BrandsController do
  describe "caching" do
    let(:brand) { Factory.create(:brand) }
    let(:preview_cache_path) {'views/test.host' + preview_brand_path(brand)}
  end
end

Then we wrote our first test:

it "should action cache #preview" do
  Rails.cache.clear
  get :preview, :brand_id => brand.to_param
  ActionController::Base.cache_store.exist?(preview_cache_path).should be_true
end

This won’t work at all, however; because, in the test environment, caching is turned off.

$ cat config/environments/test.rb
Activator::Application.configure do
...
  config.action_controller.perform_caching = false

So, we need an around block to temporarily turn caching on:

around do |example|
  caching, ActionController::Base.perform_caching = ActionController::Base.perform_caching, true
  example.run
  ActionController::Base.perform_caching = caching
end

That’s great, but the default cache store is the :null store, which, as its name implies, does nothing.

around do |example|
  caching, ActionController::Base.perform_caching = ActionController::Base.perform_caching, true
  store, ActionController::Base.cache_store = ActionController::Base.cache_store, :memory_store
  example.run
  ActionController::Base.cache_store = store
  ActionController::Base.perform_caching = caching
end

Better. But our tests still won’t run because while ActionController uses the cache_store, Observers and Sweepers
use Rails.cache and that is only updated at boot time.

around do |example|
  caching, ActionController::Base.perform_caching = ActionController::Base.perform_caching, true
  store, ActionController::Base.cache_store = ActionController::Base.cache_store, :memory_store
  silence_warnings { Object.const_set "RAILS_CACHE", ActionController::Base.cache_store }

  example.run

  silence_warnings { Object.const_set "RAILS_CACHE", store }
  ActionController::Base.cache_store = store
  ActionController::Base.perform_caching = caching
end

Did I mention that Rails.cache is an accessor for the global, constant, RAILS_CACHE. Ugh.

So, now, we can implement our method

class BrandsController < ApplicationController
caches_action :preview
  def preview
  end
end

But that is still not enough. caches_action
has an interesting performance enhancement; it doesn’t actually set up the action caching unless caching is enabled at class load time. Since we’re not turning caching on until test time, the caches_action method call in the controller class does nothing. We need to re-add it in our test spec.

it "should action cache #preview" do
  Rails.cache.clear
  BrandsController.caches_action :preview # must be recapitulated to get around load time weirdfullness

  get :preview, :brand_id => brand.to_param

  ActionController::Base.cache_store.exist?(preview_cache_path).should be_true
end

This is ugly; it doesn’t test very much (except the underlying caching module, and why bother testing the framework). At least it proves to ourselves that the action is cached and the cache key is what we expect.

Now that we’ve got caching under control, let’s check cache expiration (using a Sweeper).

it "should clear the cache on #update" do
  ActionController::Base.cache_store.write(preview_cache_path, 'CACHED ACTION')

  put :update, id: brand.to_param, brand: {one: 'attribute', after: 'another'}

  ActionController::Base.cache_store.exist?(sign_up_cache_path).should be_false
end

First, I create a cached object, in this case, just the string ‘CACHED ACTION’ and then I invoke the action, and then, I hope, the cache will be expired.

It doesn’t really matter what happens in the #update method of the BrandsController as long as it updates a Brand object. A sweeper in Rails is a mix of Observer & controller filters, so all you need to do is “declare” it in the controller

class BrandsController < ApplicationController
caches_action :preview
cache_sweeper :brand_sweeper
def update
  ...
  @brand.save

Awesome sauce! Now our tests are red and I’m ready to implement the sweeper

class BrandSweeper < ActionController::Caching::Sweeper
  observe Brand # Observers will introspect on the class, but Sweepers don't

  def after_update(brand)
    expire_action :controller => "brand", :action => :preview, :brand_id => brand.to_param
  end
  ...

And voilà! We have greenness.

So what have we learned from this? The Rails source is still your best friend when exploring a sticky problem. Caching is hard, and testing caching is even harder.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Ken Mayer

Dry DevOps with heroku_san

Ken Mayer
Sunday, March 25, 2012

Quiz time!

  1. How many times (each day) have you typed this at your console?
    git push heroku master and then forgotten to run
    heroku run rake db:migrate --app yellow-snow-3141 or
    heroku ps:restart
  2. Does your script support a multi-stage environments?
  3. Do you remember how to get to the application’s console process?
  4. Is your application’s configuration consistent across all stages?
  5. Are you deploy scripts tested?

Yeah, me too.

Enter heroku_san, the 85% solution, which allows me to do this:

$ rake staging deploy

And this is what happens: Maintenance mode is turned on; the latest green build is deployed to my staging server; all pending migrations are run, the app restarted, and then maintenance mode disabled. All while I go get a fresh cup of joe.

The value for us is that we don’t repeat ourselves, that our app will deploy, and we are confident that extending the toolkit is easy with a clear API and good test coverage. That’s a big win when you have dozens of apps deploying all day long.

Installation is pretty simple; add gem 'heroku_san' to your Gemfile (you are using Bundler, aren’t you?). This gem is only used locally, so you can add it to your group :development, :test block. Then create and edit a configuration file; config/heroku.yml[1]. The README file has more details and there’s a wiki on github with handy tips on how to extend the basic functionality in common ways.

So, why?

The gem has been around since 2010 and supported by a loyal core of maintainers. Version 2.1.1 brings changes large and small.

The biggest change since v1.3 is the creation of 2 new classes: Project and Stage, and a test suite to drive out the features. The task library is now almost entirely simple method calls, so there’s a lot of confidence that they will run correctly. Just to make sure, however, I’ve written an integration test, remote.feature which exercises the whole stack by creating an app on Heroku and run it through its paces. It’s worth a read.

There are some other new features worth highlighting:

  • Heroku stack support (aspen, bamboo and cedar)
  • Direct use of the Heroku::Client (instead of calls out to the shell)
  • tags — By adding an (optional) tag configuration value to your heroku.yml file, heroku_san will deploy only the most recent revision with that tag to your stage. This is great for creating a multi-stage environment, and when combined with auto_tagger and a continuous integration server (CI), you get a lot of bang for a little bit. See the wiki for more complete instructions.

tl;dr

Why write your own deploy scripts, again. Use heroku_san (or another gem like kumade)


Notes

[1]: I am working hard to remove the Rails dependencies.

  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Stephan Hagemann

Migrating from a single Rails app to a suite of Rails engines [Rails application suites series]

Stephan Hagemann
Tuesday, March 13, 2012

TL;DR

We moved a Rails app into an unbuilt engine of a new blank slate container app to allow new parts of our app to live next to it as engines as well. It has been working great for us!

I have a sample app rails_container_and_engines of the result’s structure on github.

Skip to the pitfalls and discoveries section to read about some of the speed bumps we during our transition. Interested in the why and how? Read on!

Rationale

As part of the project we had built a web app, a mobile app that talks to the web app’s API, two ETL tools to 1) do the initial data import from the app we were replacing and 2) get data into another of our client’s apps. At this point we knew that we would create one more big web app and several auxiliary apps.

Running up to the decision to using unbuilt engines we had several intense discussions on how to build loosely-coupled, highly-cohesive systems with Rails applications. We saw basically three choices:

  • All-in-one app that could get more structure through namespacing of its interals.
  • Services (REST or whatever, but) running as separate apps and communicating via APIs.
  • Engines running within a mostly feature-less container app.

The two big web apps share several models within the data access layer and use the same data. Because of this we chose the third option and left all of the apps within the same rails code base.

Internal discussions highlighted the costs/benefits of having all of these apps live within their own Rails project versus an engines approach. We feel that by using engines we are getting many of the benefits of a component based architecture without breaking Rails patterns. In addition, we feel that the cost of maintaining individual applications that share a central database or one giant application with less defined components would have been very high.

In order to make day to day development easier, and to avoid the “where do migrations live…” conversation and top level Rails deployment patterns, there is one twist to the architecture: everything resides in one git repo and engines are referenced from a single container application (similar to old school enterprise archive files). Each application is exposed via a unique context or resource identifier (each engine/app could also be isolated per instance via Apache). Here is the directory structure we ended up with:

container_rails_app/
  ...
  app
  config
  engines/
    etl/
    shared_modules/
    web_app_1/
    web_app_2/
  ...

Mike described this pattern in his recent blog posts Unbuilt Rails Dependencies: How to design for loosely-coupled, highly-cohesive components within a Rails application and Rails Contained: A Container for Web Application Development and Deployment. On how to make RubyMine work seamlessly with engines, read my post on IntelliJ Modules in Rubymine.

The steps we took

  • Generate a new, empty Rails app
  • Within the engines folder, create a new, mountable Rails engine
  • Copy all the tests from the original Rails app into the test directory and make them green.

    Ok. This step is a bit more involved. Essentially that’s it thought. Find some of the pitfalls we ran into described below. Here are a couple of highlights of what needs to happen:

    • Copy the files you need for a test to pass and namespace its class
    • Start with the model tests and work your way up towards integration and acceptance tests
    • Namespace everything with the name of the engine (either by fully qualifying every name – don’t do that) or with the lexical scope trick explained below. This includes tests and all classes.
    • Load needed gems explicitly: engines don’t load as much automatically as a Rails app
    • Namespace tables and assets
  • Copy the old .git directory into the new root folder in order to not loose any history. For us, git was not able to detect the changes as moves in many cases. This is certainly an area where you can improve on our solution!

For us, the real work started after this, when we started pullling out common code into a common models engine and began work on the second app.

We got all the tests to pass before we had namespaced any of the assets or rake tasks. That was an additional search-and-replace heavy step after the actual transition to an engine. It is not necessary right away if you do not have multiple applications at first, but to achieve the full effect, you will need to namespace the things as well.

Pitfalls and discoveries

Namespaces

Modules in your enige are not automatically loaded: make sure you reference them yourself before they are needed in other files.

Asset pre-compilation

Rails creates a default app/assets/stylesheets/application.css file which contains these lines:

/* ...
*= require_self
*= require_tree .
*/

If you have this file in your main app, all css files will be compiled into one file. For us, this made almost everything look right. Almost. Little things broke here and there. Our app contained a couple of sections for which the stylesheets were meant to be loaded separately. Add files that you want to have precompiled as individual files to config.assets.precompile list to have them precompiled into separate files and solve this problem.

Are all your references and associations breaking?

Try this

module M
  class X
    def use_y
      Y.do_it!
    end
  end
  class Y
    def self.do_it!
    end
  end
end

instead of

class M::X
  def use_y
    M::Y.do_it!
  end
end
class M::Y
  def self.do_it!
  end
end

The first way sets the lexical scope to the module M as well as to the class Y and allows you to reference other classes in M without their full name. The second way sets the scope only to class M::Y and you have to fully qualify every class name to find it.

Don’t fight conventions

We were using fixture builder and while we were namespacing classes in modules we were trying to override the default table names to not be namespaced… Fixture builder didn’t like that at all. There may or may not be other dependencies that make leaving the conventions hard. So, save yourself the trouble and do the migrations (and stay consistent!) and namespace your tables as well!

HABTM

They seem to be falling out of favor, which is probably a good thing. With engines they don’t seem to work (well). We ended up getting rid of our last two habtm relationships instead of trying to make them work. Creating the join as a class and adding the necessary has many :through relationships is straight forward enough.

We ended up with one production performance bug due to this: Rails

Engines depending on engines

We used kaminari for our pagination requirements. When our app first became an engine, kaminari stopped picking up our custom views. Instead it used its standard views. The load order got screwed up to which there are basically two solutions:

  • require => nil on both engines in the Gemfile and force the correct load order in your app, or…
  • avoid name clashes by namespacing your views (which you were going to do anyways, right?)
  • 0 Shares
  • Share on Facebook
  • Share on Twitter
Georg Apitz

[Standup] [Boulder] heroku and the crickets

Georg Apitz
Friday, March 2, 2012

help

  • anybody seen old content showing up on heroku at various points in time (especially at night)?
    • crickets …

interesting

  • rake execute takes a hash, rake invoke takes an ordered list
  • http://join.me is a cool tool for interactive screen sharing
  • don’t use anonymous iframes for ajax file upload, use a class or id,
    • we saw weird issues with a chat provider who uses an anonymous iframe which was getting all our file upload info
  • 0 Shares
  • Share on Facebook
  • Share on Twitter

Topics

  • agile (783)
  • rails (117)
  • testing (90)
  • ruby (86)
  • ruby on rails (71)
  • jobs (62)
  • javascript (59)
  • techtalk (44)
  • ironblogger (42)
  • rspec (39)
  • bloggerdome (34)
  • productivity (34)
  • activerecord (30)
  • rubymine (30)
  • git (29)
  • gogaruco (29)
  • nyc (27)
  • design (24)
  • mobile (23)
  • pivotal tracker (22)
  • process (21)
  • cucumber (21)
  • jasmine (19)
  • ios (18)
  • tracker ecosystem (17)
  • webos (17)
  • objective-c (17)
  • fun (16)
  • android (16)
  • palm (16)
  • ci (16)
  • "soft" ware (16)
  • bdd (15)
  • tdd (15)
  • cedar (15)
  • rails3 (14)
  • performance (14)
  • css (14)
  • gem (13)
  • mouse-free development (12)
  • selenium (12)
  • goruco (12)
  • bundler (12)
  • api (12)
  • keyboard (11)
  • meetup (11)
  • railsconf (11)
  • nyc-standup (11)
  • capybara (10)
  • mac (10)
Subscribe to rails Feed
  1. ←
  2. 1
  3. 2
  4. 3
  5. 4
  6. 5
  7. 6
  8. 7
  9. ...
  10. 12
  11. →
  • About
  • Case Studies
  • Team
  • Community
  • Careers
  • Tools
  • Contact
  • Labs
  • Events

Contact Us

contact@pivotallabs.com
+1 415-77-PIVOT
TwitterLinkedInFacebook

Pivotal Tracker

Tracker is the award-winning agile project management tool that enables real-time collaboration around a shared, prioritized backlog.
Visit pivotaltracker.com >