Aggregated attribute changed but _was is changed too. So nothing is update in database

(Frenchy, sorry =) )

Hi everyone, i’ve got some troubles with aggregated attribute.

I created a point and path class which receive postgresql point and
path type data. Path object contains an array of point object.

I’m currently working on a “hunting” module in web browser based game,
and i need a crontab executed every n minutes to move the character on
the map. I’ve created a function which remove the first point from the
path and then save:

def one_step_forward!
path_will_change!
path.points.shift
end

But look at this rails console tests:

ruby-1.9.3-rc1 :001 > @hunting = Hunting.find(1)
Hunting Load (7.6ms) SELECT “huntings”.* FROM “huntings” WHERE
“huntings”.“id” = $1 LIMIT 1 [[“id”, 1]]
=> #<Hunting id: 1, path: “((7,6),(5,4))”, created_at: “2011-10-02
16:02:11”, updated_at: “2011-10-02 18:08:45”>

ruby-1.9.3-rc1 :006 > @hunting.path
=> #<Path:0xaabba1c @points=[#<Point:0xaabb968 @x=7, @y=6>, #<Point:
0xaabb8c8 @x=5, @y=4>]>

ruby-1.9.3-rc1 :002 > @hunting.one_step_forward!
=> #<Point:0xaebc058 @x=7, @y=6>

ruby-1.9.3-rc1 :003 > @hunting.changed
=> [“path”]

ruby-1.9.3-rc1 :004 > @hunting.changes
=> {“path”=>[#<Path:0xaebbd10 @points=[#<Point:0xaebbe8c @x=5,
@y=4>]>, #<Path:0xa189da4 @points=[#<Point:0xaebbe8c @x=5, @y=4>]>]}

As you can seen, the attribute “path” is changed, but the path_was
return me the new path value! So when i’m using the save method,
nothing is saved…

I tried few things, like @path_was = path before the path_will_change!

Thank you in advance for any help.

This issue is making me crazy… Look at this:

ruby-1.9.3-rc1 :071 > @hunting = Hunting.find(1)
Hunting Load (0.5ms) SELECT “huntings”.* FROM “huntings” WHERE
“huntings”.“id” = $1 LIMIT 1 [[“id”, 1]]
=> #<Hunting id: 1, path: “((7,6),(5,4))”, created_at: “2011-10-02
16:02:11”, updated_at: “2011-10-03 19:16:44”>
ruby-1.9.3-rc1 :072 > @hunting.path
=> ((7,6),(5,4))
ruby-1.9.3-rc1 :073 > @hunting.changed?
=> false
ruby-1.9.3-rc1 :074 > @hunting.changes
=> {}
ruby-1.9.3-rc1 :075 > @hunting.one_step_forward
=> (7,6)
ruby-1.9.3-rc1 :076 > @hunting.path
=> ((5,4))
ruby-1.9.3-rc1 :077 > @hunting.changed?
=> true
ruby-1.9.3-rc1 :078 > @hunting.changes
=> {“path”=>[((5,4)), ((5,4))]}
ruby-1.9.3-rc1 :079 > @hunting.changed_attributes
=> {“path”=>((5,4))}
ruby-1.9.3-rc1 :080 > @hunting.save
(0.2ms) BEGIN
(0.5ms) UPDATE “huntings” SET “path” = ‘((7,6),(5,4))’,
“updated_at” = ‘2011-10-03 19:30:15.467748’ WHERE “huntings”.“id” = 1
(23.5ms) COMMIT
=> true

Ok, path_was == path, don’t know why, but where the hell rails found
the ‘((7,6),(5,4))’ used in the SQL statement?

I found a way to “fix” the issue but i don’t really like it:
ruby-1.9.3-rc1 :118 > @hunting = Hunting.find(1)
Hunting Load (0.5ms) SELECT “huntings”.* FROM “huntings” WHERE
“huntings”.“id” = $1 LIMIT 1 [[“id”, 1]]
=> #<Hunting id: 1, path: “((7,6),(5,4))”, created_at: “2011-10-02
16:02:11”, updated_at: “2011-10-03 19:53:11”>
ruby-1.9.3-rc1 :119 > @hunting.one_step_forward
=> (7,6)
ruby-1.9.3-rc1 :120 > Hunting.update(@hunting.id, { :path =>
@hunting.path })
Hunting Load (0.5ms) SELECT “huntings”.* FROM “huntings” WHERE
“huntings”.“id” = $1 LIMIT 1 [[“id”, 1]]
(0.2ms) BEGIN
(1.1ms) UPDATE “huntings” SET “path” = ‘((5,4))’, “updated_at” =
‘2011-10-03 19:53:37.810688’ WHERE “huntings”.“id” = 1
(26.7ms) COMMIT
=> #<Hunting id: 1, path: “((5,4))”, created_at: “2011-10-02
16:02:11”, updated_at: “2011-10-03 19:53:37”>

On Mon, Oct 3, 2011 at 9:56 PM, Romain Maz BILLOIR
[email protected]wrote:

=> false

Strange, I would expect here:
=> {“path”=>[“((7,6),(5,4))”, “((5,4))”]}

ruby-1.9.3-rc1 :079 > @hunting.changed_attributes
=> {“path”=>((5,4))}

Strange, I would expect here:
=> {“path”=>“((7,6),(5,4))”}

ruby-1.9.3-rc1 :080 > @hunting.save
(0.2ms) BEGIN
(0.5ms) UPDATE “huntings” SET “path” = ‘((7,6),(5,4))’,
“updated_at” = ‘2011-10-03 19:30:15.467748’ WHERE “huntings”.“id” = 1
(23.5ms) COMMIT
=> true

What happens, when on line :075 of your example above, you
simply say

@hunting.path ‘((5,4))’

instead of the call to Hunting#one_step_forward and replay the rest of
your
example. Does it behave as expected then?

Are there any before_save hooks at play in your Hunting model?

Actually, I tried to replay your example (using a simple string
assignment)
in Rails 3.1.1.rc1 and found the different results for .changes and
.changed_attributes after the assignment, mentioned above.

004:0> j = Job.find(1)
Job Load (0.5ms) SELECT “jobs”.* FROM “jobs” WHERE “jobs”.“id” = $1
LIMIT
1 [[“id”, 1]]
=> #<Job id: 1, created_at: “2011-10-03 20:56:27”, updated_at:
“2011-10-03
21:02:56”, name: “foo”, path: “((7,6),(5,4))”>
005:0> j.path
=> “((7,6),(5,4))”
006:0> j.changed?
=> false
007:0> j.path = “((5,4))”
=> “((5,4))”
008:0> j.changed?
=> true
009:0> j.changes # this is different from your result and what I
expected
=> {“path”=>[“((7,6),(5,4))”, “((5,4))”]}
010:0> j.changed_attributes # this is the original value, as documented
=> {“path”=>“((7,6),(5,4))”}
011:0> j.path_was
=> “((7,6),(5,4))”
012:0> j.path
=> “((5,4))”
013:0> j.save
(0.1ms) BEGIN
(0.4ms) UPDATE “jobs” SET “path” = ‘((5,4))’, “updated_at” =
‘2011-10-03
21:06:21.336787’ WHERE “jobs”.“id” = 1
(0.7ms) COMMIT
=> true
014:0> j = Job.find(1)
Job Load (0.4ms) SELECT “jobs”.* FROM “jobs” WHERE “jobs”.“id” = $1
LIMIT
1 [[“id”, 1]]
=> #<Job id: 1, created_at: “2011-10-03 20:56:27”, updated_at:
“2011-10-03
21:06:21”, name: “foo”, path: “((5,4))”>

HTH,

Peter

ruby-1.9.3-rc1 :001 > @hunting = Hunting.find(1)
Hunting Load (7.7ms) SELECT “huntings”.* FROM “huntings” WHERE
“huntings”.“id” = $1 LIMIT 1 [[“id”, 1]]
=> #<Hunting id: 1, path: “((7,6),(5,4))”, created_at: “2011-10-02
16:02:11”, updated_at: “2011-10-03 20:01:45”>
ruby-1.9.3-rc1 :002 > @hunting.path = Path.new("((5,4))")
=> ((5,4))
ruby-1.9.3-rc1 :003 > @hunting.path
=> ((5,4))
ruby-1.9.3-rc1 :004 > @hunting.path_was
=> “((7,6),(5,4))”
ruby-1.9.3-rc1 :005 > @hunting.changes
=> {“path”=>["((7,6),(5,4))", ((5,4))]}
ruby-1.9.3-rc1 :006 > @hunting.save
(0.2ms) BEGIN
(0.7ms) UPDATE “huntings” SET “path” = ‘((5,4))’, “updated_at” =
‘2011-10-04 04:53:54.121491’ WHERE “huntings”.“id” = 1
(28.8ms) COMMIT
=> true

Works fine.

Actually, there is no “before_save” in my model.

On Mon, Oct 3, 2011 at 11:24 PM, Peter V.
[email protected]wrote:

=> ((7,6),(5,4))
ruby-1.9.3-rc1 :078 > @hunting.changes

What happens, when on line :075 of your example above, you
simply say

@hunting.path ‘((5,4))’

@hunting.path = ‘((5,4))’ # sorry, typo …