Fabrication gem cyclic dependency - ruby-on-rails

I've got a cyclic dependency when worked with fabrication gem. Here I'll show you what I've did. Let's suppose I have 2 models:
class User < AR::Base
has_many :messages
class Message < AR::Base
belongs_to :user
So, the fabricators for them will be:
Fabricator(:user) do
# bla-bla-bla
messages(count: 5)
end
Fabricator(:message) do
# bla-bla-bla
user
end
It seems all right, yeah? But when I run Fabricate(:user) or Fabricate(:message) I get cyclic dependencies, because of fabricating of message fabricates new user, fabricating new user fabricates a messages for him and so on. How can I avoid this diabolic circle?

I would typically have two user fabricators in an instance like this.
Fabricator(:user)
Fabricator(:user_with_messages, from: :user) do
messages(count: 5)
end
You could alternatively do this to make what you have work.
Fabricator(:user) do
messages(count: 5) { Fabricate.build(:message, user: nil) }
end
The messages will be saved automatically by AR when the user is saved. It will handle setting up the correct references.

Related

Rails association callback with cached query

I have two models:
class Basket < ActiveRecord::Base
has_many :products
def recalculate_price!
price = products.map(&:price).sum
save
end
end
class Product < ActiveRecord::Base
belongs_to :basket
after_save :update_basket
def update_basket
basket.recalculate_price!
end
end
and than I call it like this:
basket = Basket.new
basket.products.build(price)
basket.save
The problem: basket.price don't update.
I've investigated the problem and found out that there is some kind of caching in update_basket method. The problem could be solved with placing reload before basket.recalculate_price!.
Is there any option to leave the update_basket method untouched? I have many such cases in my application.
I would like to understand how it works and avoid similar problems in the future.
note: I recently upgraded rails from 3.2 to 4.2. In a previous version everything worked fine.
Is there any option to leave the update_basket method untouched?
Absolutely. You can place a condition on your after_create to avoid executing it some times:
after_save :update_basket if: :basked_should_be_updated
[...]
private
def basked_should_be_updated
[...]
# return true or false in here
end

Creating factories for has_many and belongs_to where validation is set on both sides of association

I have the following setup in my codebase. In such a case what would be the correct way to specify the factories so that we get to keep validation on both the models.
Models:
class LabTest < ActiveRecord::Base
has_many :lab_test_fields
validates_presence_of :lab_test_fields
end
class LabTestField < ActiveRecord::Base
belongs_to :lab_test
validates_presence_of :lab_test
end
Factories:
factory :lab_test do
lab_test_fields FactoryGirl.build_list(:lab_test_field, 5)
end
factory :lab_test_field do
lab_test
end
With this setup if I try to create lab_test or lab_test_field factory by doing FactoryGirl.create(:lab_test) or FactoryGirl.create(:lab_test_field), I get Trait not registered: lab_test (ArgumentError) which is quite unexpected.
From what I can see, you have a typo in the factory. This line...
lab_test_fields FactoryGirl.build_list(:lab_test_field, 5)
...is missing an "=" sign.
So it should be:
lab_test_fields = FactoryGirl.build_list(:lab_test_field, 5).
Also, if you're trying to create 5 lab_test_fields, you want to use create_list not build_list. Create will give you a persisted object, while build gives you an object that isn't persisted. Anyway, that missing "=" sign sounds like its why you're getting that error.
Hope this helps!

