ActiveRecord - <%= and <% yielding different records

I have a problem of gut-wrenching proportions that can easily be
illustrated by the following snippit of template code:

<% member=Member.find(params['id']) %>
<p>ID: <%= member.id %></p>
<% outfile=File.open('/tmp/look1','w')
    outfile.puts(member.id)
    outfile.close %>

The correct id is properly displayed within the paragraph element.
However, the id that is sent to the disk file is incorrect in that,
for some unknown reason, it is the id of the very first record in the
database. If I replace '.id' with '.inspect' in the 3ed to the last
line, the disk file confirms that it is actually accessing the first
record in the DB. I can't figure out why that is. It's like a bad
dream from which I expect to awake any moment. I do hope that it
hasn't been a long day and that I'm doing something really stupid.

Thanks for any input.

         ... doug

Hi Doug,

I have a problem of gut-wrenching proportions that can easily be
illustrated by the following snippit of template code:

For starters, and please don't be offended, but other than the
<p>...</p> line, none of this code belongs in your view template.

<% member=Member.find(params['id']) %>

The find belongs in your controller or model and 'member' and its result
should be passed 'up' to your view as an instance variable: @member.

It's not possible to tell from the code you've posted, but there are
lots of ways you can run into problems with the approach you're taking.
The params hash is passed into your controller from a view. It may or
may not survive your controller/model processing for use in the next
view's rendering.

Also, the use of strings as keys in the params hash has been deprecated
for some time; replaced with symbols: e.g., params[:id]. Also, the
params hash contains strings. The find method is looking for an
integer. So you'll be much safer using params[:id].to_i

<p>ID: <%= member.id %></p>

<% outfile=File.open('/tmp/look1','w')
    outfile.puts(member.id)
    outfile.close %>

The output to the file system definitely belongs in your controller or
view.

The correct id is properly displayed within the paragraph element.

This is most likely an accident.

However, the id that is sent to the disk file is incorrect in that,
for some unknown reason, it is the id of the very first record in the
database. If I replace '.id' with '.inspect' in the 3ed to the last
line, the disk file confirms that it is actually accessing the first
record in the DB. I can't figure out why that is. It's like a bad
dream from which I expect to awake any moment. I do hope that it
hasn't been a long day and that I'm doing something really stupid.

Thanks for any input.

I'd recommend you seperate the code above more appropriately.

Try this for starters. In your controller...

def whatever
@member = Member.find(params[:id].to_i)
outfile = File.open('/tmp/look1','w')
outfile.puts(@member.id)
outfile.close
end

Personally, for debugging purposes, I'd get rid of the file processing and just to a puts to STDIO; e.g.:

@member...
puts "@member.id = #{@member.id}"

It'll save you time.

In your view, keep it to...

<p>I saved a member record with ID = <%= @member.id %></p>

See where that takes you.

HTH,
Bill

All of the above suggestions may be valid, however it does not explain
how, once the OP has managed by fair means or foul to get hold of the
member object that it manages to mutate between his two uses of it.

Colin

Hi Colin,

All of the above suggestions may be valid, however it does not explain
how, once the OP has managed by fair means or foul to get hold of the
member object that it manages to mutate between his two uses of it.

OK. I want to jump back in here to make sure that Bill doesn't think
that he did offend me with his comments. He definitely did not. I
welcome all of the help that I can get and Bill's comments are not
only valid but contain a lot of help. None the less, I also agree
with you, Colin, in that Bill's comments really offer no explanation
as to how the object manages to mutate between the two uses of it.
What I'm trying to do at the moment is to clean things up by following
Bill's suggestions. It's much easier to troubleshoot in the
controller than it is in the view. I expect that by using that
approach I will ultimately get things to work as desired.
Unfortunately, the downside of that approach is that I may never
understand why my code (bad as it may have been) didn't work. That's
a bit unsettling. Anyway, I continue to work on this issue. I will
report back to the list once I have something to report. As always,
thanks to all for the help.

          ... doug

All of the above suggestions may be valid, however it does not explain
how, once the OP has managed by fair means or foul to get hold of the
member object that it manages to mutate between his two uses of it.

OK. I want to jump back in here to make sure that Bill doesn't think
that he did offend me with his comments. He definitely did not. I
welcome all of the help that I can get and Bill's comments are not
only valid but contain a lot of help. None the less, I also agree
with you, Colin, in that Bill's comments really offer no explanation
as to how the object manages to mutate between the two uses of it.
What I'm trying to do at the moment is to clean things up by following
Bill's suggestions. It's much easier to troubleshoot in the
controller than it is in the view.

