Ransack: Breaking change in 2.0.0 re: joins+order

Created on 10 Aug 2018  路  10Comments  路  Source: activerecord-hackery/ransack

This test passes in rails 5.2.0 + ransack 1.8.8.
This test fails in rails 5.2.1 + ransack 2.0.0.
Is this an intentional breaking change, or a bug?

# frozen_string_literal: true

begin
  require "bundler/inline"
rescue ::LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
  gem "ransack", "2.0.0", require: false
  gem "rails", "5.2.1", require: false
  gem "sqlite3"
end

require "active_record"
require "ransack"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
::ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
::ActiveRecord::Base.logger = ::Logger.new(STDOUT)

::ActiveRecord::Schema.define do
  create_table :books, force: true do |t|
  end

  create_table :chapters, force: true do |t|
    t.string :name
    t.belongs_to :book
  end

  create_table :pages, force: true do |t|
    t.belongs_to :chapter
  end
end

class Book < ::ActiveRecord::Base
end

class Chapter < ::ActiveRecord::Base
  belongs_to :book
end

class Page < ::ActiveRecord::Base
  belongs_to :chapter
end

class BugTest < ::ActiveSupport::TestCase
  # Test passes in rails 5.2.0 + ransack 1.8.8
  # Test fails in rails 5.2.1 + ransack 2.0.0
  def test_association_stuff
    b = Book.create!
    c = Chapter.create!(book: b)
    Page.create!(chapter: c)
    search = { chapter_book_id_eq: b.id }
    assert_nothing_raised {
      ::Page.joins(:chapter).order('chapter.name').ransack(search).result(distinct: true)
    }
    # NoMethodError: undefined method `[]' for nil:NilClass
    # /Users/jared/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/ransack-2.0.0/lib/ransack/nodes/bindable.rb:42:in `get_attribute'
    # /Users/jared/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/ransack-2.0.0/lib/ransack/nodes/bindable.rb:34:in `get_arel_attribute'
  end
end

Most helpful comment

2.0.1 is released with a fix for this issue.

All 10 comments

This also happens with:

::Page.joins(:chapter).ransack(search)

so I think it's just related to the join.

For some reason, at a certain point table_for(parent) is called. parent is:

#<ActiveRecord::Associations::JoinDependency::JoinAssociation:0x007ff7afa59200 @join_type=Arel::Nodes::InnerJoin, @base_klass=Chapter(id: integer, name: string, book_id: integer), @children=[], @reflection=#<ActiveRecord::Reflection::BelongsToReflection:0x007ff7b0069370 @name=:chapter, @scope=nil, @options={}, @active_record=Page(id: integer, chapter_id: integer), @klass=Chapter(id: integer, name: string, book_id: integer), @plural_name="chapters", @type=nil, @foreign_type=nil, @constructable=true, @association_scope_cache=#<Concurrent::Map:0x007ff7b0069118 entries=0 default_proc=nil>, @class_name="Chapter", @inverse_name=nil, @foreign_key="chapter_id", @join_keys=#<struct ActiveRecord::Reflection::AbstractReflection::JoinKeys key="id", foreign_key="chapter_id">>, @tables=nil>

and calling .table on it returns nil which I think is not the expected behavior.

We need to know whether this is a bug or an intentional breaking change. However, in the meantime, we can work around this issue by doing our joins after ransack. With that workaround, I'll be upgrading to 2.0.0.

@jaredbeck It isn't an intentional change. I will fix later this week but PRs welcomed.

Thanks Greg. I used the workaround I mentioned above and upgraded to 2.0.0 in production on Friday. We'll see if anything blows up when business hours begin on the East coast today. (knock on wood)

Hey, guys, I have tried different approaches with this, and find out that this error raised for
has_one and belongs_to, for has_many it looks good

# frozen_string_literal: true

begin
  require "bundler/inline"
rescue ::LoadError => e
  $stderr.puts "Bundler version 1.10 or later is required. Please update your Bundler"
  raise e
end

gemfile(true) do
  source "https://rubygems.org"
  git_source(:github) { |repo| "https://github.com/#{repo}.git" }
  gem "ransack", "2.0.0", require: false
  gem "rails", "5.2.1", require: false
  gem "sqlite3"
end

require "active_record"
require "ransack"
require "minitest/autorun"
require "logger"

# This connection will do for database-independent bug reports.
::ActiveRecord::Base.establish_connection(adapter: "sqlite3", database: ":memory:")
::ActiveRecord::Base.logger = ::Logger.new(STDOUT)

::ActiveRecord::Schema.define do
  create_table :authors, force: true do |t|
  end

  create_table :books, force: true do |t|
    t.belongs_to :author
  end

  create_table :chapters, force: true do |t|
    t.string :name
    t.belongs_to :book
  end

  create_table :pages, force: true do |t|
    t.belongs_to :chapter
  end
end

class Book < ::ActiveRecord::Base
  has_many :chapters
  belongs_to :author
end

class Author < ::ActiveRecord::Base
  has_one :book
end

class Chapter < ::ActiveRecord::Base
  has_many :chapters
  belongs_to :book
end

class Page < ::ActiveRecord::Base
  belongs_to :chapter
end

class BugTest < ::ActiveSupport::TestCase
  # Test passes in rails 5.2.0 + ransack 1.8.8
  # Test fails in rails 5.2.1 + ransack 2.0.0
  def test_association_stuff
    a  = Author.create!
    b = Book.create!(author: a)
    c = Chapter.create!(book: b)
    Page.create!(chapter: c)
    search_author   = { book_id_eq: b.id }
    search_book = { chapter_id_eq: c.id }
    search = { chapter_book_id_eq: b.id }

    # Raises error for has_one association
    # assert_nothing_raised {
    #   ::Author.joins(:book).ransack(search_author).result(distinct: true)
    # }

    # Doesn't raise error for has_many association
    assert_nothing_raised {
      ::Book.joins(:chapters).ransack(search_book).result(distinct: true)
    }

    # Raises error for belongs_to association
    # assert_nothing_raised {
    #   ::Page.joins(:chapter).ransack(search).result(distinct: true)
    # }
    # NoMethodError: undefined method `[]' for nil:NilClass
    # /Users/jared/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/ransack-2.0.0/lib/ransack/nodes/bindable.rb:42:in `get_attribute'
    # /Users/jared/.rbenv/versions/2.4.4/lib/ruby/gems/2.4.0/gems/ransack-2.0.0/lib/ransack/nodes/bindable.rb:34:in `get_arel_attribute'
  end
end

Can people check whether #951 fixes this for you?

@CGA1123 the script above works for me using your branch. Thanks!

@CGA1123 Seems to work here. Thanks a lot!

@jaredbeck any chance for 2.0.1 release with this fix?

2.0.1 is released with a fix for this issue.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

llopez picture llopez  路  4Comments

seanfcarroll picture seanfcarroll  路  4Comments

seanfcarroll picture seanfcarroll  路  4Comments

mbajur picture mbajur  路  5Comments

senid231 picture senid231  路  4Comments