How to test for deleting an association in FactoryGirl? - ruby-on-rails-3

I have the following factory:
factory :store do
room
factory :store_with_items do
ignore do
items_count 4
end
after(:create) do |store, evaluator|
FactoryGirl.create_list(:equippable_item, evaluator.items_count, store: store)
end
end
end
Next, I create an object:
#store = FactoryGirl.create :store_with_items
My problem is that when I "delete" one of the store's items, the store still shows that it has 4 items.
#store.items[0].store_id = nil
#store.save!
puts #store.items.size
The puts is 4. How do I properly delete an item? Isn't this how you would do it in rails?

I used to prefer this approach, but now I avoid it; its easier and more flexible to let factories be simple and populate has_many associations at runtime.
Try this
Factory for store (same):
factory :store do
room
end
Factory for items:
factory :item do
store # will use the store factory
end
Then in my test I would populate what is appropriate for the case at hand:
#store = FactoryGirl.create :store
#item1 = FactoryGirl.create :item, store: #store
#item2 = FactoryGirl.create :equippable_item_or_whatever_factory_i_use, store: #store
To explain
By passing in the store instance explicitly, the association will be setup for you. This is because when you pass something explicitly in FactoryGirl.create or FactoryGirl.build it overrides whatever is defined in the factory definition. It even works with nil. This way, you'll have real object instances that give you all the real functionality.
To test destroy
I think the code in your example is not good; it breaks the association between store and item, but doesn't actually remove the item record so you're leaving behind an orphan record. I would do this instead:
#store.items[0].destroy
puts #store.items.size
Bonus
You probably also want to setup your child associations to be destroyed when the parent is destroyed if its not already. This would mean when you say #store.destroy all the items belonging to it will also be destroyed (removed from the db.)
class Store < ActiveRecord::Base
has_many :items, dependent: :destroy
.....
end

Related

Rails: create lots of almost-duplicate records

I want to write a method that creates a bunch of almost-duplicate records, just with one or two parameters changed. I'll make a form to control those parameters, I'm just wondering about how best to write the method, and where do keep it.
Presently in my document.rb I've written this:
def self.publish(brand, components, template)
brand.users.each do |user|
Document.create(:component_ids => components, :message => 'Message.', :template_id => template.id, :user_id => user.id)
end
end
It doesn't feel right though. Is there a better way to do this?
This code is fine if your security model allows all these fields to be bulk assignable by mention in attr_accessible in the model. If it doesn't then you're better off using the block form of create. Also, if Document, Template and User are ActiveRecord instances, you should let Rails manage the details of ids.
def self.publish(brand, components, template)
brand.users.each do |user|
Document.create do |doc|
doc.component_ids = components,
doc.message 'Message.',
doc.template = template,
doc.user = user
end
end
end
One final note is that component_ids must be serialized to store a list. This is probably a flaw in your model design. The better way is (probably) to specify Component belongs_to User and also User has_many Components. I.e. Component contains a foreign key to User. If it's necessary for a Component to belong also to many users, then you'll need either has_and_belongs_to_many or has_many ... through. The Rails guide on relations describes all this in more detail.
With the right relations set up, the code will become:
def self.publish(brand, components, template)
brand.users.each do |user|
Document.create do |doc|
doc.components = components, # Components is now a list of active records.
doc.message 'Message.',
doc.template = template,
doc.user = user
end
end
end
The resulting SQL will get all the foreign keys and (if necessary) relation tables filled in correctly.

has_many :through and build

