Assigning blueprint attributes explicitly

94 views
Skip to first unread message

Graham Ashton

unread,
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}" }
end

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
(required)>'

The backtrace looks like this:

ArgumentError: wrong number of arguments (1 for 2)
/Users/graham/.rvm/gems/ruby-1.9.2-p180@qtm/gems/rake-0.9.2/
lib/rake/file_utils_ext.rb:36:in `copy'
/Users/graham/.rvm/gems/ruby-1.9.2-p180@qtm/gems/rake-0.9.2/
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):
https://github.com/jimweirich/rake/blob/master/lib/rake/dsl_definition.rb#L163

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
screwed.

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'
end

Survey.blueprint do
user { User.make }
end

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

Cheers,
Graham

Pete Yandell

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

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

Graham Ashton

unread,
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 }
    end

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.

Cheers,
Graham
Reply all
Reply to author
Forward
0 new messages