ransack: Rails 4.2: NoMethodError: undefined method `asc' for #

I’m in the process of upgrading a Rails 4.1.8 app to Rails 4.2.0.rc2. I’m not seeing this behavior on 4.1.8 and I’m sure the error could be on my end, but I’m not quite sure I see a link to that. Also, I’ve some wrappers (Concerns) around ransack that I’ll try to unravel to simplify this report, so forgive me if things look a little funky. Here goes…

Using MRI 2.1.0, I get the same results with master (ff0327df2c21) and the rails-4.2 branch; I’ve been using ransack 1.5.1 with my Rails 4.1.8 branch with success.

The backtrace for my failing specs all look similar to this:

Failure/Error: Unable to find matching line from backtrace
     NoMethodError:
       undefined method `asc' for #<Arel::Nodes::NamedFunction:0x007ffcf0ab4a88>
     Shared Example Group: "a pageable controller" called from ./spec/controllers/zone_maps_controller_spec.rb:32
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/visitor.rb:44:in `visit_Ransack_Nodes_Sort'
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/visitor.rb:52:in `visit'
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/visitor.rb:5:in `accept'
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/visitor.rb:13:in `block in visit_Array'
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/visitor.rb:13:in `map'
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/visitor.rb:13:in `visit_Array'
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/visitor.rb:52:in `visit'
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/visitor.rb:5:in `accept'
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/adapters/active_record/context.rb:38:in `evaluate'
     # /Users/sloveless/.gem/ruby/2.1.0/bundler/gems/ransack-ff0327df2c21/lib/ransack/search.rb:42:in `result'
     # ./app/controllers/zone_maps_controller.rb:7:in `index'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/action_controller/metal/implicit_render.rb:4:in `send_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/abstract_controller/base.rb:198:in `process_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/action_controller/metal/rendering.rb:10:in `process_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/abstract_controller/callbacks.rb:20:in `block in process_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:117:in `call'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:117:in `call'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:151:in `block in halting_and_conditional'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:151:in `call'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:151:in `block in halting_and_conditional'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:169:in `call'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:169:in `block in halting'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:169:in `call'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:169:in `block in halting'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:169:in `call'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:169:in `block in halting'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:92:in `call'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:92:in `_run_callbacks'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:734:in `_run_process_action_callbacks'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/callbacks.rb:81:in `run_callbacks'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/abstract_controller/callbacks.rb:19:in `process_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/action_controller/metal/rescue.rb:29:in `process_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/action_controller/metal/instrumentation.rb:31:in `block in process_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/notifications.rb:164:in `block in instrument'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/notifications/instrumenter.rb:20:in `instrument'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activesupport-4.2.0.rc2/lib/active_support/notifications.rb:164:in `instrument'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/action_controller/metal/instrumentation.rb:30:in `process_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/action_controller/metal/params_wrapper.rb:250:in `process_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/activerecord-4.2.0.rc2/lib/active_record/railties/controller_runtime.rb:18:in `process_action'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/abstract_controller/base.rb:137:in `process'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionview-4.2.0.rc2/lib/action_view/rendering.rb:30:in `process'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/action_controller/test_case.rb:629:in `process'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/action_controller/test_case.rb:65:in `process'
     # /Users/sloveless/.gem/ruby/2.1.0/gems/actionpack-4.2.0.rc2/lib/action_controller/test_case.rb:505:in `get'
     # ./spec/controllers/zone_maps_controller_spec.rb:33:in `block (4 levels) in <top (required)>'

My controller’s #index method:

  def index
    @zone_maps = ZoneMap.
                 search_with_sort(search_params, sort_params, name: 'name').result.
                 page(page).per(page_limit)

    respond_with @zone_maps,
      meta: {
        page: current_page(@zone_maps.count),
        page_count: @zone_maps.total_pages,
        total_count: @zone_maps.total_count
      }
  end

The .search_with_sort wrapper:

  module ModelSupport
    module RansackSearchable
      extend ActiveSupport::Concern

      included do
        if table_exists?
          columns.each do |column|
            next unless %i[string text].include? column.type

            ransacker column.name.to_sym, formatter: proc(&:downcase) do |parent|
              parent.table[column.name.to_sym].lower
            end
          end
        end

        # Uses ransack to search and sort.  Prioritizes search criteria from
        # search before sort and defaults.
        #
        # @param search [Hash] Search criteria for ransack.
        # @param sort [Hash] Sort name and direction.  Used only if search param
        #   didn't have valid criteria.
        #   example: { name: 'created_at', dir: 'desc' }
        # @param defaults [Hash] Default sort criteria if all else fails.
        #   example: { name: 'name', dir: 'asc' }
        # @return [Hash] Search params for ransack.
        def self.search_with_sort(search = {}, sort = {}, defaults = {})
          results = self.search(search)

          defaults[:dir] ||= 'asc'

          results.sorts.any?(&:valid?) || results.build_sort(sort).valid? || results.build_sort(defaults)

          results
        end
      end
    end
  end

Part of the controller spec:

  describe 'GET index' do
    it_behaves_like 'a pageable controller' do
      before { get :index }
    end
  end

The shared example:

RSpec.shared_examples 'a pageable controller' do |ivar_symbol|
  let(:assigns_symbol) do
    ivar_symbol || described_class.controller_name.to_sym
  end

  before do
    allow(subject).to receive(:page_limit) { 25 }
  end

  it 'defaults to #page_limit' do
    expect(assigns(assigns_symbol).default_per_page).to eq 25
  end

  it 'has .find result that responds to #current_page' do
    expect(assigns(assigns_symbol).current_page).to eq 1
  end

  it 'has .find result that responds to #page.size' do
    expect(assigns(assigns_symbol).page.size).to eq(assigns(assigns_symbol).count)
  end
end

After that, I’m not sure what other code on my end may be pertinent. Sure smells like some changes happened in Arel…

About this issue

  • Original URL
  • State: closed
  • Created 10 years ago
  • Comments: 15 (7 by maintainers)

Commits related to this issue

Most upvoted comments

Here is my workaround:

# config/initializers/ransack.rb
class Arel::Nodes::NamedFunction
  include Arel::OrderPredications
end

The problem is that Ransack expects Arel nodes to have asc and desc methods defined, but some of them don’t have these methods. A temporary solution is to include these methods in Arel node that you use in your ransacker (in my case it was Arel::Nodes::NamedFunction).