I have three models, Account, User and Contact:
class User < ActiveRecord::Base
has_one :account
has_many :contacts, :through => :account
end
class Account < ActiveRecord::Base
belongs_to :owner, :class_name => 'User'
has_many :contacts
end
class Contact < ActiveRecord::Base
belongs_to :account
end
I'm trying to scope build a new contact through the user record, like this in my contacts controller.
def create
#contact = current_user.contacts.build(params[:contact])
respond_to do |format|
if #contact.save
...
else
...
end
end
end
When I do this, I don't receive any errors, the contact record is saved to the database however the account_id column is not set on the contact, and it is not added to the collection so calling #current_user.contacts returns an empty collection.
Any suggestions?
Using build makes a new instance of Contact in memory, but you would need to manually set the account_id on the record (e.g. #contact.account_id = current_user.account.id), or perhaps set it in a hidden field in the new form used to display the contact for creation such that it is picked up in the params array passed to the build method.
You might also want to consider whether accepts_nested_attributes_for may be helpful in this case. Another option may be to use delegate, although in both cases, your use may be sort of the opposite of what these are intended for (typically defined on the "parent").
Update:
In your case, the build method is added to both the User instance and to the Account (maybe "Owner") instance, because you have both a many-to-many relationship between User and Contact, as well as a one-to-many relationship between Account and Contact. So to get the account_id I think you would need to call Account's build, like
#contact = current_user.accounts.contacts.build(params[:contact])
Does this work?

Associated records via custom query in Rails

I have two models that are connected via a has_many/belongs_to association:
Class Project < ActiveRecord::Base
has_many :tasks
end
Class Tasks < ActiveRecord::Base
belongs_to :project
end
Each of the tasks are tagged with a HABTM relationship:
Class Tasks < ActiveRecord::Base
belongs_to :project
has_and_belongs_to_many :tags
end
I am trying to get a list of projects based on a tag id. I can get a list of projects that have tasks with a specific tag by using a class method on my Project model:
def by_tag(tag_id)
Project.joins(:tasks => :tags).where(:tags => {:id = tag_id})
end
Ideally, I'm looking to be able to list all the projects and their associated tasks for a given tag in my view. I could normally get a list of tasks belonging to a given project by using project.tasks if I used a typical find with project like Project.find(1).
However, when I try project.tasks on results found using my new class method Project.by_tag(1), I get a "NoMethodError: Undefined Method 'tasks'" error.
I looked into Named Scopes to get the Project by Tag results but it seems like people are moving away from that approach in favor of class methods. Is that true?
On your project model you need to add it to the class not the instance. Also note that this raises the self object to the class so you can eliminate "Project." unless you want to be explicit.
class << self
def by_tag(tag_id)
joins(:tasks => :tags).where(:tags => {:id = tag_id})
end
end
There is always debate over what is the best method. I myself prefer whatever gets the job done quicker. I like scopes personally but to each his own.

has_many :through -- Adding metadata to the through relationship

I have a need to add metadata about a HABTM relationship. I wanted to use a has_many :through relationship to accomplish this, but it is not necessary. Here is the problem simplified:
class Customer < ActiveRecord::Base
has_many :customer_teddy_bears
has_many :teddy_bears, :through => :customer_teddy_bears
end
class CustomerTeddyBear < ActiveRecrod::Base
belongs_to :customer
belongs_to :teddy_bear
attr_accesible :most_favoritest # just to show it exists, boolean
end
class TeddyBear < ActiveRecord::Base
has_many :cusomter_teddy_bears
end
So what I need to do is start adding teddy bears to my customers, Teddy Bears are a fixed set of data, lets say a fireman_bear, doctor_bear, dominatrix_bear. Any customer can claim to own a kind of teddy bear, but they also specify which is their most favoritest bear. Since I cannot modify the bears model because that is globally shared among all customers I am adding the metadata (among other metadata) to CustomerTeddyBear.
The problem is that the following does not work.
customer = Customer.new # new record, not yet saved, this must be handled.
customer.teddy_bears << fireman_bear
customer.teddy_bears << doctor_bear
# now to set some metadata
favoritest_record = customer.customer_teddy_bears.select{|ctb| ctb.teddy_bear == doctor_bear}.first
favoritest_record.most_favoritest = true
The above code does not work since customer_teddy_bears entries are only populated during save when creating records in the database. Is there another mechanism for doing this?
If there is nothing "automated" built into rails I will just have to manually manage this relationship by including teddy_bears when I select customer_teddy_bears and using techniques like
def teddy_bears
self.customer_teddy_bears.map(&:teddy_bear)
end
along with manually creating the associations, and not using a :through relationship.
please note, all this must happen before the #save is executed on the Customer object, so I need to set all relevant metadata while still in-memory.
Recommendations I got from #RubyOnRails
ctb = customer.customer_teddy_bears.build({:customer => customer, :teddy_bear => fireman_bear})
ctb2 = customer.customer_teddy_bears.build({:customer => customer, :teddy_bear => doctor_bear})
...
ctb.most_favoritest = true
You can simply do this:
customer = Customer.new # new record, not yet saved, this must be handled.
customer.teddy_bears << fireman_bear
customer.teddy_bears << doctor_bear
customer.save
fav = CustomerTeddyBear.where(:customer_id => customer.id, :teddybear_id => doctor_bear.id)
fav.most_favoritest = true
fav.save
The solution I was forced to resort to is manually building the CustomerTeddyBear object and setting both the customer, teddy_bear, and most_favoritest. Basically most of the time, access is by customer.customer_teddy_bears.map(&:teddy_bear) at least in logic where the possibility is that the record is not yet saved, otherwise just short-cut to customer.teddy_bears.

Rails: Make different references to a DB row refer to the same Ruby object

Suppose I have the following model relationship:
class Player < ActiveRecord::Base
has_many :cards
end
class Card < ActiveRecord::Base
belongs_to :player
end
I know from this question that Rails will return me a copy of the object representing a database row, meaning that:
p = Player.find(:first)
c = p.cards[0]
c.player.object_id == p.object_id # => false
...and therefore if the Player model modifies self, and the Card model modifies self.player in the same request, then the modifications won't take any notice of each other and the last-saved one will overwrite the others.
I'd like to work around this (presumably with some form of caching), so that all requests for a Player with a given id would return the same object (identical object_ids), thereby allowing both models to edit the same object without having to perform a database save-and-reload. I have three questions:
Is there already a plugin or gem to do this?
Are there good reasons why I shouldn't do this?
Can anyone give me some pointers on how to go about doing this?
This is supported in Rails 3.x. You can use the :inverse_of option for the has_many association for example. Documentation here (search for :inverse_of and Bi-directional associations).