Skip to content


Complex Associations in Factory Girl

I finally decided to bite the bullet and move the main Rails project I work on from FixtureReplacement to Factory Girl. Weighing in at 7404 lines of test code, this was a non-trivial task.

If you peruse the Factory Girl docs, it mentions associations, but only how to handle a simple “belongs to” or maybe “has one” sort of association. My good friend led me to this thread on StackOverflow.

Basically, the user fowlduck came up with the solution of having an “after_build” block.

My problem couldn’t be solved with a simple “has_many/belongs_to” association, because in this case I needed to ensure the invoice seller matched the producer of the product being invoiced. Here was my solution:

Factory.define :invoice do |i|
  i.after_build do |invoice|
    invoice.buyer = Factory.create :consumer unless invoice.buyer
    next if invoice.seller
    invoice.line_items = [Factory.create(:line_item)]
    invoice.seller = invoice.line_items.first.product.producer
  end
end

So first, notice the “unless invoice.buyer” and “next if invoice.seller” these allow me to override these values in my tests. You’re gonna wanna keep that in mind. Next, I create a LineItem, which automatically creates a product, which in turn, creates a producer. After that I can just set the seller to be the producer of the product in the first line item. Voila, product line_item producer matches invoice seller.

I’ve found this tremendously useful. Here’s another example of how I’ve used it:

Factory.define :user do |a|
  name = Faker::Name.name
  a.real_name { name }
  a.username { Faker::Internet.user_name(name) }
  a.password 'secret-1'
  a.password_confirmation 'secret-1'
  a.email_address { Faker::Internet.email(name) }
  a.after_build do |login|
    next if login.valid?
    next unless login.errors.invalid?(:username)
    login.username = Faker::Internet.user_name(login.real_name)+String.random(4)
  end
end

I did this because, everyone once in a great while my tests would fail because the same username is used more than once. But now, if this happens, I just tack on 4 random characters. Since i did this, I haven’t had the test fail.

Posted in Rails, Ruby, Software Development, Tips and Tricks.

Tagged with , , , , , .


Capistrano + Fetcher/Daemonize

So I’ve spent the afternoon slogging through this little problem. Something tells me I’m going to be referring to this post again and again.

Are you using the fetcher plugin and Capistrano for deployment? Hey, me too! Apparently I’m the only one in the world who didn’t immediately know how to make them work together though.

Here are my Capistrano tasks for starting/stopping/restarting my fetcher daemons:

after "deploy:restart", "fetcher:restart"
 
namespace :fetcher do
  desc "Stop the fetcher_mailer daemons"
  task :stop, :roles => :app do
    run "RAILS_ENV=#{rails_env} ruby #{current_path}/script/mailer_daemon_fetcher stop"
  end
 
  desc "Start the fetcher_mailer daemons"
  task :start, :roles => :app do
    run "RAILS_ENV=#{rails_env} nohup /opt/ruby-enterprise/bin/ruby #{current_path}/script/mailer_daemon_fetcher start", :pty => true
  end
 
  desc "Restart (cycle) the fetcher_mailer daemons"
  task :restart, :roles => :app do
    stop
    start
  end
end

The real key here is on the :start task, there are two things you need to do to get the shell to let go of the process.:

  1. Add the nohup to your command. If you don’t do this the process goes into the background, and as soon as the shell exits the OS says “SIGHUP” (“you don’t have to go home, but you can’t stay here.”)
  2. The second thing is the :pty => true. This tells Capistrano to request a pseudo terminal. I don’t know all the nitty gritty, and have neither the time nor the inclination to dig in and find out why.

So there you go, add a nohup and :pty => true and then you can get your fetcher daemon backgrounded.

Posted in Rails, Ruby, Software Development, Tips and Tricks.

Tagged with , , , , , , .


ActionView::TestCase and nil.url_for

If you’re having problems testing your helpers because of messages like these:

The error occurred while evaluating nil.url_for

The problem, it turns out is ActionView::TestCase doesn’t like url_for, it seems to expect you’re using named routes. So, in your helper, instead of doing something like:

def link_to_awesomeness(my_object)
  if my_object.awesome?
    link_to 'Awesomeness', :action => 'show_awesomeness', :id => my_object
  else
    link_to 'Make Awesome', :action => 'make_awesome', :id => my_object
  end
