Parent model method is undefined - ruby-on-rails

I'm an absolute beginner to Ruby on Rails; I literally started last night. I'm following the Getting Started with Rails tutorial, although I've made a few modifications. Instead of a blog with posts and comments, I'm making a simple task-tracking application in which Projects have Tasks associated with them.
Everything was going quite well until step #9 (Deleting Comments). According to the tutorial, I should be able to get the parent model for a comment by calling comment.post, the analogue of which in my case is task.project. But when Rails tries to render the partial in which I've got task.project, it raises a NoMethodError:
undefined method `project' for #<Task:0x7fb0011cf058>
My models are as follows:
class Project < ActiveRecord::Base
validates :name, :presence => true
has_many :tasks, :dependent => :destroy
end
class Task < ActiveRecord::Base
validates :name, :presence => :true
belongs_to :project
end
Using the rails console, I determined that my tasks do have a project_id method which returns the ID of the associated project. Thus, I can get around the problem by using Project.find(task.project_id) instead of task.project in my partial. That seems "wrong," though, and makes me wonder if the relationship between my two models is somehow broken.
What's going on here?
Update: If I comment out the validates line in the Task model, then all of its methods work properly. I don't understand why that is the case, though.
Update 2: Figured it out. I was using the symbol :true instead of the simple boolean value in the validates line of my Task model. Had I paid more attention to the stack trace in the first place, I would have figured it out much sooner. What an embarrassing mistake!

Your code looks perfect. But if you were trying that into console try reloading first and then try again
reload!
task = Task.find(<record-id>)
task.project
It should be working.

Related

Why won't rails create associated records/objects from nested form using strong parameters?

I'm trying to create a record and it's associated records from a nested form using strong parameters. My primary model is:
class MaterialDonationRequest < ActiveRecord::Base
has_many :donation_items, dependent: :destroy
accepts_nested_attributes_for :donation_items, allow_destroy: true
validates :name, presence: true
attr_accessor :due_on_event, :date, :donation_items_attributes, :event_id
end
My associated (nested) model is:
class DonationItem < ActiveRecord::Base
validates :name, presence: true
belongs_to :material_donation_request
belongs_to :global_profile
validates :name, presence: true
attr_accessor :_destroy
end
In my material_donation_requests_controller.rb, I have the following for strong parameters:
def material_donation_request_params
params.require(:material_donation_request).permit(:name, :description, :event_flag, :due_on_event, :date, :event_id, donation_items_attributes: [:id, :name, :description, :amount, :_destroy])
end
Here's the line in my create method where I create the object:
#material_donation_request = MaterialDonationRequest.new(material_donation_request_params)
After doing this, #material_donation_request is created and populated correctly from the form. But the associated donation_items do not get created. For instance, in the debugger, when I enter #material_donation_request.donation_items.first, Rails returns nil.
For reference, here is what Rails returns for material_donation_request_params in the manual tests I'm running:
{"name"=>"Name", "description"=>"", "due_on_event"=>"true", "date"=>"", "donation_items_attributes"=>{"0"=>{"name"=>"", "amount"=>"1", "_destroy"=>""}, "1427122183210"=>{"name"=>"", "amount"=>"2", "_destroy"=>""}}}
Why isn't Rails creating the associated objects from the form as well? Everywhere I've looked, it seems like this structure should create everything, and a subsequent save should save everything (or at least throw validation errors as in this case-see update below). Is there something I'm missing?
Update
Since it was brought up in the answers, yes, the material_donation_params shown above would not pass validation. That's the scenario I've been manually testing. It should generate a validation error on save, but instead, simply saves the MaterialDonationRequest with no errors of any kind, and saves nothing to DonationItems.
To be clear, though, if I fill out the form completely and get the following material_donation_request_params:
{"name"=>"Name", "description"=>"", "due_on_event"=>"true", "date"=>"", "donation_items_attributes"=>{"0"=>{"name"=>"first", "amount"=>"1", "_destroy"=>""}, "1427122183210"=>{"name"=>"second", "amount"=>"2", "_destroy"=>""}}}
and then do #material_donation_request.save, it only saves the MaterialDonationRequest, and not any of the DonationItems.
Final Update
Okay. I've deleted my previous "final update" because what I wrote, and what I wrote in some of the comments was wrong. What ended up fixing this was not an update to Rails 4.1.8. I ran the bundle update command before actually saving the gem file with the new Rails version. So really, what ended up fixing this was simply updating all the gems that didn't have fixed version numbers. God only knows why things weren't working with the previous set of gems. Sorry that this isn't so helpful...
From Rails Validations guide
presence
This helper validates that the specified attributes are not empty. It uses the blank? method to check if the value is either nil or a blank string, that is, a string that is either empty or consists of whitespace.
You are requiring donation_item to be present, but your resulting params hash clearly has donation names blank, validation is failing. Calling save! when debugging these things can be helpful since it would throw a error on failure.
I figured out the answer. In total desperation, I upgraded my Rails version from 4.0.2 which is what I had been using, to 4.1.8. After doing this, with no other changes whatsoever (except gem dependencies, of course), it just started working the way it's supposed to. So I guess Rails 4.0.2 has a problem with nested forms and strong parameters.

Why does adding a bang make create work?

Rails convention of adding a bang to the end of a method makes it throw an exception if it fails. But my experience isn't matching that behavior.
My controller for a many-to-many relationship (an audit trail). The relationship object cannot be created, only updated by posting events to the audit trail object. (Which means you create by updating...)
I have a User object and a Foo object I'll call the relationship Bar.
bar=Bar.where(:user_id=>params[:user_id]).where(:foo_id=>params[:foo_id]).first
if bar
authorize! :update, bar
else
user=User.find(params[:user_id])
authorize! :bar_create, user
foo=Foo.find(params[:foo_id])
bar=Bar.create!(:user_id=>user.id, :foo_id=>foo.id)
end
The create method does not work. I debugged, and bar.save worked fine, but the entire point of create is to avoid having to make that second call to save. I experimented, and discovered that create! works just fine.
Edit:
As I continued on, I discovered that create! did not, in fact, always save. No errors in the underlying object, just mysteriously not saved.
I've had to do a create call followed by a save call, which... honestly, I just don't understand.
Edit: Per request, adding model code -- simplified to the relevant statements by removing unnecessary methods, validation calls, and the like. (While writing this, I noticed that I haven't yet added the has_many :through calls, but... doesn't seem like those should be relevant to the issue at hand.
class User < ActiveRecord::Base
has_secure_password
has_many :progresses
end
class Bar < ActiveRecord::Base
belongs_to :user
belongs_to :foo
has_many :bar_events
validates :user, :presence=>true
validates :foo, :presence=>true
scope :user_id, -> (user_id){ where user_id: user_id}
scope :foo_id, -> (foo_id){ where foo_id: foo_id}
end
class Foo < ActiveRecord::Base
end
There is a validation error or more in one of the associations in Bar foo or user. Try inspecting those objects:
bar=Bar.create!(:user_id=>user.id, :foo_id=>foo.id)
puts bar.errors.inspect, bar.user.errors.inspect, bar.foo.errors.inspect
That will print the errors of all those objects to the terminal running rails server. The only reason create would not save is due to validation errors in itself or nested associations.

Issue with collection `build` method record instantiation in Rails 4.1.1

I'm having a problem with the Rails collection.build(attrs) method, specifically with how the framework instantiates a new record. For example, here is a much simplified view of my models:
class Document < ActiveRecord::Base
belongs_to :account
has_many :descriptions, before_add: :id_test
validates :account, presence: true
def id_test
puts self.account_id
end
end
When I do something like:
current_account.documents.build(:descriptions => [desc])
then id_test prints nothing. That is, the account_id is not set in the before_add callback (and yes, I've tried the after_add callback as well; account_id is not set in that case either).
If I do:
d = current_account.documents.build
d.assign_attributes(:descriptions => [desc])
Then everything works as expected. However, I would prefer a better alternative, since this would be a pain to implement in the controllers...
Is there a way to get Rails to add the foreign_key first, or is there some better way to set this up? I haven't gone back to check for sure, but this seems different than the way Rails 3 evaluated collection.build statements.
EDIT
Looking through the Rails code a bit, I see I can do:
current_account.documents.build do |record|
record.assign_attributes(:descriptions => [desc])
end
and everything works as expected. Although a bit more verbose, I guess this is technically more accurate anyway.
I would suggest using
Document.build(:account_id => current_account.id, :descriptions => [desc])

Weird "Can't mass-assign protected attributes" error

Im running into the following error
Can't mass-assign protected attributes:
I know this is a generally asked question, but none of the answers provided seem to work for me. The thing is, i already have this 2 lines in my "parent" model:
accepts_nested_attributes_for
AND
attr_accessible :childmodel_attributes
I had run into this error in the past, and those 2 lines succefully solved the issue.. But they dont now. BTW, the "protected attributes" are ALL the fields in my "child" model, not just certain ones.
Hope someone can help me, im stuck and dont know what else to do.
This is the full error line:
Can't mass-assign protected attributes: linea, origen_comp, conector, char_ini, char_fin, modopremio_id, codigo_opc, ochar_ini, ochar_fin
This is what the "child_model" has
:consorcio_id, :productosacierto_id, :clave, :linea, :origen_comp, :conector, :char_inic, :char_fin, :modopremio_id, codigo_opc, :ochar_ini, :onchar_fin
(Sidenote, the "parent/child model" names are just for reference, they are not their true names (productosacierto AND productosregla) would be it.
Parent Model (Productosacierto)
has_many :productosregla
accepts_nested_attributes_for :productosregla, :reject_if => :all_blank, :allow_destroy => true
attr_accessible :productosregla_attributes, :producto_id, :consorcio_id, :clave, :descripcion, :una_condicion
default_scope order: 'id'
self.table_name = "hproductos_aciertos"
Child Model (Productosregla)
self.table_name = "hproductos_reglas"
belongs_to :productosacierto
attr_accessible :consorcio_id, :productosacierto_id, :clave, :linea, :origen_comp, :conector, :char_inic, :char_fin, :modopremio_id, codigo_opc, :ochar_ini, :ochar_fin
default_scope order: 'id'
If this is an exact copy of your child model, then I guess the comma at the end of the attr_accessible line is the problem!
I feel kind of embarrassed right now. There were several sintax errors on my model and thats what was causing this issue. I took it for granted that rails would throw a sintax error if there were some, specially when it's so delicate when it comes to writting code in the model...
This is what i had:
attr_accessible :consorcio_id, :productosacierto_id, :clave, :linea, :origen_comp, :conector, :char_inic, :char_fin, :modopremio_id, codigo_opc, :ochar_ini, :ochar_fin
This is what it was supposed to be:
attr_accessible :consorcio_id, :productosacierto_id, :clave, :linea, :origen_comp, :conector, :char_ini, :char_fin, :modopremio_id, :codigo_opc, :ochar_ini, :ochar_fin
As you can see, there was a ":" missing before the codigo_opc param, and also the char_ini param had an extra "c" that didnt belong. Thanks everyone who helped, this taught me to make sure never to assume anything.

Using RSpec to test active record scope that uses the includes method

Given the following two classes:
class Location < ActiveRecord::Base
belongs_to :holiday_schedule
validates :name, :presence => true, :uniqueness => {:case_sensitive => false}
scope :with_holiday_schedule, includes(:holiday_schedule)
end
class HolidaySchedule < ActiveRecord::Base
validates_presence_of :name
has_many :locations
end
How would you spec the with_holiday_schedule scope to ensure that accessing location.holiday_schedule.name in a loop will not cause the N+1 Query problem?
After positing to the RSpec users mailing list and reading more about speccing in general, I ultimately came to the realization that this isn't worth a unit test. The :includes directive is well tested in rails and the overhead of testing that simple line is higher than the risk associated with it failing or being removed by another developer - at least in my case.
What I really care about is performance of the application. Speccing performance would be a lot more productive than jumping through hoops to unit test this line.
Have a look at Counting the number of queries performed.
This should work perfectly in your solution.
They've done this:
ActiveRecord::Base.count_queries do
Ticket.first
end
You can use it this way in your spec:
queries = ActiveRecord::Base.count_queries do
location.with_holiday_schedule.holiday_schedule.name
end
queries.should_be == 1
I hope this will work.

Resources