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
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html,
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
http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html,
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 http://api.rubyonrails.org/classes/ActionView/Helpers/FormOptionsHelper.html

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
( http://guides.rubyonrails.org/form_helpers.html )

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