Assigning blueprint attributes explicitly

Skip to first unread message

Graham Ashton

Sep 28, 2011, 10:03:13 AM9/28/11
to Machinist Users
I've been upgrading a Rails 2.3 project that used Machinist 1 to Rails
3 and Machinist 2.0.0.beta2.

I've run into a problem, looked at the code, and have a question.

Here's my blueprint:

Survey.blueprint do
user { User.make }
name { "Survey #{sn}" }
heading { "Heading #{sn}" }
copy { "Survey copy #{sn}" }

When I run my tests with `ruby test/unit/survey_test.rb` it passes.

When I run my tests with `rake test:units` I get an unfortunate error.
Here's a snippet of it:

WARNING: DSL method Machinist::ActiveRecord::Lathe#copy called at
/Users/graham/Projects/qtm/test/blueprints.rb:27:in `block in <top

The backtrace looks like this:

ArgumentError: wrong number of arguments (1 for 2)
lib/rake/file_utils_ext.rb:36:in `copy'
lib/rake/dsl_definition.rb:156:in `copy'
/Users/graham/Projects/qtm/test/blueprints.rb:27:in `block in
<top (required)>'

The "DSL method" log message is produced by Rake, in lib/rake/
dsl_definition.rb (line 156).

Here's the code (now line 163 in master):

From a cursory inspection of Machinist's Lathe class, I can see that
method_missing is used to implement assignment of attributes on new
blueprints. This means that if you try and assign an attribute whose
name matches that of an object in the global namespace, you're

There may be a cunning way round this, but I can't help thinking that
FactoryGirl's explicit receiver approach (i.e. passing the object to
the block) is a much less brittle approach. But then I might be
missing something.

In other words this next snippet also fails, and I really don't think
the API ought to allow it to:

def user
raise 'whoops'

Survey.blueprint do
user { User.make }

Any thoughts? How about specifying the receiver of these methods
explicitly, FactoryGirl style?


Pete Yandell

Sep 28, 2011, 6:20:34 PM9/28/11
to Machinist Users
The simple solution:

Survey.blueprint do
self.user { User.make }

Graham Ashton

Oct 2, 2011, 8:06:39 AM10/2/11
to Machinist Users
Cheers. I'm not comfortable with code that redefines self inside a
block (it's confusing, and I don't think there's a good enough reason
to do it), but this suggestion does make it obvious what's going on
(but only to people who know that it's possible to redefine self
inside a block).

I also found that this works fine:

    Survey.blueprint do |survey|
      survey.user { User.make }

It would be worth adding one of these to the docs, as the naming
collision cost me many hours (I'm not exaggerating, it really did) of
fiddling around, reading source code, and banging my head against what
seemed like a brick wall. I've done a lot of Ruby, but sticking self
in front of the method call still wasn't an obvious solution for me.

Reply all
Reply to author
0 new messages