Skip to content

Commit e712ff1

Browse files
author
Ray Zane
committed
Allow creating aliases for ransack attributes
1 parent a255b21 commit e712ff1

File tree

10 files changed

+110
-6
lines changed

10 files changed

+110
-6
lines changed

lib/ransack/adapters/active_record/base.rb

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ def self.extended(base)
77
alias :search :ransack unless base.respond_to? :search
88
base.class_eval do
99
class_attribute :_ransackers
10+
class_attribute :_ransack_aliases
1011
self._ransackers ||= {}
12+
self._ransack_aliases ||= {}
1113
end
1214
end
1315

@@ -20,15 +22,19 @@ def ransacker(name, opts = {}, &block)
2022
.new(self, name, opts, &block)
2123
end
2224

25+
def ransack_alias(new_name, old_name)
26+
self._ransack_aliases.store(new_name.to_s, old_name.to_s)
27+
end
28+
2329
# Ransackable_attributes, by default, returns all column names
2430
# and any defined ransackers as an array of strings.
2531
# For overriding with a whitelist array of strings.
2632
#
2733
def ransackable_attributes(auth_object = nil)
2834
if Ransack::SUPPORTS_ATTRIBUTE_ALIAS
29-
column_names + _ransackers.keys + attribute_aliases.keys
35+
column_names + _ransackers.keys + _ransack_aliases.keys + attribute_aliases.keys
3036
else
31-
column_names + _ransackers.keys
37+
column_names + _ransackers.keys + _ransack_aliases.keys
3238
end
3339
end
3440

lib/ransack/adapters/mongoid/base.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,14 @@ def quote_column_name name
3333
end
3434

3535
module ClassMethods
36+
def _ransack_aliases
37+
@_ransack_aliases ||= {}
38+
end
39+
40+
def _ransack_aliases=(value)
41+
@_ransack_aliases = value
42+
end
43+
3644
def _ransackers
3745
@_ransackers ||= {}
3846
end
@@ -49,13 +57,17 @@ def ransack(params = {}, options = {})
4957

5058
alias_method :search, :ransack
5159

60+
def ransack_alias(new_name, old_name)
61+
self._ransack_aliases.store(new_name.to_s, old_name.to_s)
62+
end
63+
5264
def ransacker(name, opts = {}, &block)
5365
self._ransackers = _ransackers.merge name.to_s => Ransacker
5466
.new(self, name, opts, &block)
5567
end
5668

5769
def all_ransackable_attributes
58-
['id'] + column_names.select { |c| c != '_id' } + _ransackers.keys
70+
['id'] + column_names.select { |c| c != '_id' } + _ransackers.keys + _ransack_aliases.keys
5971
end
6072

6173
def ransackable_attributes(auth_object = nil)

lib/ransack/context.rb

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,10 @@ def unpolymorphize_association(str)
120120
end
121121
end
122122

123+
def ransackable_alias(str)
124+
klass._ransack_aliases.fetch(str, str)
125+
end
126+
123127
def ransackable_attribute?(str, klass)
124128
klass.ransackable_attributes(auth_object).include?(str) ||
125129
klass.ransortable_attributes(auth_object).include?(str)

lib/ransack/nodes/condition.rb

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,9 @@ class Condition < Node
99

1010
class << self
1111
def extract(context, key, values)
12-
attributes, predicate = extract_attributes_and_predicate(key, context)
12+
attributes, predicate, combinator = extract_attributes_and_predicate(key, context)
13+
1314
if attributes.size > 0 && predicate
14-
combinator = key.match(/_(or|and)_/) ? $1 : nil
1515
condition = self.new(context)
1616
condition.build(
1717
:a => attributes,
@@ -38,12 +38,15 @@ def extract_attributes_and_predicate(key, context = nil)
3838
unless predicate || Ransack.options[:ignore_unknown_conditions]
3939
raise ArgumentError, "No valid predicate for #{key}"
4040
end
41+
str = context.ransackable_alias(str) if context.present?
42+
combinator = str.match(/_(or|and)_/) ? $1 : nil
4143
if context.present? && context.attribute_method?(str)
4244
attributes = [str]
4345
else
4446
attributes = str.split(/_and_|_or_/)
4547
end
46-
[attributes, predicate]
48+
49+
[attributes, predicate, combinator]
4750
end
4851
end
4952

spec/mongoid/adapters/mongoid/base_spec.rb

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,21 @@ module Mongoid
5050
end
5151
end
5252

53+
describe '#ransack_alias' do
54+
it 'translates an alias to the correct attributes' do
55+
p = Person.create!(name: 'Meatloaf', email: '[email protected]')
56+
57+
s = Person.ransack(term_cont: 'atlo')
58+
expect(s.result.to_a).to eq [p]
59+
60+
s = Person.ransack(term_cont: 'babi')
61+
expect(s.result.to_a).to eq [p]
62+
63+
s = Person.ransack(term_cont: 'nomatch')
64+
expect(s.result.to_a).to eq []
65+
end
66+
end
67+
5368
describe '#ransacker' do
5469
# For infix tests
5570
def self.sane_adapter?
@@ -213,6 +228,7 @@ def self.sane_adapter?
213228
it { should include 'name' }
214229
it { should include 'reversed_name' }
215230
it { should include 'doubled_name' }
231+
it { should include 'term' }
216232
it { should include 'only_search' }
217233
it { should_not include 'only_sort' }
218234
it { should_not include 'only_admin' }
@@ -224,6 +240,7 @@ def self.sane_adapter?
224240
it { should include 'name' }
225241
it { should include 'reversed_name' }
226242
it { should include 'doubled_name' }
243+
it { should include 'term' }
227244
it { should include 'only_search' }
228245
it { should_not include 'only_sort' }
229246
it { should include 'only_admin' }

spec/mongoid/nodes/condition_spec.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ module Ransack
44
module Nodes
55
describe Condition do
66

7+
context 'with an alias' do
8+
subject {
9+
Condition.extract(
10+
Context.for(Person), 'term_start', Person.first(2).map(&:name)
11+
)
12+
}
13+
14+
specify { expect(subject.combinator).to eq 'or' }
15+
specify { expect(subject.predicate.name).to eq 'start' }
16+
17+
it 'converts the alias to the correct attributes' do
18+
expect(subject.attributes.map(&:name)).to eq(['name', 'email'])
19+
end
20+
end
21+
722
context 'with multiple values and an _any predicate' do
823
subject { Condition.extract(Context.for(Person), 'name_eq_any', Person.first(2).map(&:name)) }
924

spec/mongoid/support/schema.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ class Person
2020
has_many :articles
2121
has_many :comments
2222

23+
ransack_alias :term, :name_or_email
24+
2325
# has_many :authored_article_comments, :through => :articles,
2426
# :source => :comments, :foreign_key => :person_id
2527

spec/ransack/adapters/active_record/base_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,32 @@ module ActiveRecord
9494
end
9595
end
9696

97+
describe '#ransack_alias' do
98+
it 'translates an alias to the correct attributes' do
99+
p = Person.create!(name: 'Meatloaf', email: '[email protected]')
100+
101+
s = Person.ransack(term_cont: 'atlo')
102+
expect(s.result.to_a).to eq [p]
103+
104+
s = Person.ransack(term_cont: 'babi')
105+
expect(s.result.to_a).to eq [p]
106+
107+
s = Person.ransack(term_cont: 'nomatch')
108+
expect(s.result.to_a).to eq []
109+
end
110+
111+
it 'also works with associations' do
112+
dad = Person.create!(name: 'Birdman')
113+
son = Person.create!(name: 'Weezy', parent: dad)
114+
115+
s = Person.ransack(daddy_eq: 'Birdman')
116+
expect(s.result.to_a).to eq [son]
117+
118+
s = Person.ransack(daddy_eq: 'Drake')
119+
expect(s.result.to_a).to eq []
120+
end
121+
end
122+
97123
describe '#ransacker' do
98124
# For infix tests
99125
def self.sane_adapter?
@@ -416,6 +442,7 @@ def self.simple_escaping?
416442
it { should include 'name' }
417443
it { should include 'reversed_name' }
418444
it { should include 'doubled_name' }
445+
it { should include 'term' }
419446
it { should include 'only_search' }
420447
it { should_not include 'only_sort' }
421448
it { should_not include 'only_admin' }

spec/ransack/nodes/condition_spec.rb

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,21 @@ module Ransack
44
module Nodes
55
describe Condition do
66

7+
context 'with an alias' do
8+
subject {
9+
Condition.extract(
10+
Context.for(Person), 'term_start', Person.first(2).map(&:name)
11+
)
12+
}
13+
14+
specify { expect(subject.combinator).to eq 'or' }
15+
specify { expect(subject.predicate.name).to eq 'start' }
16+
17+
it 'converts the alias to the correct attributes' do
18+
expect(subject.attributes.map(&:name)).to eq(['name', 'email'])
19+
end
20+
end
21+
722
context 'with multiple values and an _any predicate' do
823
subject {
924
Condition.extract(

spec/support/schema.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ class Person < ActiveRecord::Base
5252

5353
alias_attribute :full_name, :name
5454

55+
ransack_alias :term, :name_or_email
56+
ransack_alias :daddy, :parent_name
57+
5558
ransacker :reversed_name, formatter: proc { |v| v.reverse } do |parent|
5659
parent.table[:name]
5760
end

0 commit comments

Comments
 (0)