In case you did not know it is possible to use ruby-debug in the view
by inserting <% debugger %> at the appropriate point, Variables can
then be inspected. This assumes you have setup ruby-debug of course.

Colin

Hi Doug,

> All of the above suggestions may be valid, however it does not explain
> how, once the OP has managed by fair means or foul to get hold of the
> member object that it manages to mutate between his two uses of it.

OK. I want to jump back in here to make sure that Bill doesn't think
that he did offend me with his comments. He definitely did not. I
welcome all of the help that I can get and Bill's comments are not
only valid but contain a lot of help.

I'm glad to see you're back.

None the less, I also agree with you, Colin, in that Bill's comments
really offer no explanation as to how the object manages to mutate
between the two uses of it.

I'll bet you a nickle that that is not happening. Objects do not
'mutate' on their own. We've been pretty successful at eliminating the
old 'branch on occasion' flow control :wink: I'm often reminded of a
little ditty I learned in my first CS class.

I hate this damn computer.
I think I'm gonna sell it.
It never does just what I want,
But only what I tell it.

What I'm trying to do at the moment is to clean things up by following
Bill's suggestions. It's much easier to troubleshoot in the
controller than it is in the view. I expect that by using that
approach I will ultimately get things to work as desired.

I believe you will. And if not, it will be much easier for us to help
you.

Unfortunately, the downside of that approach is that I may never
understand why my code (bad as it may have been) didn't work. That's
a bit unsettling.

Given that you're willing to invest the effort to learn to write Rails
code, as opposed to code that happens to be 'wrapped' in the Rails
framework, I'm more than willing to invest in helping you understand why
your previous effort did not work. My guess, however, is that by the
time you 'get things to work as desired', you will have figured out on
your own why your previous effort did not work.

If not, whenever you're ready, re-post the original code. If you do,
please post the entire controller method that renders the view in
question along with any filters and library includes / requires you use
in the controller, the model, and the view template in its entirety.

Anyway, I continue to work on this issue. I will
report back to the list once I have something to report. As always,
thanks to all for the help.

Looking forward to hearing back, and you're welcome.

Best regards,
Bill

THE GOOD NEWS: I followed Bill's suggestions for cleaning things up
and when I finished it all worked! :slight_smile:

THE BAD NEWS: I still have no idea why my original code (bad as it
may have been) didn't work.

My guess, however, is that by the
time you 'get things to work as desired', you will have figured out on
your own why your previous effort did not work.

Unfortunately that didn't happen.

I'm more than willing to invest in helping you understand why
your previous effort did not work.

Bill, I really appreciate your willingness to help. I truly believe
that we could get to the bottom of it and I also don't really believe
that objects mutate between uses. However, it sure SEEMED like that
was what was happening. That's why I said that it was like a bad
dream. Things like that only happen in dreams. In any event, at this
point it would be difficult for me to go back and investigate that old
code. I agree with you that there is some explanation.
Unfortunately, I just don't know what it is. Since I plan on
following your suggestions in the future and avoid writing that kind
of code altogether, I'm not sure that it would be worth the effort for
us to pursue this. So, with your permission I would just like to
leave it as one of life's great unsolved mysteries.

Thanks to all who contributed.

           ... doug

THE GOOD NEWS: I followed Bill's suggestions for cleaning things up
and when I finished it all worked! :slight_smile:

Congrats!

THE BAD NEWS: I still have no idea why my original code (bad as it
may have been) didn't work.

> My guess, however, is that by the
> time you 'get things to work as desired', you will have figured out on
> your own why your previous effort did not work.

Unfortunately that didn't happen.

> I'm more than willing to invest in helping you understand why
> your previous effort did not work.

Bill, I really appreciate your willingness to help. I truly believe
that we could get to the bottom of it and I also don't really believe
that objects mutate between uses. However, it sure SEEMED like that
was what was happening. That's why I said that it was like a bad
dream. Things like that only happen in dreams. In any event, at this
point it would be difficult for me to go back and investigate that old
code. I agree with you that there is some explanation.
Unfortunately, I just don't know what it is. Since I plan on
following your suggestions in the future and avoid writing that kind
of code altogether, I'm not sure that it would be worth the effort for
us to pursue this. So, with your permission I would just like to
leave it as one of life's great unsolved mysteries.

