From 549342aeed2a5e9962c43f14f744930511d86de8 Mon Sep 17 00:00:00 2001 From: Marten Schilstra Date: Wed, 23 Sep 2015 16:03:04 +0200 Subject: [PATCH] Add support for attribute alias. --- lib/ransack.rb | 7 ++++++ lib/ransack/adapters/active_record/base.rb | 6 ++++- lib/ransack/nodes/bindable.rb | 24 ++++++++++++++----- .../adapters/active_record/base_spec.rb | 12 ++++++++++ spec/ransack/search_spec.rb | 11 +++++++++ spec/support/schema.rb | 7 +++--- 6 files changed, 57 insertions(+), 10 deletions(-) diff --git a/lib/ransack.rb b/lib/ransack.rb index 2e28cdc1d..f33732313 100644 --- a/lib/ransack.rb +++ b/lib/ransack.rb @@ -8,6 +8,13 @@ module Ransack extend Configuration class UntraversableAssociationError < StandardError; end; + + SUPPORTS_ATTRIBUTE_ALIAS = + begin + ActiveRecord::Base.respond_to?(:attribute_aliases) + rescue NameError + false + end end Ransack.configure do |config| diff --git a/lib/ransack/adapters/active_record/base.rb b/lib/ransack/adapters/active_record/base.rb index 66b2b8e55..6d9ef7b31 100644 --- a/lib/ransack/adapters/active_record/base.rb +++ b/lib/ransack/adapters/active_record/base.rb @@ -25,7 +25,11 @@ def ransacker(name, opts = {}, &block) # For overriding with a whitelist array of strings. # def ransackable_attributes(auth_object = nil) - column_names + _ransackers.keys + if Ransack::SUPPORTS_ATTRIBUTE_ALIAS + column_names + _ransackers.keys + attribute_aliases.keys + else + column_names + _ransackers.keys + end end # Ransackable_associations, by default, returns the names diff --git a/lib/ransack/nodes/bindable.rb b/lib/ransack/nodes/bindable.rb index b80d8aec0..72bb17887 100644 --- a/lib/ransack/nodes/bindable.rb +++ b/lib/ransack/nodes/bindable.rb @@ -27,14 +27,26 @@ def reset_binding! private - def get_arel_attribute - if ransacker - ransacker.attr_from(self) - else - context.table_for(parent)[attr_name] - end + def get_arel_attribute + if ransacker + ransacker.attr_from(self) + else + get_attribute end + end + def get_attribute + if is_alias_attribute? + context.table_for(parent)[context.klass.attribute_aliases[attr_name]] + else + context.table_for(parent)[attr_name] + end + end + + def is_alias_attribute? + Ransack::SUPPORTS_ATTRIBUTE_ALIAS && + context.klass.attribute_aliases.key?(attr_name) + end end end end diff --git a/spec/ransack/adapters/active_record/base_spec.rb b/spec/ransack/adapters/active_record/base_spec.rb index 957a66fc9..34cf49246 100644 --- a/spec/ransack/adapters/active_record/base_spec.rb +++ b/spec/ransack/adapters/active_record/base_spec.rb @@ -274,6 +274,14 @@ def self.simple_escaping? expect(s.result.to_a).to eq [p] end + it "should translate attribute aliased column names", + :if => Ransack::SUPPORTS_ATTRIBUTE_ALIAS do + s = Person.ransack(:full_name_eq => 'Nicolas Cage') + expect(s.result.to_sql).to match( + /WHERE #{quote_table_name("people")}.#{quote_column_name("name")}/ + ) + end + it 'allows sort by "only_sort" field' do s = Person.ransack( "s" => { "0" => { "dir" => "asc", "name" => "only_sort" } } @@ -382,6 +390,10 @@ def self.simple_escaping? it { should include 'only_search' } it { should_not include 'only_sort' } it { should_not include 'only_admin' } + + if Ransack::SUPPORTS_ATTRIBUTE_ALIAS + it { should include 'full_name' } + end end context 'with auth_object :admin' do diff --git a/spec/ransack/search_spec.rb b/spec/ransack/search_spec.rb index 779fba8f6..52d3cf93b 100644 --- a/spec/ransack/search_spec.rb +++ b/spec/ransack/search_spec.rb @@ -93,6 +93,17 @@ module Ransack expect(condition.value).to eq 'Ernie' end + + it 'creates conditions for aliased attributes', + :if => Ransack::SUPPORTS_ATTRIBUTE_ALIAS do + search = Search.new(Person, full_name_eq: 'Ernie') + condition = search.base[:full_name_eq] + expect(condition).to be_a Nodes::Condition + expect(condition.predicate.name).to eq 'eq' + expect(condition.attributes.first.name).to eq 'full_name' + expect(condition.value).to eq 'Ernie' + end + it 'preserves default scope and conditions for associations' do search = Search.new(Person, published_articles_title_eq: 'Test') expect(search.result.to_sql).to include 'default_scope' diff --git a/spec/support/schema.rb b/spec/support/schema.rb index 735b056a6..51caec7db 100644 --- a/spec/support/schema.rb +++ b/spec/support/schema.rb @@ -50,6 +50,8 @@ class Person < ActiveRecord::Base of_age ? where("age >= ?", 18) : where("age < ?", 18) } + alias_attribute :full_name, :name + ransacker :reversed_name, :formatter => proc { |v| v.reverse } do |parent| parent.table[:name] end @@ -89,9 +91,9 @@ class Person < ActiveRecord::Base def self.ransackable_attributes(auth_object = nil) if auth_object == :admin - column_names + _ransackers.keys - ['only_sort'] + super - ['only_sort'] else - column_names + _ransackers.keys - ['only_sort', 'only_admin'] + super - ['only_sort', 'only_admin'] end end @@ -102,7 +104,6 @@ def self.ransortable_attributes(auth_object = nil) column_names + _ransackers.keys - ['only_search', 'only_admin'] end end - end class Article < ActiveRecord::Base