ranked-model icon indicating copy to clipboard operation
ranked-model copied to clipboard

RankedModel::InvalidField for perfectly valid ActiveRecord instance

Open JeanMertz opened this issue 11 years ago • 8 comments

I'm seeing some weird RankedModel behavior:

ruby 2.0.0p195 (2013-05-14 revision 40734) [x86_64-darwin12.3.0]
rails (4.0.0.rc1)
ranked-model (0.2.0)
class Column < ActiveRecord::Base
  include RankedModel
  ranks :display, with_same: :board_id

  belongs_to :board
  scope :ordered, -> { rank(:display) }
end
$ b = Board.create
=> #<Board id: 2, title: nil, data: {}, created_at: "2013-05-18 06:25:41", updated_at: "2013-05-18 06:25:41">

$ c1 = Column.create(title: 'c1', board: b)
=> #<Column id: 10, title: "c1", display: 0, board_id: 2, data: {}, created_at: "2013-05-18 06:26:04", updated_at: "2013-05-18 06:26:04">

$ c2 = Column.create(title: 'c2', board: b)
RankedModel::InvalidField: No field called "board_id" found in model

If I pry what's happening, I see the following:

$ c1 = Column.create(title: 'c1', board: b)
$ instance.respond_to?(ranker.with_same)
=> true
$ instance
=> #<Column id: nil, title: "c1", display: 0, board_id: 2, data: {}, created_at: nil, updated_at: nil>
$ exit
=> #<Column id: 10, title: "c1", display: 0, board_id: 2, data: {}, created_at: "2013-05-18 06:26:04", updated_at: "2013-05-18 06:26:04">

$ c2 = Column.create(title: 'c2', board: b)
$ instance.respond_to?(ranker.with_same)
=> true
$ instance
=> #<Column id: nil, title: "c2", display: 0, board_id: 2, data: {}, created_at: nil, updated_at: nil>
$ exit
#  Column Load (0.9ms)  SELECT "columns"."id", "columns"."display" FROM "columns"
# WHERE "columns"."board_id" = 2 AND "columns"."display" = 0
# ORDER BY "columns"."id" ASC LIMIT 1
$ instance.respond_to?(ranker.with_same)
=> false
$ instance
=> #<Column id: 10, display: 0>
$ exit
RankedModel::InvalidField: No field called "board_id" found in model

What seems to be happening is that on creating the second column, it actually checks the other columns to see if things need to shift around. However, the query (which I added in the stack trace above) only selects the id and display fields, leaving out the board_id field. This in return results in instance.respond_to?(ranker.with_same) failing.

This seems like too big a bug to have gone unnoticed for so long, so I fully expect the problem to be on my end, but I can't figure out what could be causing this. I am not doing anything special.

JeanMertz avatar May 18 '13 06:05 JeanMertz

For completeness sake, here's the full (unmodified) backtrace:

2.0.0 (main):0 > b = Board.create
   (0.2ms)  BEGIN
  SQL (6.6ms)  INSERT INTO "boards" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id"  
[["created_at", Sat, 18 May 2013 06:41:50 UTC +00:00], ["updated_at", Sat, 18 May 2013 06:41:50 UTC +00:00]]
   (1.3ms)  COMMIT
=> #<Board id: 5, title: nil, data: {}, created_at: "2013-05-18 06:41:50", updated_at: "2013-05-18 06:41:50">

2.0.0 (main):0 > c1 = Column.create(title: 'c1', board: b)
   (0.3ms)  BEGIN
  Column Load (1.1ms)  SELECT "columns"."id", "columns"."display" FROM "columns" WHERE 
"columns"."board_id" = 5 AND "columns"."display" = 0 ORDER BY "columns"."id" ASC LIMIT 1
  SQL (1.0ms)  INSERT INTO "columns" ("board_id", "created_at", "title", "updated_at") VALUES ($1, $2, $3, $4) 
RETURNING "id"  [["board_id", 5], ["created_at", Sat, 18 May 2013 06:41:52 UTC +00:00], ["title", "c1"], 
["updated_at", Sat, 18 May 2013 06:41:52 UTC +00:00]]
   (1.3ms)  COMMIT
=> #<Column id: 13, title: "c1", display: 0, board_id: 5, data: {}, created_at: "2013-05-18 06:41:52",
updated_at: "2013-05-18 06:41:52">

2.0.0 (main):0 > c2 = Column.create(title: 'c2', board: b)
   (0.2ms)  BEGIN
  Column Load (0.6ms)  SELECT "columns"."id", "columns"."display" FROM "columns" WHERE 
"columns"."board_id" = 5 AND "columns"."display" = 0 ORDER BY "columns"."id" ASC LIMIT 1
   (0.3ms)  ROLLBACK
RankedModel::InvalidField: No field called "board_id" found in model
from /Users/Jean/.rbenv/versions/2.0.0-p195/gemsets/flow/gems/ranked-model-0.2.0/
lib/ranked-model/ranker.rb:47:in `validate_ranker_for_instance!'

JeanMertz avatar May 18 '13 06:05 JeanMertz

Adding the with_same columns to the select optimization from ranker.rb#L226 solved the issue and everything seems to be working still. This helps me to at least continue until you find the time to comment on this so we can possibly find out what the real cause is (because I still don't believe this has never come up before, so I expect it to be some edge case on my end).

+ _select = [instance_class.arel_table[:id], instance_class.arel_table[ranker.column]]
+ _select.concat [ranker.with_same].flatten if ranker.with_same
- _finder.order(instance_class.arel_table[ranker.column].asc).select([instance_class.arel_table[:id], instance_class.arel_table[ranker.column]])
+ _finder.order(instance_class.arel_table[ranker.column].asc).select(_select)

JeanMertz avatar May 18 '13 12:05 JeanMertz

Meant to post this issue sooner. Was able to fix this on my rails 4 branch. See my commit here https://github.com/mikeycgto/ranked-model/commit/fb0795b70563f0ae0da96c83fc053b288849afe2

mjc-gh avatar May 21 '13 13:05 mjc-gh

@JeanMertz your fix seems to make sense - That was all you needed to change for Rails 4? @mikeycgto had to update .all method calls to .load and such.

I'm hoping for time this week to some up with a testing solution for testing against rails 3 and 4 in the gem.

mixonic avatar May 21 '13 14:05 mixonic

@mixonic I do get deprecation warnings in my logs regarding the .all calls. If you'd want to get rid of them, I'd swap .all for .to_a. That one is cross-version compatible instead of the Rails 4 only .load.

Other than that, things seem to work fine.

Although one thing I noticed is #{ranker}_position being nil whenever I want to get the relative position. I am not sure if this ever was intended to be used like this (the setter #{ranker}_position= works correctly). I haven't had a real need for it though, so haven't really investigated yet.

JeanMertz avatar May 21 '13 14:05 JeanMertz

@mixonic I believe we can make those .all calls to be .to_a calls. This should give rails 3 and 4 support.

mjc-gh avatar May 21 '13 15:05 mjc-gh

same problem here on rails4 RC2

elfassy avatar Jun 21 '13 19:06 elfassy

@mikeycgto's commit worked for me, lets get that merged! :+1:

elfassy avatar Jun 21 '13 19:06 elfassy

Closing due to age. Perhaps this was fixed?

brendon avatar Jun 04 '24 04:06 brendon