Merge pull request #623 from rzane/ransack-alias
Allow creating aliases for ransack attributes.
This commit is contained in:
commit
1de34535ea
|
@ -7,7 +7,9 @@ module Ransack
|
|||
alias :search :ransack unless base.respond_to? :search
|
||||
base.class_eval do
|
||||
class_attribute :_ransackers
|
||||
class_attribute :_ransack_aliases
|
||||
self._ransackers ||= {}
|
||||
self._ransack_aliases ||= {}
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -20,15 +22,19 @@ module Ransack
|
|||
.new(self, name, opts, &block)
|
||||
end
|
||||
|
||||
def ransack_alias(new_name, old_name)
|
||||
self._ransack_aliases.store(new_name.to_s, old_name.to_s)
|
||||
end
|
||||
|
||||
# Ransackable_attributes, by default, returns all column names
|
||||
# and any defined ransackers as an array of strings.
|
||||
# For overriding with a whitelist array of strings.
|
||||
#
|
||||
def ransackable_attributes(auth_object = nil)
|
||||
if Ransack::SUPPORTS_ATTRIBUTE_ALIAS
|
||||
column_names + _ransackers.keys + attribute_aliases.keys
|
||||
column_names + _ransackers.keys + _ransack_aliases.keys + attribute_aliases.keys
|
||||
else
|
||||
column_names + _ransackers.keys
|
||||
column_names + _ransackers.keys + _ransack_aliases.keys
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -33,6 +33,14 @@ module Ransack
|
|||
end
|
||||
|
||||
module ClassMethods
|
||||
def _ransack_aliases
|
||||
@_ransack_aliases ||= {}
|
||||
end
|
||||
|
||||
def _ransack_aliases=(value)
|
||||
@_ransack_aliases = value
|
||||
end
|
||||
|
||||
def _ransackers
|
||||
@_ransackers ||= {}
|
||||
end
|
||||
|
@ -49,13 +57,17 @@ module Ransack
|
|||
|
||||
alias_method :search, :ransack
|
||||
|
||||
def ransack_alias(new_name, old_name)
|
||||
self._ransack_aliases.store(new_name.to_s, old_name.to_s)
|
||||
end
|
||||
|
||||
def ransacker(name, opts = {}, &block)
|
||||
self._ransackers = _ransackers.merge name.to_s => Ransacker
|
||||
.new(self, name, opts, &block)
|
||||
end
|
||||
|
||||
def all_ransackable_attributes
|
||||
['id'] + column_names.select { |c| c != '_id' } + _ransackers.keys
|
||||
['id'] + column_names.select { |c| c != '_id' } + _ransackers.keys + _ransack_aliases.keys
|
||||
end
|
||||
|
||||
def ransackable_attributes(auth_object = nil)
|
||||
|
|
|
@ -120,6 +120,10 @@ module Ransack
|
|||
end
|
||||
end
|
||||
|
||||
def ransackable_alias(str)
|
||||
klass._ransack_aliases.fetch(str, str)
|
||||
end
|
||||
|
||||
def ransackable_attribute?(str, klass)
|
||||
klass.ransackable_attributes(auth_object).include?(str) ||
|
||||
klass.ransortable_attributes(auth_object).include?(str)
|
||||
|
|
|
@ -9,9 +9,9 @@ module Ransack
|
|||
|
||||
class << self
|
||||
def extract(context, key, values)
|
||||
attributes, predicate = extract_attributes_and_predicate(key, context)
|
||||
attributes, predicate, combinator = extract_attributes_and_predicate(key, context)
|
||||
|
||||
if attributes.size > 0 && predicate
|
||||
combinator = key.match(/_(or|and)_/) ? $1 : nil
|
||||
condition = self.new(context)
|
||||
condition.build(
|
||||
:a => attributes,
|
||||
|
@ -38,12 +38,15 @@ module Ransack
|
|||
unless predicate || Ransack.options[:ignore_unknown_conditions]
|
||||
raise ArgumentError, "No valid predicate for #{key}"
|
||||
end
|
||||
str = context.ransackable_alias(str) if context.present?
|
||||
combinator = str.match(/_(or|and)_/) ? $1 : nil
|
||||
if context.present? && context.attribute_method?(str)
|
||||
attributes = [str]
|
||||
else
|
||||
attributes = str.split(/_and_|_or_/)
|
||||
end
|
||||
[attributes, predicate]
|
||||
|
||||
[attributes, predicate, combinator]
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -50,6 +50,21 @@ module Ransack
|
|||
end
|
||||
end
|
||||
|
||||
describe '#ransack_alias' do
|
||||
it 'translates an alias to the correct attributes' do
|
||||
p = Person.create!(name: 'Meatloaf', email: 'babies@example.com')
|
||||
|
||||
s = Person.ransack(term_cont: 'atlo')
|
||||
expect(s.result.to_a).to eq [p]
|
||||
|
||||
s = Person.ransack(term_cont: 'babi')
|
||||
expect(s.result.to_a).to eq [p]
|
||||
|
||||
s = Person.ransack(term_cont: 'nomatch')
|
||||
expect(s.result.to_a).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ransacker' do
|
||||
# For infix tests
|
||||
def self.sane_adapter?
|
||||
|
@ -213,6 +228,7 @@ module Ransack
|
|||
it { should include 'name' }
|
||||
it { should include 'reversed_name' }
|
||||
it { should include 'doubled_name' }
|
||||
it { should include 'term' }
|
||||
it { should include 'only_search' }
|
||||
it { should_not include 'only_sort' }
|
||||
it { should_not include 'only_admin' }
|
||||
|
@ -224,6 +240,7 @@ module Ransack
|
|||
it { should include 'name' }
|
||||
it { should include 'reversed_name' }
|
||||
it { should include 'doubled_name' }
|
||||
it { should include 'term' }
|
||||
it { should include 'only_search' }
|
||||
it { should_not include 'only_sort' }
|
||||
it { should include 'only_admin' }
|
||||
|
|
|
@ -4,6 +4,21 @@ module Ransack
|
|||
module Nodes
|
||||
describe Condition do
|
||||
|
||||
context 'with an alias' do
|
||||
subject {
|
||||
Condition.extract(
|
||||
Context.for(Person), 'term_start', Person.first(2).map(&:name)
|
||||
)
|
||||
}
|
||||
|
||||
specify { expect(subject.combinator).to eq 'or' }
|
||||
specify { expect(subject.predicate.name).to eq 'start' }
|
||||
|
||||
it 'converts the alias to the correct attributes' do
|
||||
expect(subject.attributes.map(&:name)).to eq(['name', 'email'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with multiple values and an _any predicate' do
|
||||
subject { Condition.extract(Context.for(Person), 'name_eq_any', Person.first(2).map(&:name)) }
|
||||
|
||||
|
|
|
@ -20,6 +20,8 @@ class Person
|
|||
has_many :articles
|
||||
has_many :comments
|
||||
|
||||
ransack_alias :term, :name_or_email
|
||||
|
||||
# has_many :authored_article_comments, :through => :articles,
|
||||
# :source => :comments, :foreign_key => :person_id
|
||||
|
||||
|
|
|
@ -94,6 +94,32 @@ module Ransack
|
|||
end
|
||||
end
|
||||
|
||||
describe '#ransack_alias' do
|
||||
it 'translates an alias to the correct attributes' do
|
||||
p = Person.create!(name: 'Meatloaf', email: 'babies@example.com')
|
||||
|
||||
s = Person.ransack(term_cont: 'atlo')
|
||||
expect(s.result.to_a).to eq [p]
|
||||
|
||||
s = Person.ransack(term_cont: 'babi')
|
||||
expect(s.result.to_a).to eq [p]
|
||||
|
||||
s = Person.ransack(term_cont: 'nomatch')
|
||||
expect(s.result.to_a).to eq []
|
||||
end
|
||||
|
||||
it 'also works with associations' do
|
||||
dad = Person.create!(name: 'Birdman')
|
||||
son = Person.create!(name: 'Weezy', parent: dad)
|
||||
|
||||
s = Person.ransack(daddy_eq: 'Birdman')
|
||||
expect(s.result.to_a).to eq [son]
|
||||
|
||||
s = Person.ransack(daddy_eq: 'Drake')
|
||||
expect(s.result.to_a).to eq []
|
||||
end
|
||||
end
|
||||
|
||||
describe '#ransacker' do
|
||||
# For infix tests
|
||||
def self.sane_adapter?
|
||||
|
@ -416,6 +442,7 @@ module Ransack
|
|||
it { should include 'name' }
|
||||
it { should include 'reversed_name' }
|
||||
it { should include 'doubled_name' }
|
||||
it { should include 'term' }
|
||||
it { should include 'only_search' }
|
||||
it { should_not include 'only_sort' }
|
||||
it { should_not include 'only_admin' }
|
||||
|
|
|
@ -4,6 +4,21 @@ module Ransack
|
|||
module Nodes
|
||||
describe Condition do
|
||||
|
||||
context 'with an alias' do
|
||||
subject {
|
||||
Condition.extract(
|
||||
Context.for(Person), 'term_start', Person.first(2).map(&:name)
|
||||
)
|
||||
}
|
||||
|
||||
specify { expect(subject.combinator).to eq 'or' }
|
||||
specify { expect(subject.predicate.name).to eq 'start' }
|
||||
|
||||
it 'converts the alias to the correct attributes' do
|
||||
expect(subject.attributes.map(&:name)).to eq(['name', 'email'])
|
||||
end
|
||||
end
|
||||
|
||||
context 'with multiple values and an _any predicate' do
|
||||
subject {
|
||||
Condition.extract(
|
||||
|
|
|
@ -52,6 +52,9 @@ class Person < ActiveRecord::Base
|
|||
|
||||
alias_attribute :full_name, :name
|
||||
|
||||
ransack_alias :term, :name_or_email
|
||||
ransack_alias :daddy, :parent_name
|
||||
|
||||
ransacker :reversed_name, formatter: proc { |v| v.reverse } do |parent|
|
||||
parent.table[:name]
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue