Added associations but don't see generated methods

Hi,

I've got a Rails app working that includes two two classes, etc.: Expense & Vendor. I eventually learned that the mental concept I had of their relationship should be express in Rails as:

class Expense < ActiveRecord::Base; belongs_to :vendor; end class Vendor < ActiveRecord::Base; has_many :expenses; end

http://rails.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html informs me that the following methods will be generated in Expense:

vendor vendor= vendor.nil

1. Are my expectations correct? 2. What do I have to do to get them generated? (I can't find any such methods defined anywhere.)

Thanks in Advance, Richard

Hi,

I've got a Rails app working that includes two two classes, etc.: Expense & Vendor. I eventually learned that the mental concept I had of their relationship should be express in Rails as:

class Expense < ActiveRecord::Base; belongs_to :vendor; end class Vendor < ActiveRecord::Base; has_many :expenses; end

http://rails.rubyonrails.org/classes/ActiveRecord/Associations/ClassM… informs me that the following methods will be generated in Expense:

vendor vendor= vendor.nil

1. Are my expectations correct?

yes (and there are some other methods too)

2. What do I have to do to get them generated? (I can't find any such methods defined anywhere.)

what makes you think they aren't there ?

Fred

Hi Richard,

http://rails.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html informs me that the following methods will be generated in Expense:

vendor vendor= vendor.nil

1. Are my expectations correct? 2. What do I have to do to get them generated? (I can't find any such methods defined anywhere.)

In the Rails console, try these:

Expense.methods Expense.public_methods Expense.private_methods

One of the things that takes some getting used to with Rails is how much it does a lot for you behind the scenes.

HTH, Bill

Hi Fred,

Thanks for your timely response.

what makes you think they aren't there ?

I searched for them to no avail. Shouldn't they be in the Expense model (app\models\expense.rb); or Expense controller (app\controllers\expenses_controller.rb?

I made a case-insensitive search for /vendor.nil\?/ in every application file whose name matched /e?rb/i to no avail.

Could it be that adding relationships after the app & the database have been created doesn't work? Would I have better luck if I scrapped everything and recreated the app with the relationships defined? IMHO, that would substantially limit the usefulness of relationship macros.

Thanks again, Richard

what makes you think they aren’t there ?

I searched for them to no avail. Shouldn’t they be in the

Expense model (app\models\expense.rb); or

Expense controller (app\controllers\expenses_controller.rb?

No, they are dynamically defined and exist in memory only at runtime. Welcome to dynamic objects and classes :slight_smile:

Try using script/console to execute them.

Cheers,

Andy

Hi Andy,

expense = Expense.find(1)

=> #<Expense id: 1, vendor: "Z01-2010.06.26 (Z01-Test)", [snip]

expense.vendor.nil?

=> true

quit

Welcome to dynamic objects and classes :slight_smile:

Thanks. This restores my faith in Rails and enhances my appreciation of newsgroups, especially this one. Now I've got to put them to use!

Best wishes, Richard

Welcome to dynamic objects and classes :slight_smile:

Thanks. This restores my faith in Rails and enhances my appreciation

of newsgroups, especially this one.

Now I’ve got to put them to use!

Good luck.

Post back if you have any more problems.

Cheers,

Andy

Post back if you have any more problems.

Thanks for your generous offer. So here's my last (I hope) stumbling block. I got:

NoMethodError in Expenses#new

Showing app/views/expenses/new.html.erb where line #14 raised:

undefined method `merge' for :nickname:Symbol

Extracted source (around line #14):

11: <%# New version of vendor selection -%> 12: <% params[:expense] = 10 -%> 13: <% @vendors = Vendor.find( :all, :order=>"nickname ASC") -%> 14: <%= f.collection_select(:vendor, :id, @vendors, :id, :nickname) %> 15: <%# End of New version -%> 16: 17: <%#= f.collection_select(:vendor, @vendors, :id, :nickname) %>

But the claim about nickname being bogus seems suspicious to me. For one thing:

Vendor.find(:first)

=> #<Vendor id: 62, nickname: "vendor 5/15/2010", qbname: "Test new field names", [snip]

And the arguments seem to agree with ActionView::Helpers::FormOptionsHelper, particularly the guidance for the public instant method:

collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})

Any ideas?

Thanks in Advance, Richard

Post back if you have any more problems.

Thanks for your generous offer. So here's my last (I hope) stumbling block. I got:

NoMethodError in Expenses#new

Showing app/views/expenses/new.html.erb where line #14 raised:

undefined method `merge' for :nickname:Symbol

Extracted source (around line #14):

11: <%# New version of vendor selection -%> 12: <% params[:expense] = 10 -%> 13: <% @vendors = Vendor.find( :all, :order=>"nickname ASC") -%> 14: <%= f.collection_select(:vendor, :id, @vendors, :id, :nickname) %> 15: <%# End of New version -%> 16: 17: <%#= f.collection_select(:vendor, @vendors, :id, :nickname) %>

But the claim about nickname being bogus seems suspicious to me. For one thing:

Vendor.find(:first)

=> #<Vendor id: 62, nickname: "vendor 5/15/2010", qbname: "Test new field names", [snip]

And the arguments seem to agree with ActionView::Helpers::FormOptionsHelper, particularly the guidance for the public instant method:

collection_select(object, method, collection, value_method, text_method, options = {}, html_options = {})

If you use collection_select on its own you need to pass the object, but f.collection_select inside a form_for takes the object from form_for.

The documentation is a bit confusing in that area.

Colin

Hi Bill,

I didn't notice your response a while ago. I did try out all three "method" displays. They all returned a slew of methods, even the most restrictive one, private_methods.

One of the strangest results to me is:

expense = Expense.find(:first)

=> #<Expense id: 1, vendor: "Z01-2010.06.26 (Z01-Test)", description: "Added new vendor", [snip]

expense.vendor

=> nil

Clearly the vendor attribute of the first expense record in not nil, as expense.vendor reports BTW, the vendor attribute represents the (artificial vendor name) rather than a vendor id because I've got another problem, which I reported in one of the other posts on this thread.

Thanks for weighing in here about my problem. Things are looking up.

Best wishes, Richard

Hey Colin,

If you use collection_select on its own you need to pass the object, but f.collection_select inside a form_for takes the object from form_for.

Awesome insight! Thanks. I've been trying for days to dig myself out of this blunder.

Idle question: Do you know whether that distinction is actually mentioned somewhere? I'll search for it in ActionView::Helpers::FormOptionsHelper

Serious question. I deleted the first argument (the object) and ran the app successfully: 1. created a new expense 2. created a new vendor for this expense 3. selected the new vendor from the drop-down list 4. saved the expense

The problem: The vendor field of the expense record is nil.. I expected a vendor_id there!

I need one last (I hope) idea. Some details below

Thanks in advance, Richard .

Here are the details I intended to add to my last post:

expense = Expense.find(:last)

=> #<Expense id: 5, vendor: nil, description: "Chose vendor 5/21/2010", category: "cat", account: "act", [snip]

Best wishes, Richard

Hey Colin,

Here's one more detail I should have added from my new-expense-view code:

    <%= f.label :vendor %><br />

    <%# New version of vendor selection -%>     <% params[:expense] = 10 -%>     <% @vendors = Vendor.find( :all, :order=>"nickname ASC") -%>     <%= f.collection_select(:id, @vendors, :id, :nickname) %>     <%# End of New version -%>

I apologize for the fragmented response. I was in a hurry to go out for my 3-mile walk :slight_smile:

Best again, Richard

Hey Colin,

Here's one more detail I should have added from my new-expense-view code:

&lt;%= f\.label :vendor %&gt;&lt;br /&gt;

&lt;%\# New version of vendor selection \-%&gt;
&lt;% params\[:expense\] = 10 \-%&gt;
&lt;% @vendors = Vendor\.find\( :all, :order=&gt;&quot;nickname ASC&quot;\) \-%&gt;
&lt;%= f\.collection\_select\(:id, @vendors, :id, :nickname\) %&gt;

because the first argument should be the name of the attribute you are trying to set (ie vendor_id). This and f.collection_select not needing the first argument was on the link I gave on one of your other posts ( Action View Form Helpers — Ruby on Rails Guides )

Fred

Hi Fred,

Thanks for taking the trouble to respond again, especially since I failed to follow your previous response. The problem then was that I was unsuccessful in applying it or didn't understand it ... probable both. I definitely failed to realize that the argument (object) was to be omitted in the context of form elements. That "little detail" seems missing in ActionView::Helpers::FormOptionsHelper. Someone explicitly mentioned that in a subsequent post on this thread.

Sadly, when I followed your suggestion "to the letter", I was unsuccessful again. I've got: ====== Error Msg ========= NameError in Expenses#new

Showing app/views/expenses/new.html.erb where line #13 raised:

undefined local variable or method `vendor_id' for #<ActionView::Base: 0x4822310>

Extracted source (around line #13):

10: 11: <%# New version of vendor selection -%> 12: <% @vendors = Vendor.find( :all, :order=>"nickname ASC") -%> 13: <%= f.collection_select(vendor_id, @vendors, :id, :nickname) %> 14: <%# End of New version -%> [snip] ====== Error Msg =========

So I tested whether vendor_id would be defined if I created an Expense instance in Rails console: Here's what I got (edited for brevity): K:\_Projects\Ruby\_Rails_Apps\_EIMS\RTS>ruby script/console Loading development environment (Rails 2.3.5)

expense = Expense.new

=> #<Expense id: nil, vendor: nil, description: nil, category: nil, [snip]

expense.vendor_id [NoMethodError] Expense.vendor_id [NoMethodError] params[:vendor_id] [NameError]

I also tried using the symbol :vendor_id as the first arg. That failed, too.

I apologize for being so obtuse. But I'd really like to get this app working, and getting this expense-vendor hookup working may be my last obstacle (for a while, anyway).

If you can give me a little more help about this problem, I'd be most appreciative. If you need more info, I'd be happy to post portions or all my code to a website from which you could download it for inspection.

Best wishes, Richard

Hi All,

I got closer. I tried:

    <% @vendors = Vendor.find( :all, :order=>"nickname ASC") -%>     <%= f.collection_select(:vendor, @vendors, :id, :nickname) %>

I ran the app and created a new expense. I clicked the drop-down list, selected a vendor, populated the remaining fields of the form and saved the expense. The app then crashed (that's the bad part), *but* the Request parameters were perfect, specifically: "vendor"=>"64"

So I saw the selected vendor's name when I clicked Save/Submit/ whatever and the generated expense record contained the id of the selected vendor, not the name of that vendor, as had been the case in some earlier versions of my code.

Below are some details of this crash.

Thanks for any help you may offer in resolving this matter.

Hi All,

I got closer. I tried:

<% @vendors = Vendor.find( :all, :order=>"nickname ASC") -%> <%= f.collection_select(:vendor, @vendors, :id, :nickname) %>

I ran the app and created a new expense. I clicked the drop-down list, selected a vendor, populated the remaining fields of the form and saved the expense. The app then crashed (that's the bad part), *but* the Request parameters were perfect, specifically: "vendor"=>"64"

So I saw the selected vendor's name when I clicked Save/Submit/ whatever and the generated expense record contained the id of the selected vendor, not the name of that vendor, as had been the case in some earlier versions of my code.

Below are some details of this crash.

Thanks for any help you may offer in resolving this matter. -- Richard

ActiveRecord::AssociationTypeMismatch in ExpensesController#create

Vendor(#37038620) expected, got String(#21132310)

It is always worth while trying to understand error messages, quite often they can give you a clue as the problem. The error above says that a Vendor object was expected, but you have given it a string. From the trace below you can see that it is at line 44 of expenses_controller.rb. Have a look at that line and see if you can work out why it is expecting a Vendor object, see what you have actually provided and see if you understand what is going on.

Colin

Well, I don't see anything wrong. stmt 44 in Expense controller creates new Expense instance passing it the content of the :expense item in the params hash, which is displayed in detail in the crash output.

The :expense item is itself a hash of all the values enter in the New Expense form. In particular, the vendor string key has the value string 65, which is indeed the vendor I select from the drop-down.

I restarted Mongrel with the --debugger argument, and Mongrel confirmed that the debugger is enabled. I then put a debugger statement immediately ahead of the offending statement, which thus became line# 45. However, when I refreshed the program in Firefox, the crash output still claimed that it crashed on line 44 (the debugger statement). But the command widow where I started Mongrel seemed to indicated that the app was stopped on line 45.

I issues "p params" in the command window and got the same display (except less neatly) as the hash I got in the crash dump.

Give all that, I still have no idea why the app crashed. Do you have any idea?

Best wishes, Richard

Firstly have I asked before? Please do not top post, it makes it much easier to follow the thread if you insert your comments at appropriate points in the previous post. Thanks.

Well, I don't see anything wrong. stmt 44 in Expense controller creates new Expense instance passing it the content of the :expense item in the params hash, which is displayed in detail in the crash output.

Please post that bit of code and a few lines around it, indicate which is that line please.

The :expense item is itself a hash of all the values enter in the New Expense form. In particular, the vendor string key has the value string 65, which is indeed the vendor I select from the drop-down.

I cannot see the hash in the trace that you posted. Am I right in understanding that in there you have an expense hash and that within that you have a vendor field containing the string "65". You say that you are constructing a new expense instance giving it the expense hash. Rails will see in there the vendor value ("65") and assume that you are trying to set the vendor field of the expense to 65. I am assuming now that the expense table does not have a field called vendor, but that you have expense belongs_to vendor. It therefore expects to find a vendor object in the hash and instead finds a string. Hence the error message. Does that make sense? Is it possibly expense.vendor_id that you are trying to set to 65, assuming that the table has a vendor_id column.

Colin

Hi Fred,

Thanks for taking the trouble to respond again, especially since I failed to follow your previous response. The problem then was that I was unsuccessful in applying it or didn't understand it ... probable both. I definitely failed to realize that the argument (object) was to be omitted in the context of form elements. That "little detail" seems missing in ActionView::Helpers::FormOptionsHelper. Someone explicitly mentioned that in a subsequent post on this thread.

It's definitely in the form helpers guide - well worth reading (there's also a section on collection_select and association type mismatch errors)

I also tried using the symbol :vendor_id as the first arg. That failed, too.

That's what you should be doing - how did it fail?

Fred