1

I'm trying to create an object with embedded attributes for testing. This example uses a user with multiple languages but I would love a solution for the general case of creating embedded objects.

Present creation code:

def valid_attributes
  { :languages => [Language.new(language: "en-US", proficiency: "1")] }
end

user = User.create! valid_attributes

The models:

class User
  include Mongoid::Document
  field :languages
  embeds_many :languages 
  validates_presence_of :languages
  attr_accessible :languages_attributes
  accepts_nested_attributes_for :languages, :reject_if => lambda { |a| a[:language].blank? }, :allow_destroy => true
end

class Language
  include Mongoid::Document
  field :language
  field :proficiency
  key :language
  embedded_in :user
  attr_accessible :language, :proficiency 
end

These models work fine for creating objects from nested forms in Ryan Bates' footsteps (https://github.com/ryanb/complex-form-examples). I don't know whether that is the right way, but I assume so.

There are a few obvious solutions I can see. One is to just hardcode the input like what is generated from the forms:

{"user"=>{ "languages_attributes"=>{"0"=>{"language"=>"en-US", "proficiency"=>"1", "_destroy"=>"false", "id"=>"en-dash-us"}}}

That doesn't seem DRY or sane in the long run, to me.

The other solution is to just cut the embedded objects and use Arrays. Mongoid is pretty good at supporting arrays but you lose the ability to write validations for each object and the code would be less reusable.

Thoughts, Stackoverflowers?

2 Answers 2

3

For what it's worth, I followed @cug's advice and used Fabrication. I'm posting my code here for the benefit of others who hit this issue.

The spec/user/fabricator.rb Fabricator

Fabricator(:language) do
  language "en-US"
  proficiency "1"
end

Fabricator(:user) do
  languages { [ Fabricate.build(:language, :language => "en-US", :proficiency => "1") ] } 
end

Creating a user like this:

user = Fabricate.build(:user)

Thanks all, case closed. ^^

Sign up to request clarification or add additional context in comments.

Comments

2

You should really use Fixtures, or better Factories for that. FactoryGirl is very popular for creating factories.

In your case you will have to define the factory in spec/factories.rb like that:

FactoryGirl.define do
  factory :user do
    languages { [association(:language)] }
  end

  factory :language do
    language "en-US"
    proficiency "1"
  end
end

And than use it in your tests like that

user = FactoryGirl.create :user

3 Comments

FactoryGirl seems to be the right way to do it (it was on my list of 'things to learn about when I get time'). However, I don't understand much about it and I'm getting an error with your code. ` NoMethodError: undefined method new?' for nil:NilClass On the "languages { [association(:language)] }" line.
uh oh, sorry it seems that factory_girl is tightly coupled with ActiveRecord, so you may have problems using it with Mongoid. Nevertheless there are a lot of other choices of factories for rails, for example fabricationgem.org. I myself didn't use it but heard a lot of positive reviews and it says on it's page that Fabrication supports Mongoid.
Fabricate is spot on. I'm amazed at other mongoid using apps not getting caught out by factory_girl.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.