I have this code in my model.
belongs_to :user, -> { includes :user_profile }
I went through shoulda doc i did not found any way to test that proc
containing includes part. Please help.
Assuming the model in question is named Car (you haven't posted the name of the enclosing class so I'm totally making this up) you can test it as:
describe 'user' do
let(:car) { create_car_somehow }
it 'preloads the user_profile association' do
expect(car.user.association(:user_profile)).to be_loaded
end
end
#association returns metadata of the specified association (of type ActiveRecord::Associations::BelongsToAssociation and so on). The returned object responds to #loaded? which returns true if the association has been loaded and false otherwise.
Related
I'm new doing unit test in Ruby on Rails, so I need some help in a test.
This is my model User.rb
class User < ApplicationRecord
belongs_to :created_by, foreign_key: :created_by_id, class_name: 'User'
end
I want to create a test to verify this association. I tried doing this on my user_spec.rb
describe 'should validates associations' do
subject { User.new }
it { should belong_to(subject.created_by) }
end
This is the error response
Failures:
1) User should validates associations should belong to
Failure/Error: it { should belong_to(subject.created_by) }
Expected User to have a belongs_to association called (no association > called )
# ./spec/models/user_spec.rb:17:in `block (3 levels) in '
The ActiveRecord shoulda matchers do not require any object of the class to be instantiated for the tests to run. Here, you have initialised a new User instance as the subject, and have tried to pass that to the shoulda matcher to check the belongs_to association.
However, in order to check a belongs_to association on a model with a particular foreign key and class name, the following test can be used:
it { should belong_to(:created_by).with_foreign_key(:created_by_id).class_name('User') }
ActiveRecord matchers take lots of other options apart from the two mentioned above. These options are very well documented in the Shoulda code on GitHub
You give an instance to matcher but it waits for the reference name and the referenced class name. Your test should be like following.
it { should belong_to(:created_by).of_type(User) }
From inside edit action in the contacts controller have ...
#programs << #contact.program
Which produces the following error:
NoMethodError - undefined method `<<' for Program::ActiveRecord_Relation
Contacts Model:
belongs_to :program
Program Model:
has_many :contacts
validates :name, presence: true, uniqueness: true
#programs.class
Program::ActiveRecord_Relation
#contact.program.class
Program(id: integer, name: string, active: boolean, created_at: datetime, updated_at: datetime)
Question: Why does this operation fail? Why can't the record get added to the record collection. What is preventing the collection(ActiveRecord_Relation) from adding the record?
You're contradicting yourself here:
Program has_many contacts vs Programs << Contact.program
If you're trying to add a Contact to a particular program, you would be looking at adding the contact:
program.contacts << contact
And if you're trying to set the program for the contact:
contact.program = program
What does not make sense, however, is to try to add something to “programs”, which isn't a relationship. Nothing in this system as you've defined it has_many :programs, so #programs.<< cannot possibly act on a relationship.
You're receiving this error because the ActiveRecord::Relation class is only a collection of results returned by an ActiveRecord query. You probably got it by running Program.where or a similar query. It is not an ActiveRecord::Association and therefore you cannot add more records to it.
You must instead use the association returned by the parent object.
Here's an example of what you're doing, vs what you should be doing:
class User < ApplicationRecord
has_many :programs
end
class Program < ApplicationRecord
belongs_to :user
end
new_program = Program.new
# What you're attempting.
programs_where = Program.where(user_id: User.first) # Class is Program::ActiveRecord_Relation
programs_where << new_program # Throws Error b/c << is not available on ActiveRecord::Relation objects.
# What you should be attempting.
user = User.first
programs_assoc = user.programs # Returns Programs::ActiveRecord_Associations_CollectionProxy
programs_assoc << new_program # Returns Correctly
Note: It's not clear how #programs is defined. Is this answer does not work for you then please provide the complete controller code, as well as the other model you're using.
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!
Basically: My model requires at least one instance of an associated model be present. Should I use validates_presence_of to assert this validation, or should I write some custom validation code?
Here are the essentials of my model:
class Event < ActiveRecord::Base
has_and_belongs_to_many :channels
validates_presence_of :channels, :message => "can't be empty"
end
(I assume things would be the same if I used has_many in place of has_and_belongs_to_many.)
Instead of the validates_presence_of line I could do this:
def validate
errors.add(:channels, "can't be empty") if channels.size < 1
end
I replaced the latter with the former in the Rails app I'm working on and am wondering if there might be any problems.
So to be more sure, I wrote the following rspec coverage, and both implementations respond the same:
describe Event do
before do
#net = Factory.create(:network)
#net_config = Factory.create(:network_config, :network => #net)
end
it "must have a channel" do
e = Factory.build(:event, :network => #net, :channels => [])
e.should have(1).error_on(:channels)
end
end
That is, if I remove the validation code, the above spec fails; if I put in either version of the validation code, the above spec passes.
So I might assume that my new implementation is ok. But I've read that validates_presence triggers a database load which, in turn, would wipe out any in-memory objects constructed from nested attributes. The proxy_target method, on the other hand, will return the in-memory objects without triggering a load. Some links on proxy_target: http://rubydoc.info/docs/rails/ActiveRecord/Associations/AssociationProxy http://withoutscope.com/2008/8/22/don-t-use-proxy_target-in-ar-association-extensions
In my particular case I'm not using ActiveRecord::Relation, but I wonder if I need to be cautious about this.
I've got a multi-level nested form using formtastic_cocoon (jquery version of formtastic).
I am trying to do some validation in the sense of
if value is_numeric do
insert into database
else do
database lookup on text
insert id as association
end
I was hoping tha the accepts_nested_attributes_for would have an :if option, but apparently there is only the :reject_if.
Is there a way to create a validation like I describe as part of the accepts_nested_attributes_for??
-----------------------Updated as per Zubin's Response ---------------------------
I believe Zubin is on the right track with a method, but I can't seem to get it working just right. The method I am using is
def lookup_prereq=(lookup_prereq)
return if lookup_prereq.blank?
case lookup_prereq
when lookup_prereq.is_a?(Numeric) == true
self.task_id = lookup_prereq
else
self.task = Task.find_by_title(lookup_prereq)
end
end
When I trigger this function, the self.task_id is being put in the database as '0' rather than the Task.id.
I'm wondering if I'm missing something else.
I'm not completely sure that the method is actually being called. Shouldn't I need to say
lookup_prereq(attr[:prereq_id)
at some point?
-------------------further edit -----------------------
I think from what I can find that the method is called only if it is named with the same name as the value for the database, therefore I've changed the method to
def completed_task=(completed_task)
Unfortunately this is still resulting in 0 as the value in the database.
Sounds like you need a method in your nested model to handle that, eg:
class Post < ActiveRecord::Base
has_many :comments
accepts_nested_attributes_for :comments
end
class Comment < ActiveRecord::Base
belongs_to :post
belongs_to :author
def lookup_author=(lookup_author)
return if lookup_author.blank?
case lookup_author
when /^\d+$/
self.author_id = lookup_author
else
self.author = Author.find_by_name(lookup_author)
end
end
end