end

Try something more like this:

def link_to_awesomeness(my_object)
  if my_object.awesome?
    link_to 'Awesomeness', awesomeness_path(my_object)
  else
    link_to 'Make Awesome', make_awesome_path(my_object)
  end
end

Of course this implies you’ve created those named routes in your config/routes.rb, e.g.:

map.awesomeness 'my_objects/:id/awesomeness', :controller => 'my_objects', :action => 'show_awesomeness'
map.make_awesome 'my_objects/:id/make_awesome', :controller => 'my_objects', :action => 'make_awesome'

Posted in Rails, Ruby.


Fun Little ActiveRecord Gotcha

So I was cruising along adding a new model to a project. The model is being used to store credit card information and when I generated the model, I absentmindedly did:

$ script/generate model CreditCard ... encrypted_expiration_date:date ...

Chugging along writing my tests I wrote a test like this:

class CreditCardTest  < ActiveSupport::TestCase
  context "A CreditCard instance" do
    subject { CreditCard.new(:expiration_date => new_expiration_date) }
    should("ensure expiration_date is encrypted") do
      assert_equal expiration_date_cipher_text, subject.encrypted_expiration_date
    end
    def new_expiration_date; Date.today+90; end
    def expiration_date_cipher_text; "!!!EXPIRATION_DATE_CIPHER_TEXT!!!"; end
  end

And the model looking something like this:

class CreditCard < ActiveRecord::Base
  def expiration_date=(exp_date)
    @expiration_date = exp_date
    self.encrypted_expiration_date = encrypt(exp_date)
  end
end

The test would fail every time like:

Finished in 0.030941 seconds.

  1) Failure:
test:  should encrypt expiration_date. (CreditCardTest)
    [test/unit/credit_card_test.rb:53:in `__bind_1261076018_868915'
     shoulda (2.10.2) [v] lib/shoulda/context.rb:351:in `call'
     shoulda (2.10.2) [v] lib/shoulda/context.rb:351:in `test:  should encrypt expiration_date. ']:
<"!!!EXPIRATION_DATE_CIPHER_TEXT!!!"> expected but was
.

1 tests, 1 assertions, 1 failures, 0 errors

Can you guess the problem? Ten points to Gryffindor if you said it’s because the column type is date. And another five points if you can say it’s because ActiveRecord is tries to parse it to be a date object, which fails, and returns nil.

Posted in Rails, Ruby, Software Development, Tips and Tricks.


Limit Access to WordPress Admin

This entry on the Internet Storm Center’s Handler’s Diary was about “Distributed WordPress admin account cracking” — scary stuff.

In the the article they suggest limiting the addresses from which the admin can be accessed. If you’re using Apache, here’s one way using the <Location> directive:

<Location /wp-admin>
   Order Deny,Allow
   Deny from all
   Allow from example.com 10.211.34.83
</Location>
<Location /wp-login.php>
   Order Deny,Allow
   Deny from all
   Allow from example.com 10.211.34.83
</Location>

Posted in Apache, Security, Tips and Tricks.

Tagged with , , , , .


Expected response to be a <:redirect>, but was <0>

If you’re seeing this in your test:

Expected response to be a <:redirect> , but was <0>

You are probably doing something like this in your controller:

redirect_to :action => 'messages', :status => 'read'

Watch out when using :status in your params. :status is used to set the http status code in the redirect_to method, like so:

redirect_to :action=>'atom', :status=>302

Posted in Rails, Ruby, Software Development, Tips and Tricks.

Tagged with , , , .


Enforcing Executing User on Bash Scripts

Sometimes I want to make sure that a script is *not* being run by the root user:

#!/bin/bash
if [[ $EUID -eq 0 ]]; then
  echo "Don't run as root!"
  exit 1
fi

Posted in Bash, Tips and Tricks.

Tagged with , , .


Rails fields_for “is not allowed as an instance variable name”

So I’ve been trying to figure this error out, it just cropped up:

`@my_object[such_and_such_attributes]' is not allowed as an instance variable name

As is usually the case with Rails, the answer is pretty obvious once you understand what’s going on. Here’s the relevant code:

<% fields_for 'my_object[such_and_suches_attributes]', @my_object.such_and_suches.first do |such_and_such_form| -%>
  <%= such_and_such_form.text_field :name %>
<% end -%>

So why was the above creating the error? It’s because @my_object.such_and_suches was empty, thus Rails would try and use “my_object[such_and_suches_attributes]” as the attribute name which naturally didn’t work. This is the shortcut that allows this to work:

<% fields_for :posts do |post_form| -%>
  <%= post_form.text_field :title %>
<% end -%>

Now in my situation, why the “such_and_suches” is empty is a whole other problem…

Posted in Rails, Ruby, Software Development, Tips and Tricks.


Firefox Gotcha: innerHTML Strips td Tags

Try and run the following in Firefox, and it’s gonna tell you “you’re screwed”:

var newTable = document.createElement('table');
var rowContent = '&lt;td&gt;some stuff&lt;/td&gt;&lt;td&gt;&lt;em&gt;something else&lt;/em&gt;&lt;/td&gt;';
var newTr = document.createElement('tr');
newTr.innerHTML = rowContent;
newTable.appendChild(newTr);
 
if ('&lt;td&gt;some stuff&lt;/td&gt;&lt;td&gt;&lt;em&gt;something else&lt;/em&gt;&lt;/td&gt;' == newTr.innerHTML) {
    alert("Everything's hunkydory");
}
else {
    alert("You're screwed");
}

Why? My guess is Firefox doesn’t like it when you add td tags to elements that aren’t in a table, so it strips them. After doing the innerHTML assignment

<td>some stuff</td><td><em>something else</em></td>

becomes

some stuff<em>something else</em>

Fortunately, the solution is easy, just append the tr before the innerHTML assignment:

newTable.appendChild(newTr);
newTr.innerHTML = rowContent;

Then you’ll see “Everything’s hunkydory”. 😉

Posted in Javascript, Software Development, Tips and Tricks.

Tagged with , , , , .


Rails/ActiveRecord: belongs_to :polymorphic => true

This morning, I was looking for a way to have an ActiveRecord object “belong to” one of a handful of different object types. That’s when I came across the :polymorphic option of the belongs_to method.

From the description and the “Polymorphic” section of the docs it seemed like it would do what I need, so I set out to figure out how to use it. I was looking for an easy clear description of how it’s used, but several Google searches produced nothing. So I decided to make one myself.

We’ll start off with a little project used to track items in a “media library,” e.g. Books, Music, and Movies. Given the following models:

$ script/generate model Book author:string title:string summary:string isbn:string
$ script/generate model Album artist:string title:string track_listing:string
$ script/generate model Movie director:string title:string summary:string
$ script/generate model Artwork library_item_id:integer library_item_type:string

We want our Book, Album, and Movie objects to have one or more “Artwork” objects to represent covers or movie posters. First let’s tell the Artwork class it belongs to the other objects:

class Artwork < ActiveRecord::Base
  belongs_to :library_item, :polymorphic => true
end

Notice how the :library_item corresponds to the columns we defined: library_item_id and library_item_type. ActiveRecord is going to store the Book#id, Album#id, or Movie#id in the library_item_id. In the library_item_type column ActiveRecord will put "Book", "Album", or "Movie", as appropriate. For example, if you do this:

>> Artwork.new(:library_item => Book.create)
=> #<Artwork id: nil, library_item_id: 1, library_item_type: "Book", created_at: nil, updated_at: nil>

The library_item_id and library_item_type columns will have Book#id, in this case ‘1’, and Book#class, which is Book, respectively.

Now here’s how the Book, Album, and Movie classes are tied to the “artwork”:

class Book < ActiveRecord::Base
  has_one :cover_image, :class_name => 'Artwork', :as => :library_item
end
class Album < ActiveRecord::Base
  has_one :cover_art, :class_name => 'Artwork', :as => :library_item
end
class Movie < ActiveRecord::Base
  has_one :poster_image, :class_name => 'Artwork', :as => :library_item
end

This gives us methods like Album#cover_art we can use to reference the Artwork object from the Album object.

So there you go, hopefully this proves useful…

Posted in Rails, Ruby, Software Development, Tips and Tricks.

Tagged with , , , , , , , .