Skip to content

Instantly share code, notes, and snippets.

@jtrupiano
Created March 16, 2010 19:44
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jtrupiano/334413 to your computer and use it in GitHub Desktop.
Save jtrupiano/334413 to your computer and use it in GitHub Desktop.
Should factory_girl bypass :attr_accessible by default?
# factory_girl has difficulty playing nicely with attr_accessible.
class Article
belongs_to :owner
attr_accessible :name
end
Factory.define(:article) do |a|
a.association :owner
a.sequence(:name) {|i| "Article #{i}"}
end
# A core problem is that attr_accessible denies factory_girl's assignment of those specific attributes.
# It forces us as consumers of the Factory to use the :build strategy and then manually assign those
# attributes which is long-winded and annoying to say the least.
@article = Factory.build(:article)
@article.owner = Factory(:owner)
# Anyone who's used attr_accessible before will recognize a similar pattern in their controllers.
def create
@article = Article.new(params[:article])
@article.owner = admin? ? User.find(params[:article][:owner_id]) : current_user
@article.save
end
# For the controller case, we can leverage Ryan Bates' trusted-params plugin.
def create
params[:article].trust(:owner_id) if admin?
@article = Article.new(params[:article])
@article.save
end
# However, we're still left out to dry if we want to use factory_girl for our functional tests.
# My current solution is to define two separate factories to properly test both an admin and a regular
# user creating an article.
Factory.define(:attrs_for_article, :default_strategy => :attributes_for, :class => 'Article') do |a|
a.sequence(:name) {|i| "Article #{i}"}
end
# When used in conjunction with the trusted-params snippet, this works as intended.
Factory.define(:article, :parent => :attrs_for_article) do |a|
a.association :owner
end
# So great, now I can do my functional tests. But what about my unit tests?
Factory(:article) # ==> error that owner is not set!! FUCK!
# Grrrr. So how can I get trusted-params-like functionality in my models/unit tests?
# One way that factory_girl could solve this would be to support a before_save callback, e.g.
Factory.define(:article, :parent => :attrs_for_article) do |a|
a.before_save {|article| article.owner ||= Factory(:owner) }
end
# Another option would be to bake in a :bypass_attr_accessible option for factories, e.g.
Factory(:article, :bypass_attr_accessible)
# But even this has its shortcomings. Now I as a consumer of the Article factory must remember that it's
# protected some attributes (which I'm not even passing in because they're part of the default factory
# definition).
# Perhaps a better solution is to add it as an option to the factory definition?
Factory.define(:article, :attr_accessible => :bypass) do |a|
a.association :owner
a.sequence(:name) {|i| "Article #{i}"}
end
# What is nice about this is we can very easily test our authorization in our functional tests.
def test_should_update_article_as_a_regular_user
@user = Factory(:user)
@name = 'Something now in the factory'
@article = Factory(:article, :name => @name, :owner => @user)
log_in @user
# keep in mind the article factory will create a brand new user!
put :update, :id => @article.id, :article => Factory.attributes_for(:article)
assert_equal @article.reload.owner, @current_user
assert_not_equal @article.name, @name
end
# Or better yet, should factory_girl just bypass attr_accessible by default?
@webandy
Copy link

webandy commented Dec 9, 2010

Hi John. What did you end up doing to use factory_girl and attr_accessible together? (rails 2.3.5, factory_girl 1.3.2)

@jtrupiano
Copy link
Author

Hey Andy, honestly I don't remember. I actually have a sneaking suspicion that this gist is inaccurate...that attr_accessible was not giving me problems.

@goat000
Copy link

goat000 commented Mar 15, 2012

attr_accessible does seem to be causing the problems in my use of factory_girl for the record... so I wouldn't be surprised if it was your problem, too.

@goat000
Copy link

goat000 commented Mar 16, 2012

Also, for the poor soul who googled your way here 3 years in the future, it looks like factory_girl callbacks have since solved this, in a manner similar to the before_save suggestion above: http://codeulate.com/2009/11/factory_girl-callbacks/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment