Loccasions: Hiring a Foreman, Inheriting Resources, & Occasions

Share this article

In this post, I want to finally get the Occasions MVC sequence done. This is the seventh post in the series, and I thought we’d be farther by now. Those responsible for our less-than-expected progress have been sacked. First, however, let’s make firing up the development environment a bit easier. Maybe that will kickstart our productivity…

Hiring a Foreman

Every time I want to hack on Loccasions, I have to fire up guard, a web server (rails s, for now), and mongodb along with my vim session. Without fail, I forget to fire up mongodb, so guard blows up all over the place. It’s an annoying time-waster and also puts me in a bad mindset at the start of my hack session. I would like to clean this up a bit, so I am bringing in Foreman . Foreman is a “manager for Procfile-based applications”, which Google will tell you means you can create a Procfile (we’ll put ours in the root of the app) and list out the processes we want Foreman to start up.

That’s sounds positively smashing to me, so I add gem foreman, "~> 0.24.0" to the :development and :test groups in my Gemfile, quick bundle install and foreman is officially on the payrole.

I have three processes I want to run in development: mongod, guard, and rails s, so my Procfile looks like:

web: rails s
test: guard
db: mongod --dbpath=/Users/ggoodrich/db/data

Now, I can type foreman start in my application directory and Forman will start these three processes.

No hard hat here

I like to imagine a scruffy, hard-hat wearing dude screaming at the processes (“ALL RIGHT, Database! Get off your lazy shard and prepare for data!”) Although, being honest, I think a better name for Formean would have been Procadile. I can already see the logo…maybe I need to get a non-programming hobby…

OK, maybe the logo would be better than this...


We’re finally to a point where we can design how we’ll add Occasions. Occasions, as you may remember, belong to an Event. An Occasion is an individual occurrence of that Event. So, if your Event is “Selling Girl Scout Cookies”, then an Occasion for that event might be “February 2nd, 2010” with the lat/long of 35.223/-85.443 (My Neighbor’s House), and a note of “2 boxes of Samoas”. Another Occasion for that Event could, then, have a date of February 10th, 2010, with the lat/long of (lat/long for my kid’s school), and a note that says “Mrs. Whatsherface bought 1 box of Thin Mints.”

Let’s write some unit tests around that idea. Put this in spec/models/occasion_spec.rb

require 'spec_helper'

describe 'Occasion' do
  before do
    @event = Factory.build(:event)
    @occasion = @event.occasions.build

  it "should belong to an event" do
    @occasion.event.should_not be_nil
  it "should have a time and date of occurrence" do
    dt = Time.now

    @occasion.occurred_at = dt
    @occasion.occurred_at.to_s.should == dt.to_s

  it "should have a latitude and longitude" do
    @occasion.latitude = -85.000
    @occasion.longitude = 35.3232

    @occasion.latitude.should == -85.000
    @occasion.longitude.should == 35.3232

  it "should have a note" do
    @occasion.note = "This thang went down"
    @occasion.note.should == "This thang went down"


These tests fail, because we haven’t created an Occasion model and Event doesn’t have a occasions method. A quick rails g model Occasion occurred_at:datetime latitude:float longitude:float note:text -s will take care of that. (Note: the -s skips existing files, which is our spec file that we already created). We have to modify the generated model file to tell it that it lives in Events. Our app/models/occasion.rb file looks like: (I’ve gone ahead and added validations and accessors)

class Occasion
  include Mongoid::Document
  field :occurred_at, :type => Time
  field :latitude, :type => Float
  field :longitude, :type => Float
  field :note, :type => String
  embedded_in :event, :inverse_of => :occasions
  validates :occurred_at, :latitude, :longitude, :presence => true

  attr_accessible :occurred_at, :latitude, :longitude, :note

Also, open up models/event.rb and add embeds_many :occasions below the embedded_in :user line. I realized, looking at this file again, that I had neglected to defined which attributes on Event should be accessible. This is bad mojo, so I added attr_accessible :name, :description to the Event model.

Changing Our Spork Configuration

In the midst of writing the Occasion model spec, I added a new factory to create an Occasion in spec/factories.rb

factory :occasion do
    latitude 35.1234
    longitude -80.1234
    occurred_at DateTime.now
    note "Test Occasion"

With my new factory, I changed the before block in the occasion spec to use it. This resulted in my specs blowing up all over the place with errors like:

Donde esta mi factory?