As you wish. My personal guess is that it would be a worthwhile effort.
We learn more from our mistakes than from our successes. WRT 'difficult
to go back', I hope you don't mean that you don't have a source code
control system in place. If that is the situation, I strongly recommend
you invest the time to correct that. Github is free. SVN is free.
Being unable to revert to a prior code state can be very, very
expensive.

Best regards,
Bill

Guess what? IT'S BACK! A variant of the problem has re-surfaced.
I'll try to get something together tomorrow to illustrate the issue.
Of course, now that I have things better organized, I may find the
solution in the process. One way or another, I'm hoping that we'll be
able to get to the bottom of this.

Thanks.

          ... doug

OK. Here's my controller code (line numbers assigned for
convenience):

1 def update()
2 outfile=File.open('/tmp/look1','w')
3 outfile.puts(params.inspect)
4 outfile.close
5 @member=Member.find(params[:id].to_i)
6 outfile=File.open('/tmp/look2','w')
7 outfile.puts(@member.inspect)
8 outfile.close
9 profile=Member.profile(@member.flags)
10 outfile=File.open('/tmp/look3','w')
11 outfile.puts(profile.inspect)
12 outfile.close
13 # @profile=profile
14 end

As you can see, aside from the code which writes crucial data to
respective disk files for debugging purposes, there are really only 3
lines of code in the controller, line 5, line 9, and line 13 which is
commented out. You may have noticed that lines 9 and 13 can easily be
combined into a single line. I broke them apart to make clear that
the problem is not in the model code.

I invoke the code by entering 'http://my_host/members/update/15' into
my browser.

With line 13 commented out, everything seems to work perfectly. Under
that condition, here is what the contents of my debugging files looks
like:

$ cat /tmp/look1
{"action"=>"update", "id"=>"15", "controller"=>"members"}
$ cat /tmp/look2
#<Member id: 15, login: "bob1", password: "ppss", flags: 2,
first_name: "Bob", last_name: "Chaput", email: "bob@rchapix.com",
courtesy_title: "Mr.", created_at: nil, updated_at: "2009-08-09
23:40:18">
$ cat /tmp/look3
[["1", "Members List"], ["0", "Other List 1"], [0, "Other List 2"]]

The above results are exactly what is expected.

Where the trouble arises is when I uncomment line 13 and pass the
profile data to the view via the @profile instance variable. When I
do that, things get all screwed up. Here is the output of the
debugging files under that circumstance:

$ cat /tmp/look1
{"format"=>"gif", "action"=>"update", "id"=>"SpryMenuBarRightHover",
"controller"=>"members"}
$ cat /tmp/look2
#<Member id: 0, login: "admin", password: "secret", flags: 0,
first_name: "Admin", last_name: "Admin", email: "foo@bar.com",
courtesy_title: "Mr.", created_at: nil, updated_at: "2009-08-09
18:03:42">
$ cat /tmp/look3
[["0", "Members List"], [0, "Other List 1"], [0, "Other List 2"]]

So, somehow passing the profile data to the view via the @profile
instance variable causes things to get screwed up. It appears that
the cause of the problem is that for reasons unknown to me, the params
are now totally wrong. That's the culprit. I just don't know what's
causing it. Any ideas?

Although I'm not sure what it has to do with the issue, here is my
relevant view code:

<select name="flags[]" multiple="multiple" size="4" style="display:
block; margin-left: auto; margin-right: auto;">
  <% index=0
        value=1
        @profile.each do |option| %>
  <%= "<option value=\"#{value}\" #{"selected='selected'" if option
[0].to_i==1}>#{option[1]}</option>" %>
  <% index+=1
        value=2*value
        end %>
</select>

Thanks for any input. I'm totally at a loss:

          ...doug

1 def update()
2 outfile=File.open('/tmp/look1','w')
3 outfile.puts(params.inspect)
4 outfile.close
5 @member=Member.find(params[:id].to_i)
6 outfile=File.open('/tmp/look2','w')
7 outfile.puts(@member.inspect)
8 outfile.close
9 profile=Member.profile(@member.flags)

Without seeing your models this is a WAG, but is #profile really a
class method? If a Profile belongs to a Member, I'd expect to see
something like

            profile = @member.profile

10 outfile=File.open('/tmp/look3','w')
11 outfile.puts(profile.inspect)
12 outfile.close
13 # @profile=profile
14 end

FWIW,

Thanks for the input. I *THINK* that I have it right; but, just to
avoid any possible confusion, here is my relevant model code:

class Member < ActiveRecord::Base
  def self.profile(decimal_profile)
    profile=['Members List','Other List 1','Other List 2']
    binary_profile=decimal_profile.to_s(2).split(//)
    outArray=Array.new
    profile.length.times do |index|
      outArray[index]=[binary_profile.fetch(index,0),profile[index]]
    end
    return outArray
  end
end

[Taking things slightly out of order...]

Thanks for any input. I'm totally at a loss:

Me too. At a loss, that is. So let me make sure I understand...

Where the trouble arises is when I uncomment line 13 and pass the
profile data to the view via the @profile instance variable. When I
do that, things get all screwed up. Here is the output of the
debugging files under that circumstance:

Let me make sure I understand what you're doing \ expecting via
the request \ response cycle as it applies to Rails.

The cycle starts when a request is made from the browser to a method in
a controller in the Rails app. In the simplest case, the controller
grabs data via its associated model and then passes that data via instance
variables to the associated view templates to render a response page.

Where the above doesn't make sense to me is that it implies that your
update() method is called from some page, renders a new page which then
calls it again. Is that right? If not, which cycle is the view
containing the select below involved in? Is it feeding update(), or
being generated via update() ?

OK. Here's my controller code (line numbers assigned for
convenience):

1 def update()

What is the purpose of the parens above?

2 outfile=File.open('/tmp/look1','w')
3 outfile.puts(params.inspect)
4 outfile.close
5 @member=Member.find(params[:id].to_i)
6 outfile=File.open('/tmp/look2','w')
7 outfile.puts(@member.inspect)
8 outfile.close
9 profile=Member.profile(@member.flags)
10 outfile=File.open('/tmp/look3','w')
11 outfile.puts(profile.inspect)
12 outfile.close
13 # @profile=profile
14 end

As you can see, aside from the code which writes crucial data to
respective disk files for debugging purposes, there are really only 3
lines of code in the controller, line 5, line 9, and line 13 which is
commented out. You may have noticed that lines 9 and 13 can easily be
combined into a single line. I broke them apart to make clear that
the problem is not in the model code.

I invoke the code by entering 'http://my_host/members/update/15' into
my browser.

What do you mean 'entering'? An update should be a POST or better yet a
PUT of a params hash that would come from a form. I don't understand
'entering'.

With line 13 commented out, everything seems to work perfectly. Under
that condition, here is what the contents of my debugging files looks
like:

$ cat /tmp/look1
{"action"=>"update", "id"=>"15", "controller"=>"members"}
$ cat /tmp/look2
#<Member id: 15, login: "bob1", password: "ppss", flags: 2,
first_name: "Bob", last_name: "Chaput", email: "bob@rchapix.com",
courtesy_title: "Mr.", created_at: nil, updated_at: "2009-08-09
23:40:18">

This is an inappropriate use of the update method. Should be using
'show' here since all you're doing is GETting information about the
member.

$ cat /tmp/look3
[["1", "Members List"], ["0", "Other List 1"], [0, "Other List 2"]]

The above results are exactly what is expected.

Where the trouble arises is when I uncomment line 13 and pass the
profile data to the view via the @profile instance variable. When I
do that, things get all screwed up. Here is the output of the
debugging files under that circumstance:

$ cat /tmp/look1
{"format"=>"gif", "action"=>"update", "id"=>"SpryMenuBarRightHover",
"controller"=>"members"}
$ cat /tmp/look2
#<Member id: 0, login: "admin", password: "secret", flags: 0,
first_name: "Admin", last_name: "Admin", email: "foo@bar.com",
courtesy_title: "Mr.", created_at: nil, updated_at: "2009-08-09
18:03:42">
$ cat /tmp/look3
[["0", "Members List"], [0, "Other List 1"], [0, "Other List 2"]]

So, somehow passing the profile data to the view via the @profile
instance variable causes things to get screwed up. It appears that
the cause of the problem is that for reasons unknown to me, the params
are now totally wrong. That's the culprit. I just don't know what's
causing it. Any ideas?

Although I'm not sure what it has to do with the issue, here is my
relevant view code:

What do you mean 'relevant'? What's the name of this template? And, at
a minimum, post the <%= form... %> line so we can understand the flow.

<select name="flags[]" multiple="multiple" size="4" style="display:
block; margin-left: auto; margin-right: auto;">
  <% index=0
        value=1
        @profile.each do |option| %>
  <%= "<option value=\"#{value}\" #{"selected='selected'" if option