undefined method `price_tier' for nil:NilClass

Wondering if someone can help me find this issue. I'm using rails 4, ruby 2, and have spent alot of time trying different accessors, etc and nothing has worked.
The whole plan model:
class Plan < ActiveRecord::Base
has_many :users
end
Some of the user model:
class User < ActiveRecord::Base
...
validate :plan_type_valid
belongs_to :plan
...
def plan_type_valid
if free_ok
# the following line causes error
valid_plans = Plan.where(price_tier: plan.price_tier).pluck(:id)
else
valid_plans = Plan.where(price_tier: plan.price_tier).where.not(stripe_id: 'free').pluck(:id)
end
unless valid_plans.include?(plan.id)
errors.add(:plan_id, 'is invalid')
end
end
end
Here's a pastebin of the whole users controller:
http://pastebin.com/GnXz3R8k
the migration was all messed up because of a superuser issue and it wasn't able to create the extensions for hstore field type.

How can I validate that two associated objects have the same parent object?

I have two different objects which can belong to one parent object. These child objects can both also belong to each other (many to many). What's the best way to ensure that child objects which belong to each other also belong to the same parent object.
As an example of what I'm trying to do I have a Kingdom which has both many People and Land. The People model would have a custom validate which checks each related Land and error.adds if one has a mismatched kingdom_id. The Land model would have a similar validate.
This seems to work, but when updating it allows the record to save the 'THIS IS AN ERROR' error is in people.errors, however the Land which raised the error has been added to the People collection.
kingdom = Kingdom.create
people = People.create(:kingdom => kingdom)
land = Land.create(:kingdom_id => 999)
people.lands << land
people.save
puts people.errors.inspect # #messages={:base=>["THIS IS AN ERROR"]
puts people.lands.inspect # [#<Land id: 1...
Ideally I'd want the error to cancel the record update. Is there another way I should be going about this, or am I going in the wrong direction entirely?
# Models:
class Kingdom < ActiveRecord::Base
has_many :people
has_many :lands
end
class People < ActiveRecord::Base
belongs_to :kingdom
has_and_belongs_to_many :lands
validates :kingdom_id, :presence => true
validates :kingdom, :associated => true
validate :same_kingdom?
private
def same_kingdom?
if self.lands.any?
errors.add(:base, 'THIS IS AN ERROR') unless kingdom_match
end
end
def kingdom_match
self.lands.each do |l|
if l.kingdom_id != self.kingdom_id
return false
end
end
end
end
class Land < ActiveRecord::Base
belongs_to :kingdom
has_and_belongs_to_many :people
end
Firstly, the validation won't prevent the record from being added to the model's unpersisted collection. It will prevent the revised collection from being persisted to the database. So the model will be in an invalid state, and flagged as such with the appropriate errors. To see this, you can simply reload the people object.
You also have an error in your logic - the kingdom_match method will never return true even if no invalid kingdom_id's are found. You should add a line to fix this:
def kingdom_match
self.lands.each do |l|
return false if l.kingdom_id != self.kingdom_id
end
true
end
And you can make this validation a bit more concise and skip the kingdom_match method entirely:
def same_kingdom?
if self.lands.any?{|l| l.kingdom_id != self.kingdom_id }
errors.add(:base, 'THIS IS AN ERROR')
end
end

Testing has_many association with RSpec

I'm trying to test the Hour model with RSpec, namely the class method 'find_days_with_no_hours' that behaves like a scope. Business has_many Hours associated through STI.
find_days_with_no_hours needs to be called through a Business object and I can't figure out how to set this up in the RSpec test.
I want to be able to test something like:
bh = #business.hours.find_days_with_no_hours
bh.length.should == 2
I've tried various approaches, like creating a Business object (with, say, Business.create), then setting #business.hours << mock_model(BusinessHour, ..., ..., ...) but that doesn't work.
How is this normally done?
class Business < ActiveRecord::Base
has_many :hours, :as => :hourable
end
class Hour < ActiveRecord::Base
belongs_to :hourable, :polymorphic => true
def self.find_days_with_no_hours
where("start_time IS NULL")
end
end
You can't test an arel method by creating the object via mocks. Arel is going to go straight into the database, and not see any mocks or anything that you've created in memory. I would grab factory_girl and then define an hour factory for yourself:
Factory.define :hour do |f|
f.start_time {Time.now}
end
Factory.define :unstarted_day, :parent => :hour do |f|
f.start_time nil
end
And then in your test...
business = Factory.create(:business)
business.hours << Factory.create(:unstarted_day)
bh = business.hours.find_days_with_no_hours
bh.length.should == 1
However, factory_girl is just a personal preference for setting up known state, you can just as easily use create statements or fixtures, the problem for you was trying to use mock_model() (which prevents a database hit), and then using a method that queries the database.

Resources