So, my new-fangled Spork/Guard super fantastic environment wasn’t reloading the factories. I frantically turned to Google and asked “WHAT NOW??!?” Google calmly replied, “Put this in the Spork.each_run block in your spec/spec_helper.rb file, my man.”

# Reload our factories
  Dir[Rails.root.join("spec/factories.rb")].each{|f| load f}

Guard knows to reload the RSpec environment when you mess with spec_helper.rb, so my tests were happy again. While we are in there, let’s add something to reload the routes too:

# Reload routes

Now that we have a model, we need a way to create them.

You Say Potatoe “Hurry up”, and I Say Potahtoe “Occasions Controller”

At this point, we should all be Olympic Gold Medalists at creating the vanilla Rails Controller for a resource. In this case, our resources are Occasions. Go ahead and try to get a working (and spec’d) controller for Occasions up and running. You can check what I did with this gist and see how it came out.

Inherited Resources

WHOA! What’s up with THAT gist? That doesn’t look like what we did for the events controller. You’re right, it doesn’t look like that. I tricked you. Jose Valim of Plataformatec (and Crafting Rails Applications) fame created the inherited_resources gem to address the fact that 95% of all RESTful controllers in Rails do the same stuff. Using Jose’s gem, we can have our OccasionsController inherit from InheritedResources::Base and we get the 7 ~~Deadly~~common controller actions for free. I heart this community. (BTW, now we be a good time to add gem "inherited_resources", "~> 1.3.0"
to your Gemfile and bundle install that baby.)

In this case, it’s not totally free, though, as we have to do some configuration to handle our “special” circumstances.
These circumstances relate mostly to our using MongoDB and the fact that Occasions are embedded within a document hierarchy (User ==> Events ==> Occasions). If you try to do something like Occasion.where(:event_id => @event.id) or whatever, you get the following error that scares the hell out of you the first time you see it:

Mongoid::Errors::InvalidCollection: Access to the collection for Occasion is not allowed since it is an embedded document, please access a collection from the root document.

Once you calm down, you realize that this makes total sense. Because we are using a document database, occasions are embedded within events and events are embedded within users. So, rather than use the regular ActiveModel class methods to access the collections, you have to walk down the document hierarchy. We need a user (current_user, which we are already using to scope events), and an event. Where do we get the event?

The route parameters have a :event_id entry so, if we were doing this ourselves, we’d grab that and query the current_user.events collection. This is a pretty common scenario, and the inheritedresources gem is crazy smart about common scenarios. Let’s take a look at this configuration in the app/controllers/occasionscontoller.rb


belongs_to :event
actions :all, :except => [:show, :index]

def begin_of_association_chain

But wait! There’s more!! You see that action method call up there? That tells inherited_resources which actions we want (or don’t want, in this case) for our controller. Occasions will only ever been seen through an Event, so there is no point in creating the show and index actions (we will change our mind when we get to the Loccasions API) right now. The truly perceptive among you are now asking “But, what about redirects?”, which is a great question. A common idiom for Rails RESTful controllers is to redirect to the index or show page after resource creation. Again, we aren’t going to do that here, we want to go to the events#show action. The inherited_resources gem has a feature called “Smart redirects” that (from their github page:)

Redirects in create and update actions calculates in following order resourceurl, collectionurl, parenturl (which we are going to see later), rooturl. Redirect in destroy action calculate in following order collectionurl, parenturl, root_url.

In other words, it figures out what we want. I squealed like a little girl when I found that feature. (To be fair, though, I squeal a lot.)

Pretty straightforward, and we’ve reduced the amount of code we need to write. Occasions can be added to an event. I’ve written the spec/acceptance/add_occasions_spec.rb and delete_occasions_spec.rb. I am not currently going to worry about update, because I am having a problem seeing the use case. I am sure we’ll be back to update later, but right now I want to get to the map.

Update: Alert Reader Nicholas Henry points out in the comments below that you need to:

  • Amend events/show.html.haml with the Occasion form github
  • Add occasions/_occasion.html.haml github
  • Add the route for occasions github

Loccasions.map do { |its| about.time()}

Well, almost…the map will be the next post.

Glenn GoodrichGlenn Goodrich
View Author

Glenn works for Skookum Digital Works by day and manages the SitePoint Ruby channel at night. He likes to pretend he has a secret identity, but can't come up with a good superhero name. He's settling for "Roob", for now.

Share this article
Read Next
Get the freshest news and resources for developers, designers and digital creators in your inbox each week
Loading form