[0].to_i==1}>#{option[1]}</option>" %>
  <% index+=1
        value=2*value
        end %>
</select>

Tell us about the other view templates and controller methods that are
involved here. Also, please post your Member model.

Best regards,
Bill

I just wanted to mention that I think that this issue has boiled down
to, "Why are the params different?" When we know that, I think that
we'll have the answer. Thanks again.

            ... doug

The params hash get passed from the view to the controller. So the
question is... you've shown a controller method. Is that method being
called from the view you showed? Or generating the values passed to the
view template?

Some of your post implies the former, while others imply the latter.
Please post the view template that calls the method as well as the view
template that's rendered by the method. The former would typically be
edit.rhtml while the latter whould be update.rhtml. But post whatever
you're actually using. The request / response flow is what we need to
understand to help you.

Best regards,
Bill

Thanks for the input. I *THINK* that I have it right; but, just to
avoid any possible confusion, here is my relevant model code:

Yeah, I'm pretty sure that's not what you want :slight_smile:

Consider the following:

ripple:~$ irb

class Foo
def self.profile
  "something"
end
def profile
  "otherwise"
end
end

=> nil

foo = Foo.new

=> #<Foo:0x1258040>

Foo.profile

=> "something"

foo.profile

=> "otherwise"

You'd probably do well to open up a console and try experimenting
directly with Member.profile and variations. Or at least, that usually
helps me get my thinking around a problem.

:fwiw

The params hash get passed from the view to the controller.

Really? I'm definitely thinking the other way around. Let's think
about this for a minute. In the URL box of my browser I enter 'http://
my_host/members/update/15' and press enter. I would expect this
request to be routed to the update action of the members controller
and I would expect there to be an :id=>'15' entry available in params
for that action to use along with an :action=>'update' entry and
the default update view which is the view that I showed. As far as I
know, that's exactly what happens when I have the @profile=profile
line in my controller commented out. However, when I uncomment that
line, for reasons unknown to me, I get a totally different set of
parameters available in my action even though I used exactly the same
URL in my browser. It blows my mind! :slight_smile:

So, to answer your question:

you've shown a controller method. Is that method being
called from the view you showed? Or generating the values passed to the
view template?

It is the later, i.e., the controller method generates the values
passed to the view template.

The request / response flow is what we need to
understand to help you.

I hope that my response in this post has clarified the issue. If not,
please let me know. Thanks.

           ... doug

You'd probably do well to open up a console and try experimenting
directly with Member.profile and variations.

Well, unfortunately, I have already done that. Member.profile seems
to behave exactly as expected in a variety of cases tested. Also, if
you look at the debug data you can see that the corruption occurs long
before Member.profile is even called. That's what makes this so
puzzling. The params die is cast; but, the result of casting that die
is determined by something that happens much later. Is this voodoo or
what? Thanks.

         ... doug

> The params hash get passed from the view to the controller.

Really? I'm definitely thinking the other way around. Let's
think about this for a minute.

OK.

In the URL box of my browser I enter 'http://
my_host/members/update/15' and press enter. I would expect this
request to be routed to the update action of the members controller
and I would expect there to be an :id=>'15' entry available in params
for that action to use along with an :action=>'update' entry and
a :controller=>'members' entry.

That is correct. All three 'entries' are members of the params hash. I
should have worded my statement more carefully. The params hash is
passed from the page rendered by the view in the *last* request /
response cycle, to the controller / method as the start of the *current*
request / response cycle. Instance variables get passed from the
controller to the view templates in the current request / response cycle
to render the response page. I apologize if my use of the word 'view'
confused.

The action does its thing and renders the default update view which is
the view that I showed. As far as I know, that's exactly what happens
when I have the @profile=profile line in my controller commented out.

Yes.

However, when I uncomment that line, for reasons unknown to me, I get a
totally different set of parameters available in my action even though
I used exactly the same URL in my browser. It blows my mind! :slight_smile:

Commenting or uncommenting that line has nothing at all to do with what
the controller is receiving in the params hash from a request initiated
via a page rendered in the last cycle. It has only to do with what the
view template will render in the current cycle. And if, in fact, the
view you showed is update.rhtml then that view will throw an error on
nil.each if you comment out line 13. You haven't mentioned that. Is it
happening or not?

This is too hard to follow in fits and spurts. Zip your app up and send
it to me offline. I'll take a look and get back to you with the
explanation.

Bill
bwalton dot im at gmail dot com