Skip to content

Instantly share code, notes, and snippets.

@cawel
Forked from RohanM/ransack.rb
Last active December 16, 2015 14:29
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save cawel/5448691 to your computer and use it in GitHub Desktop.
Save cawel/5448691 to your computer and use it in GitHub Desktop.
# Patch for ransack (https://github.com/ernie/ransack) to use scopes
# Helps migrating from Searchlogic or MetaSearch
# Place this file into config/initializer/ransack.rb of your Rails 3.2 project
#
# Usage:
# class Debt < ActiveRecord::Base
# scope_ransackable :overdue
# scope :overdue, lambda { where(["status = 'open' AND due_date < ?", Date.today]) }
# end
#
# Ransack out of the box ignores scopes. Example:
# Debt.search(:overdue => true, :amount_gteq => 10).result.to_sql
# => "SELECT `debts`.* FROM `debts` AND (`debts`.`amount` >= 10.0)"
#
# This is changed by the patch. Example:
# Debt.search(:overdue => true).result.to_sql
# => "SELECT `debts`.* FROM `debts` WHERE `debts`.`status` = 'open' AND (due_date < '2012-11-23') AND (`debts`.`amount` >= 10.0)"
#
# Only scopes that return ActiveRecord::Relation are supported.
# Any other scope or method that is called via Ransack will throw an exception
# and cause a database transaction rollback.
#
module Ransack
class ScopeNotWhitelistedError < StandardError; end
module Adapters
module ActiveRecord
module Base
def scope_ransackable(*args)
@ransackable_scopes ||= []
@ransackable_scopes += args.map(&:to_sym).reject { |scope| scope == :scope_ransackable }
# make the custom scopes available in view, so that we can use view helpers
# e.g. f.select :my_field instead of select_tag 'q[my_field]'
# note: that means we need to override with :selected => value
args.each do |method|
Ransack::Search.define_method method do
nil
end
end
end
def scope_is_ransackable?(scope)
@ransackable_scopes ||= []
@ransackable_scopes.include? scope.to_sym
end
def search_with_scopes(params = {}, options = {})
ransack_scope = self
ransack_params = {}
# Extract params which refer to a scope
transaction do
(params||{}).each_pair do |k,v|
if ransack_scope.respond_to?(k)
if !scope_is_ransackable? k
raise ScopeNotWhitelistedError.new
elsif (v == true)
ransack_scope = ransack_scope.send(k)
else
ransack_scope = ransack_scope.send(k, v)
end
raise ArgumentError.new "#{k} is not a scope that returns an ActiveRecord::Relation" unless ransack_scope.is_a? ::ActiveRecord::Relation
else
ransack_params.merge!(k => v)
end
end
end
ransack_scope.search_without_scopes(ransack_params, options)
end
alias_method_chain :search, :scopes
# refresh already existing ransack alias
alias_method :ransack, :search
end
end
end
end
@dexion
Copy link

dexion commented Apr 25, 2013

it doesn't work in this way:

Debt.search({'combinator' => 'or', 'groupings' => { '0' => {'overdue' => true}, '1' => {'amount_gteq' => 10}}}).result.to_sql

result is:

SELECT "debts".* FROM "debts"  WHERE "debts"."amount" >= 10

but should be:

SELECT "debts".* FROM "debts"  WHERE (`debts`.`status` = 'open' AND (due_date < '2012-11-23')) OR (`debts`.`amount` >= 10.0)

What are you think about this?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment