Merge branch 'master' into bring-rails-3
Conflicts: .travis.yml Gemfile README.md lib/ransack/adapters/active_record/3.0/context.rb lib/ransack/adapters/active_record/3.1/context.rb lib/ransack/adapters/active_record/3.2/context.rb lib/ransack/adapters/active_record/base.rb lib/ransack/adapters/active_record/context.rb lib/ransack/constants.rb lib/ransack/context.rb lib/ransack/helpers/form_builder.rb lib/ransack/helpers/form_helper.rb lib/ransack/nodes/attribute.rb lib/ransack/nodes/condition.rb lib/ransack/nodes/grouping.rb lib/ransack/nodes/sort.rb lib/ransack/predicate.rb lib/ransack/translate.rb ransack.gemspec spec/blueprints/people.rb spec/ransack/adapters/active_record/base_spec.rb spec/ransack/adapters/active_record/context_spec.rb spec/ransack/configuration_spec.rb spec/ransack/dependencies_spec.rb spec/ransack/helpers/form_builder_spec.rb spec/ransack/helpers/form_helper_spec.rb spec/ransack/predicate_spec.rb spec/ransack/search_spec.rb spec/ransack/translate_spec.rb spec/support/schema.rb
This commit is contained in:
commit
b4973315e9
15
.travis.yml
15
.travis.yml
|
@ -9,12 +9,21 @@ rvm:
|
|||
- 2.1.1
|
||||
|
||||
env:
|
||||
- RAILS=4-0-stable DB=sqlite3
|
||||
- RAILS=4-0-stable DB=mysql
|
||||
- RAILS=4-0-stable DB=postgres
|
||||
- RAILS=4-1-stable DB=sqlite3
|
||||
- RAILS=4-1-stable DB=mysql
|
||||
- RAILS=4-1-stable DB=postgres
|
||||
- RAILS=4-0-stable DB=sqlite3
|
||||
- RAILS=4-0-stable DB=mysql
|
||||
- RAILS=4-0-stable DB=postgres
|
||||
- RAILS=3-2-stable DB=sqlite
|
||||
- RAILS=3-2-stable DB=mysql
|
||||
- RAILS=3-2-stable DB=postgres
|
||||
- RAILS=3-1-stable DB=sqlite
|
||||
- RAILS=3-1-stable DB=mysql
|
||||
- RAILS=3-1-stable DB=postgres
|
||||
- RAILS=3-0-stable DB=sqlite
|
||||
- RAILS=3-0-stable DB=mysql
|
||||
- RAILS=3-0-stable DB=postgres
|
||||
|
||||
before_script:
|
||||
- mysql -e 'create database ransack collate utf8_general_ci;'
|
||||
|
|
3
Gemfile
3
Gemfile
|
@ -27,4 +27,7 @@ else
|
|||
gem 'activerecord'
|
||||
gem 'actionpack'
|
||||
end
|
||||
if rails == '3-0-stable'
|
||||
gem 'mysql2', '< 0.3'
|
||||
end
|
||||
end
|
||||
|
|
|
@ -133,6 +133,15 @@ Once you've done so, you can make use of the helpers in Ransack::Helpers::FormBu
|
|||
construct much more complex search forms, such as the one on the
|
||||
[demo page](http://ransack-demo.heroku.com) (source code [here](https://github.com/activerecord-hackery/ransack_demo)).
|
||||
|
||||
### Ransack #search method
|
||||
|
||||
Ransack will try to to make `#search` available in your models, but in the case that `#search` has already been defined, you can use `#ransack` instead. For example the following would be equivalent:
|
||||
|
||||
```
|
||||
Article.search(params[:q])
|
||||
Article.ransack(params[:q])
|
||||
```
|
||||
|
||||
### has_many and belongs_to associations
|
||||
|
||||
You can easily use Ransack to search in associated objects.
|
||||
|
|
|
@ -25,4 +25,4 @@ require 'ransack/adapters/active_record' if defined?(::ActiveRecord::Base)
|
|||
require 'ransack/helpers'
|
||||
require 'action_controller'
|
||||
|
||||
ActionController::Base.helper Ransack::Helpers::FormHelper
|
||||
ActionController::Base.helper Ransack::Helpers::FormHelper
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
if Arel::Nodes::And < Arel::Nodes::Binary
|
||||
class Ransack::Visitor
|
||||
def visit_Ransack_Nodes_And(object)
|
||||
nodes = object.values.map {|o| accept(o)}.compact
|
||||
nodes = object.values.map { |o| accept(o) }.compact
|
||||
return nil unless nodes.size > 0
|
||||
|
||||
if nodes.size > 1
|
||||
|
@ -132,9 +132,15 @@ module Arel
|
|||
end
|
||||
|
||||
def visit_Arel_Nodes_NamedFunction o
|
||||
"#{o.name}(#{o.distinct ? 'DISTINCT ' : ''}#{o.expressions.map { |x|
|
||||
visit x
|
||||
}.join(', ')})#{o.alias ? " AS #{visit o.alias}" : ''}"
|
||||
"#{
|
||||
o.name
|
||||
}(#{
|
||||
o.distinct ? 'DISTINCT ' : ''
|
||||
}#{
|
||||
o.expressions.map { |x| visit x }.join(', ')
|
||||
})#{
|
||||
o.alias ? " AS #{visit o.alias}" : ''
|
||||
}"
|
||||
end
|
||||
|
||||
def visit_Arel_Nodes_And o
|
||||
|
@ -146,13 +152,17 @@ module Arel
|
|||
end
|
||||
|
||||
def visit_Arel_Nodes_Values o
|
||||
"VALUES (#{o.expressions.zip(o.columns).map { |value, attr|
|
||||
"VALUES (#{
|
||||
o.expressions.zip(o.columns)
|
||||
.map { |value, attr|
|
||||
if Nodes::SqlLiteral === value
|
||||
visit_Arel_Nodes_SqlLiteral value
|
||||
else
|
||||
quote(value, attr && column_for(attr))
|
||||
end
|
||||
}.join ', '})"
|
||||
}
|
||||
.join ', '
|
||||
})"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -11,6 +11,8 @@ module Ransack
|
|||
JoinDependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency
|
||||
JoinBase = JoinDependency::JoinBase
|
||||
|
||||
# Redefine a few things for ActiveRecord 3.0.
|
||||
|
||||
def initialize(object, options = {})
|
||||
super
|
||||
@arel_visitor = Arel::Visitors.visitor_for @engine
|
||||
|
@ -24,10 +26,11 @@ module Ransack
|
|||
viz = Visitor.new
|
||||
relation = @object.where(viz.accept(search.base))
|
||||
if search.sorts.any?
|
||||
relation = relation.except(:order).reorder(viz.accept(search.sorts))
|
||||
relation = relation.except(:order)
|
||||
.reorder(viz.accept(search.sorts))
|
||||
end
|
||||
opts[:distinct] ? relation.select(
|
||||
"DISTINCT #{@klass.quoted_table_name}.*") : relation
|
||||
opts[:distinct] ?
|
||||
relation.select("DISTINCT #{@klass.quoted_table_name}.*") : relation
|
||||
end
|
||||
|
||||
def attribute_method?(str, klass = @klass)
|
||||
|
@ -38,10 +41,15 @@ module Ransack
|
|||
elsif (segments = str.split(/_/)).size > 1
|
||||
remainder = []
|
||||
found_assoc = nil
|
||||
while !found_assoc && remainder.unshift(segments.pop) && segments.size > 0 do
|
||||
assoc, poly_class = unpolymorphize_association(segments.join('_'))
|
||||
while !found_assoc && remainder.unshift(segments.pop) &&
|
||||
segments.size > 0 do
|
||||
assoc, poly_class = unpolymorphize_association(
|
||||
segments.join('_')
|
||||
)
|
||||
if found_assoc = get_association(assoc, klass)
|
||||
exists = attribute_method?(remainder.join('_'), poly_class || found_assoc.klass)
|
||||
exists = attribute_method?(
|
||||
remainder.join('_'), poly_class || found_assoc.klass
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -52,10 +60,12 @@ module Ransack
|
|||
def table_for(parent)
|
||||
parent.table
|
||||
end
|
||||
|
||||
|
||||
def type_for(attr)
|
||||
return nil unless attr && attr.valid?
|
||||
klassify(attr.parent).columns_hash[attr.arel_attribute.name.to_s].type
|
||||
klassify(attr.parent)
|
||||
.columns_hash[attr.arel_attribute.name.to_s]
|
||||
.type
|
||||
end
|
||||
|
||||
private
|
||||
|
@ -68,22 +78,26 @@ module Ransack
|
|||
elsif (segments = str.split(/_/)).size > 1
|
||||
remainder = []
|
||||
found_assoc = nil
|
||||
while remainder.unshift(segments.pop) && segments.size > 0 && !found_assoc do
|
||||
while remainder.unshift(segments.pop) && segments.size > 0 &&
|
||||
!found_assoc do
|
||||
assoc, klass = unpolymorphize_association(segments.join('_'))
|
||||
if found_assoc = get_association(assoc, parent)
|
||||
join = build_or_find_association(found_assoc.name, parent, klass)
|
||||
parent, attr_name = get_parent_and_attribute_name(remainder.join('_'), join)
|
||||
join = build_or_find_association(
|
||||
found_assoc.name, parent, klass
|
||||
)
|
||||
parent, attr_name = get_parent_and_attribute_name(
|
||||
remainder.join('_'), join
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
[parent, attr_name]
|
||||
end
|
||||
|
||||
def get_association(str, parent = @base)
|
||||
klass = klassify parent
|
||||
ransackable_association?(str, klass) &&
|
||||
klass.reflect_on_all_associations.detect {|a| a.name.to_s == str}
|
||||
klass.reflect_on_all_associations.detect { |a| a.name.to_s == str }
|
||||
end
|
||||
|
||||
def join_dependency(relation)
|
||||
|
@ -113,9 +127,9 @@ module Ransack
|
|||
association_joins = buckets['association_join'] || []
|
||||
stashed_association_joins = buckets['stashed_join'] || []
|
||||
join_nodes = buckets['join_node'] || []
|
||||
string_joins = (buckets['string_join'] || []).map { |x|
|
||||
x.strip
|
||||
}.uniq
|
||||
string_joins = (buckets['string_join'] || [])
|
||||
.map { |x| x.strip }
|
||||
.uniq
|
||||
|
||||
join_list = relation.send :custom_join_sql, (string_joins + join_nodes)
|
||||
|
||||
|
@ -133,13 +147,16 @@ module Ransack
|
|||
end
|
||||
|
||||
def build_or_find_association(name, parent = @base, klass = nil)
|
||||
found_association = @join_dependency.join_associations.detect do |assoc|
|
||||
found_association = @join_dependency.join_associations
|
||||
.detect do |assoc|
|
||||
assoc.reflection.name == name &&
|
||||
assoc.parent == parent &&
|
||||
(!klass || assoc.reflection.klass == klass)
|
||||
end
|
||||
unless found_association
|
||||
@join_dependency.send(:build, Polyamorous::Join.new(name, @join_type, klass), parent)
|
||||
@join_dependency.send(
|
||||
:build, Polyamorous::Join.new(name, @join_type, klass), parent
|
||||
)
|
||||
found_association = @join_dependency.join_associations.last
|
||||
# Leverage the stashed association functionality in AR
|
||||
@object = @object.joins(found_association)
|
||||
|
|
|
@ -10,6 +10,8 @@ module Ransack
|
|||
JoinDependency = ::ActiveRecord::Associations::JoinDependency
|
||||
JoinPart = JoinDependency::JoinPart
|
||||
|
||||
# Redefine a few things for ActiveRecord 3.1.
|
||||
|
||||
def initialize(object, options = {})
|
||||
super
|
||||
@arel_visitor = Arel::Visitors.visitor_for @engine
|
||||
|
@ -23,10 +25,12 @@ module Ransack
|
|||
viz = Visitor.new
|
||||
relation = @object.where(viz.accept(search.base))
|
||||
if search.sorts.any?
|
||||
relation = relation.except(:order).reorder(viz.accept(search.sorts))
|
||||
relation = relation.except(:order)
|
||||
.reorder(viz.accept(search.sorts))
|
||||
end
|
||||
opts[:distinct] ? relation.select(
|
||||
"DISTINCT #{@klass.quoted_table_name}.*") : relation
|
||||
opts[:distinct] ?
|
||||
relation.select("DISTINCT #{@klass.quoted_table_name}.*") :
|
||||
relation
|
||||
end
|
||||
|
||||
def attribute_method?(str, klass = @klass)
|
||||
|
@ -37,10 +41,15 @@ module Ransack
|
|||
elsif (segments = str.split(/_/)).size > 1
|
||||
remainder = []
|
||||
found_assoc = nil
|
||||
while !found_assoc && remainder.unshift(segments.pop) && segments.size > 0 do
|
||||
assoc, poly_class = unpolymorphize_association(segments.join('_'))
|
||||
while !found_assoc && remainder.unshift(segments.pop) &&
|
||||
segments.size > 0 do
|
||||
assoc, poly_class = unpolymorphize_association(
|
||||
segments.join('_')
|
||||
)
|
||||
if found_assoc = get_association(assoc, klass)
|
||||
exists = attribute_method?(remainder.join('_'), poly_class || found_assoc.klass)
|
||||
exists = attribute_method?(
|
||||
remainder.join('_'), poly_class || found_assoc.klass
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -74,11 +83,16 @@ module Ransack
|
|||
elsif (segments = str.split(/_/)).size > 1
|
||||
remainder = []
|
||||
found_assoc = nil
|
||||
while remainder.unshift(segments.pop) && segments.size > 0 && !found_assoc do
|
||||
while remainder.unshift(segments.pop) && segments.size > 0 &&
|
||||
!found_assoc do
|
||||
assoc, klass = unpolymorphize_association(segments.join('_'))
|
||||
if found_assoc = get_association(assoc, parent)
|
||||
join = build_or_find_association(found_assoc.name, parent, klass)
|
||||
parent, attr_name = get_parent_and_attribute_name(remainder.join('_'), join)
|
||||
join = build_or_find_association(
|
||||
found_assoc.name, parent, klass
|
||||
)
|
||||
parent, attr_name = get_parent_and_attribute_name(
|
||||
remainder.join('_'), join
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -89,7 +103,7 @@ module Ransack
|
|||
def get_association(str, parent = @base)
|
||||
klass = klassify parent
|
||||
ransackable_association?(str, klass) &&
|
||||
klass.reflect_on_all_associations.detect {|a| a.name.to_s == str}
|
||||
klass.reflect_on_all_associations.detect { |a| a.name.to_s == str }
|
||||
end
|
||||
|
||||
def join_dependency(relation)
|
||||
|
@ -119,11 +133,12 @@ module Ransack
|
|||
association_joins = buckets['association_join'] || []
|
||||
stashed_association_joins = buckets['stashed_join'] || []
|
||||
join_nodes = buckets['join_node'] || []
|
||||
string_joins = (buckets['string_join'] || []).map { |x|
|
||||
x.strip
|
||||
}.uniq
|
||||
string_joins = (buckets['string_join'] || [])
|
||||
.map { |x| x.strip }
|
||||
.uniq
|
||||
|
||||
join_list = relation.send :custom_join_ast, relation.table.from(relation.table), string_joins
|
||||
join_list = relation.send :custom_join_ast,
|
||||
relation.table.from(relation.table), string_joins
|
||||
|
||||
join_dependency = JoinDependency.new(
|
||||
relation.klass,
|
||||
|
@ -139,13 +154,16 @@ module Ransack
|
|||
end
|
||||
|
||||
def build_or_find_association(name, parent = @base, klass = nil)
|
||||
found_association = @join_dependency.join_associations.detect do |assoc|
|
||||
found_association = @join_dependency.join_associations
|
||||
.detect do |assoc|
|
||||
assoc.reflection.name == name &&
|
||||
assoc.parent == parent &&
|
||||
(!klass || assoc.reflection.klass == klass)
|
||||
end
|
||||
unless found_association
|
||||
@join_dependency.send(:build, Polyamorous::Join.new(name, @join_type, klass), parent)
|
||||
@join_dependency.send(
|
||||
:build, Polyamorous::Join.new(name, @join_type, klass), parent
|
||||
)
|
||||
found_association = @join_dependency.join_associations.last
|
||||
# Leverage the stashed association functionality in AR
|
||||
@object = @object.joins(found_association)
|
||||
|
|
|
@ -8,7 +8,7 @@ module Ransack
|
|||
module ActiveRecord
|
||||
class Context < ::Ransack::Context
|
||||
|
||||
# Redefine a few things that have changed with 3.2.
|
||||
# Redefine a few things for ActiveRecord 3.2.
|
||||
|
||||
def initialize(object, options = {})
|
||||
super
|
||||
|
|
|
@ -17,8 +17,8 @@ module Ransack
|
|||
end
|
||||
|
||||
def ransacker(name, opts = {}, &block)
|
||||
self._ransackers = _ransackers.merge name.to_s => Ransacker.new(
|
||||
self, name, opts, &block)
|
||||
self._ransackers = _ransackers.merge name.to_s => Ransacker
|
||||
.new(self, name, opts, &block)
|
||||
end
|
||||
|
||||
def ransackable_attributes(auth_object = nil)
|
||||
|
@ -26,7 +26,8 @@ module Ransack
|
|||
end
|
||||
|
||||
def ransortable_attributes(auth_object = nil)
|
||||
# Here so users can overwrite the attributes that show up in the sort_select
|
||||
# Here so users can overwrite the attributes
|
||||
# that show up in the sort_select
|
||||
ransackable_attributes(auth_object)
|
||||
end
|
||||
|
||||
|
|
|
@ -95,6 +95,5 @@ module Ransack
|
|||
unescaped.to_s.gsub(/([\\|\%|.])/, '\\\\\\1')
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
|
|
@ -61,7 +61,9 @@ module Ransack
|
|||
obj
|
||||
elsif obj.respond_to? :klass
|
||||
obj.klass
|
||||
elsif obj.respond_to? :base_klass
|
||||
elsif obj.respond_to? :active_record # Rails 3
|
||||
obj.active_record
|
||||
elsif obj.respond_to? :base_klass # Rails 4
|
||||
obj.base_klass
|
||||
else
|
||||
raise ArgumentError, "Don't know how to klassify #{obj.inspect}"
|
||||
|
@ -148,5 +150,9 @@ module Ransack
|
|||
def sortable_attributes(str = '')
|
||||
traverse(str).ransortable_attributes(auth_object)
|
||||
end
|
||||
|
||||
def searchable_associations(str = '')
|
||||
traverse(str).ransackable_associations(auth_object)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,4 @@
|
|||
require 'action_view'
|
||||
require 'simple_form' if
|
||||
(ENV['RANSACK_FORM_BUILDER'] || '').match('SimpleForm')
|
||||
|
||||
require 'simple_form' if
|
||||
(ENV['RANSACK_FORM_BUILDER'] || '').match('SimpleForm')
|
||||
|
@ -109,7 +107,12 @@ module Ransack
|
|||
|
||||
def predicate_select(options = {}, html_options = {})
|
||||
options[:compounds] = true if options[:compounds].nil?
|
||||
default = options.delete(:default) || 'cont'
|
||||
if ::ActiveRecord::VERSION::STRING >= "4"
|
||||
default = options.delete(:default) || 'cont'
|
||||
else
|
||||
default = options.delete(:default) || 'eq'
|
||||
end
|
||||
|
||||
keys = options[:compounds] ? Predicate.names :
|
||||
Predicate.names.reject { |k| k.match(/_(any|all)$/) }
|
||||
if only = options[:only]
|
||||
|
|
|
@ -23,7 +23,8 @@ module Ransack
|
|||
end
|
||||
|
||||
class Name < String
|
||||
attr_reader :singular, :plural, :element, :collection, :partial_path, :human, :param_key, :route_key, :i18n_key
|
||||
attr_reader :singular, :plural, :element, :collection, :partial_path,
|
||||
:human, :param_key, :route_key, :i18n_key
|
||||
alias_method :cache_key, :collection
|
||||
|
||||
def initialize
|
||||
|
|
|
@ -52,4 +52,4 @@ module Ransack
|
|||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,7 +19,8 @@ module Ransack
|
|||
m: combinator,
|
||||
v: predicate.wants_array ? Array(values) : [values]
|
||||
)
|
||||
# TODO: Figure out what to do with multiple types of attributes, if anything.
|
||||
# TODO: Figure out what to do with multiple types of attributes,
|
||||
# if anything.
|
||||
# Tempted to go with "garbage in, garbage out" on this one
|
||||
predicate.validate(condition.values, condition.default_type) ?
|
||||
condition : nil
|
||||
|
@ -208,14 +209,14 @@ module Ransack
|
|||
|
||||
def inspect
|
||||
data = [
|
||||
['attributes', a.try(:map, &:name)],
|
||||
['predicate', p],
|
||||
['combinator', m],
|
||||
['values', v.try(:map, &:value)]
|
||||
]
|
||||
.reject { |e| e[1].blank? }
|
||||
.map { |v| "#{v[0]}: #{v[1]}" }
|
||||
.join(', ')
|
||||
['attributes', a.try(:map, &:name)],
|
||||
['predicate', p],
|
||||
['combinator', m],
|
||||
['values', v.try(:map, &:value)]
|
||||
]
|
||||
.reject { |e| e[1].blank? }
|
||||
.map { |v| "#{v[0]}: #{v[1]}" }
|
||||
.join(', ')
|
||||
"Condition <#{data}>"
|
||||
end
|
||||
|
||||
|
@ -228,4 +229,4 @@ module Ransack
|
|||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -129,10 +129,9 @@ module Ransack
|
|||
when /^(g|c|m|groupings|conditions|combinator)=?$/
|
||||
true
|
||||
else
|
||||
name.
|
||||
split(/_and_|_or_/).
|
||||
select { |n| !@context.attribute_method?(n) }.
|
||||
empty?
|
||||
name.split(/_and_|_or_/)
|
||||
.select { |n| !@context.attribute_method?(n) }
|
||||
.empty?
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -191,7 +190,6 @@ module Ransack
|
|||
Predicate.detect_and_strip_from_string!(string)
|
||||
string
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -31,4 +31,4 @@ module Ransack
|
|||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -41,4 +41,4 @@ module Ransack
|
|||
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -107,4 +107,4 @@ module Ransack
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -29,8 +29,9 @@ module Ransack
|
|||
end
|
||||
|
||||
def name_from_attribute_name(attribute_name)
|
||||
names_by_decreasing_length.
|
||||
detect { |p| attribute_name.to_s.match(/_#{p}$/) }
|
||||
names_by_decreasing_length.detect {
|
||||
|p| attribute_name.to_s.match(/_#{p}$/)
|
||||
}
|
||||
end
|
||||
|
||||
def for_attribute_name(attribute_name)
|
||||
|
@ -75,4 +76,4 @@ module Ransack
|
|||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -21,4 +21,4 @@ module Ransack
|
|||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -104,7 +104,11 @@ module Ransack
|
|||
end
|
||||
|
||||
def self.i18n_key(klass)
|
||||
klass.model_name.i18n_key
|
||||
if ActiveRecord::VERSION::MAJOR == 3 && ActiveRecord::VERSION::MINOR == 0
|
||||
klass.model_name.i18n_key.to_s.tr('.', '/')
|
||||
else
|
||||
klass.model_name.i18n_key.to_s
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -15,9 +15,9 @@ Gem::Specification.new do |s|
|
|||
|
||||
s.rubyforge_project = "ransack"
|
||||
|
||||
s.add_dependency 'actionpack', '>= 4.0'
|
||||
s.add_dependency 'activerecord', '>= 4.0'
|
||||
s.add_dependency 'activesupport', '>= 4.0'
|
||||
s.add_dependency 'actionpack', '>= 3.0'
|
||||
s.add_dependency 'activerecord', '>= 3.0'
|
||||
s.add_dependency 'activesupport', '>= 3.0'
|
||||
s.add_dependency 'i18n'
|
||||
# s.add_dependency 'polyamorous', '~> 0.6.0'
|
||||
s.add_development_dependency 'rspec', '~> 2.8.0'
|
||||
|
|
|
@ -5,4 +5,4 @@ Person.blueprint do
|
|||
only_sort
|
||||
only_search
|
||||
only_admin
|
||||
end
|
||||
end
|
||||
|
|
|
@ -19,4 +19,3 @@ Sham.define do
|
|||
end
|
||||
|
||||
Schema.create
|
||||
|
||||
|
|
|
@ -37,6 +37,7 @@ module Ransack
|
|||
# ransacker :doubled_name do |parent|
|
||||
# Arel::Nodes::InfixOperation.new('||', parent.table[:name], parent.table[:name])
|
||||
# end
|
||||
|
||||
it 'creates ransack attributes' do
|
||||
s = Person.search(reversed_name_eq: 'htimS cirA')
|
||||
s.result.should have(1).person
|
||||
|
@ -60,18 +61,20 @@ module Ransack
|
|||
s.result.count.should eq 1
|
||||
end if defined?(Arel::Nodes::InfixOperation) && sane_adapter?
|
||||
|
||||
it "should remove empty key value pairs from the params hash" do
|
||||
s = Person.search(children_reversed_name_eq: '')
|
||||
s.result.to_sql.should_not match /LEFT OUTER JOIN/
|
||||
end
|
||||
if ::ActiveRecord::VERSION::STRING >= "4"
|
||||
it "should remove empty key value pairs from the params hash" do
|
||||
s = Person.search(children_reversed_name_eq: '')
|
||||
s.result.to_sql.should_not match /LEFT OUTER JOIN/
|
||||
end
|
||||
|
||||
it "should keep proper key value pairs in the params hash" do
|
||||
s = Person.search(children_reversed_name_eq: 'Testing')
|
||||
s.result.to_sql.should match /LEFT OUTER JOIN/
|
||||
end
|
||||
it "should keep proper key value pairs in the params hash" do
|
||||
s = Person.search(children_reversed_name_eq: 'Testing')
|
||||
s.result.to_sql.should match /LEFT OUTER JOIN/
|
||||
end
|
||||
|
||||
it "should function correctly when nil is passed in" do
|
||||
s = Person.search(nil)
|
||||
it "should function correctly when nil is passed in" do
|
||||
s = Person.search(nil)
|
||||
end
|
||||
end
|
||||
|
||||
it "should function correctly when using fields with dots in them" do
|
||||
|
@ -219,4 +222,4 @@ module Ransack
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -48,4 +48,4 @@ module Ransack
|
|||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -65,4 +65,4 @@ module Ransack
|
|||
Ransack.predicates.should_not have_key 'test_array_predicate_all'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#describe 'Ransack' do
|
||||
# it 'can be required without errors' do
|
||||
# output = `bundle exec ruby -e "require 'ransack'" 2>&1`
|
||||
# output.should be_empty
|
||||
# end
|
||||
#end
|
||||
unless ::ActiveRecord::VERSION::STRING >= "4"
|
||||
describe 'Ransack' do
|
||||
it 'can be required without errors' do
|
||||
output = `bundle exec ruby -e "require 'ransack'" 2>&1`
|
||||
output.should be_empty
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -55,7 +55,11 @@ module Ransack
|
|||
describe '#sort_link' do
|
||||
it 'sort_link for ransack attribute' do
|
||||
sort_link = @f.sort_link :name, controller: 'people'
|
||||
sort_link.should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
sort_link.should match /people\?q%5Bs%5D=name\+asc/
|
||||
else
|
||||
sort_link.should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end
|
||||
sort_link.should match /sort_link/
|
||||
sort_link.should match /Full Name<\/a>/
|
||||
end
|
||||
|
|
|
@ -33,48 +33,63 @@ module Ransack
|
|||
controller: 'people'
|
||||
)
|
||||
}
|
||||
it { should match /people\?q(%5B|\[)s(%5D|\])=name\+asc/ }
|
||||
it { should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?q%5Bs%5D=name\+asc/
|
||||
else
|
||||
/people\?q(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end)
|
||||
}
|
||||
it { should match /sort_link desc/ }
|
||||
it { should match /Full Name ▼/ }
|
||||
end
|
||||
|
||||
describe '#sort_link with default search_key defined as symbol' do
|
||||
subject { @controller.view_context.
|
||||
sort_link(
|
||||
Person.search({ sorts: ['name desc'] }, search_key: :people_search),
|
||||
:name,
|
||||
controller: 'people'
|
||||
subject { @controller.
|
||||
view_context.sort_link(
|
||||
Person.search({ :sorts => ['name desc'] }, :search_key => :people_search),
|
||||
:name, :controller => 'people'
|
||||
)
|
||||
}
|
||||
it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ }
|
||||
it { should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?people_search%5Bs%5D=name\+asc/
|
||||
else
|
||||
/people\?people_search(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end)
|
||||
}
|
||||
end
|
||||
|
||||
describe '#sort_link with default search_key defined as string' do
|
||||
subject { @controller.view_context.
|
||||
sort_link(
|
||||
Person.search({ sorts: ['name desc'] }, search_key: 'people_search'),
|
||||
:name,
|
||||
controller: 'people'
|
||||
subject {
|
||||
@controller.view_context.sort_link(
|
||||
Person.search({ :sorts => ['name desc'] }, :search_key => 'people_search'),
|
||||
:name, :controller => 'people'
|
||||
)
|
||||
}
|
||||
it { should match /people\?people_search(%5B|\[)s(%5D|\])=name\+asc/ }
|
||||
it { should match(
|
||||
if ActiveRecord::VERSION::STRING =~ /^3\.[1-2]\./
|
||||
/people\?people_search%5Bs%5D=name\+asc/
|
||||
else
|
||||
/people\?people_search(%5B|\[)s(%5D|\])=name\+asc/
|
||||
end)
|
||||
}
|
||||
end
|
||||
|
||||
context 'view has existing parameters' do
|
||||
before do
|
||||
@controller.view_context.
|
||||
params.merge!({ exist: 'existing' })
|
||||
@controller.view_context.params.merge!({ :exist => 'existing' })
|
||||
end
|
||||
describe '#sort_link should not remove existing params' do
|
||||
subject {
|
||||
@controller.view_context.
|
||||
sort_link(
|
||||
Person.search({ sorts: ['name desc'] }, search_key: 'people_search'),
|
||||
:name,
|
||||
controller: 'people'
|
||||
)
|
||||
@controller.view_context.sort_link(
|
||||
Person.search({ :sorts => ['name desc'] }, :search_key => 'people_search'),
|
||||
:name, :controller => 'people'
|
||||
)
|
||||
}
|
||||
it {
|
||||
should match /exist\=existing/
|
||||
}
|
||||
it { should match /exist\=existing/ }
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -42,17 +42,15 @@ module Ransack
|
|||
end
|
||||
|
||||
describe 'cont' do
|
||||
|
||||
|
||||
it_has_behavior 'wildcard escaping', :name_cont,
|
||||
(
|
||||
if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
||||
(if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
||||
/"people"."name" ILIKE '%\\%\\._\\\\%'/
|
||||
elsif ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
||||
/`people`.`name` LIKE '%\\\\%\\\\._\\\\\\\\%'/
|
||||
else
|
||||
/"people"."name" LIKE '%%._\\%'/
|
||||
end
|
||||
) do
|
||||
/"people"."name" LIKE '%%._\\%'/
|
||||
end) do
|
||||
subject { @s }
|
||||
end
|
||||
|
||||
|
@ -65,15 +63,13 @@ module Ransack
|
|||
|
||||
describe 'not_cont' do
|
||||
it_has_behavior 'wildcard escaping', :name_not_cont,
|
||||
(
|
||||
if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
||||
(if ActiveRecord::Base.connection.adapter_name == "PostgreSQL"
|
||||
/"people"."name" NOT ILIKE '%\\%\\._\\\\%'/
|
||||
elsif ActiveRecord::Base.connection.adapter_name == "Mysql2"
|
||||
/`people`.`name` NOT LIKE '%\\\\%\\\\._\\\\\\\\%'/
|
||||
else
|
||||
/"people"."name" NOT LIKE '%%._\\%'/
|
||||
end
|
||||
) do
|
||||
/"people"."name" NOT LIKE '%%._\\%'/
|
||||
end) do
|
||||
subject { @s }
|
||||
end
|
||||
|
||||
|
|
|
@ -89,13 +89,11 @@ module Ransack
|
|||
|
||||
it 'accepts "attributes" hashes for conditions' do
|
||||
search = Search.new(Person,
|
||||
c: {
|
||||
'0' => { a: ['name'], p: 'eq', v: ['Ernie'] },
|
||||
'1' => {
|
||||
a: ['children_name', 'parent_name'],
|
||||
p: 'eq', v: ['Ernie'], m: 'or'
|
||||
}
|
||||
}
|
||||
:c => {
|
||||
'0' => { :a => ['name'], :p => 'eq', :v => ['Ernie'] },
|
||||
'1' => { :a => ['children_name', 'parent_name'],
|
||||
:p => 'eq', :v => ['Ernie'], :m => 'or' }
|
||||
}
|
||||
)
|
||||
conditions = search.base.conditions
|
||||
conditions.should have(2).items
|
||||
|
@ -105,8 +103,7 @@ module Ransack
|
|||
|
||||
it 'creates conditions for custom predicates that take arrays' do
|
||||
Ransack.configure do |config|
|
||||
config.add_predicate 'ary_pred',
|
||||
wants_array: true
|
||||
config.add_predicate 'ary_pred', :wants_array => true
|
||||
end
|
||||
|
||||
search = Search.new(Person, name_ary_pred: ['Ernie', 'Bert'])
|
||||
|
@ -183,18 +180,42 @@ module Ransack
|
|||
second.should match /#{children_people_name_field} = 'Bert'/
|
||||
end
|
||||
|
||||
it 'returns distinct records when passed distinct: true' do
|
||||
search = Search.new(
|
||||
Person, g: [
|
||||
{ m: 'or',
|
||||
comments_body_cont: 'e',
|
||||
articles_comments_body_cont: 'e'
|
||||
}
|
||||
]
|
||||
)
|
||||
search.result.load.should have(9000).items
|
||||
search.result(distinct: true).should have(10).items
|
||||
search.result.load.uniq.should eq search.result(distinct: true).load
|
||||
if ::ActiveRecord::VERSION::STRING >= "4"
|
||||
it 'returns distinct records when passed distinct: true' do
|
||||
search = Search.new(
|
||||
Person, g: [
|
||||
{ m: 'or',
|
||||
comments_body_cont: 'e',
|
||||
articles_comments_body_cont: 'e'
|
||||
}
|
||||
]
|
||||
)
|
||||
search.result.load.should have(9000).items
|
||||
search.result(distinct: true).should have(10).items
|
||||
search.result.load.uniq.should eq search.result(distinct: true).load
|
||||
end
|
||||
else
|
||||
it 'returns distinct records when passed :distinct => true' do
|
||||
search = Search.new(
|
||||
Person, :g => [
|
||||
{ :m => 'or',
|
||||
:comments_body_cont => 'e',
|
||||
:articles_comments_body_cont => 'e'
|
||||
}
|
||||
]
|
||||
)
|
||||
if ActiveRecord::VERSION::MAJOR == 3
|
||||
all_or_load, uniq_or_distinct = :all, :uniq
|
||||
else
|
||||
all_or_load, uniq_or_distinct = :load, :distinct
|
||||
end
|
||||
search.result.send(all_or_load).
|
||||
should have(920).items
|
||||
search.result(:distinct => true).
|
||||
should have(330).items
|
||||
search.result.send(all_or_load).send(uniq_or_distinct).
|
||||
should eq search.result(:distinct => true).send(all_or_load)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -6,9 +6,7 @@ module Ransack
|
|||
describe '.attribute' do
|
||||
it 'translate namespaced attribute like AR does' do
|
||||
ar_translation = ::Namespace::Article.human_attribute_name(:title)
|
||||
ransack_translation = Ransack::Translate.attribute(
|
||||
:title, :context => ::Namespace::Article.search.context
|
||||
)
|
||||
ransack_translation = Ransack::Translate.attribute(:title, :context => ::Namespace::Article.search.context)
|
||||
ransack_translation.should eq ar_translation
|
||||
end
|
||||
end
|
||||
|
|
|
@ -23,7 +23,11 @@ else
|
|||
end
|
||||
|
||||
class Person < ActiveRecord::Base
|
||||
default_scope { order(id: :desc) }
|
||||
if ActiveRecord::VERSION::MAJOR == 3
|
||||
default_scope order('id DESC')
|
||||
else
|
||||
default_scope { order(id: :desc) }
|
||||
end
|
||||
belongs_to :parent, class_name: 'Person', foreign_key: :parent_id
|
||||
has_many :children, class_name: 'Person', foreign_key: :parent_id
|
||||
has_many :articles
|
||||
|
@ -57,6 +61,22 @@ class Person < ActiveRecord::Base
|
|||
column_names + _ransackers.keys - ['only_search', 'only_admin']
|
||||
end
|
||||
end
|
||||
|
||||
def self.ransackable_attributes(auth_object = nil)
|
||||
if auth_object == :admin
|
||||
column_names + _ransackers.keys - ['only_sort']
|
||||
else
|
||||
column_names + _ransackers.keys - ['only_sort', 'only_admin']
|
||||
end
|
||||
end
|
||||
|
||||
def self.ransortable_attributes(auth_object = nil)
|
||||
if auth_object == :admin
|
||||
column_names + _ransackers.keys - ['only_search']
|
||||
else
|
||||
column_names + _ransackers.keys - ['only_search', 'only_admin']
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class Article < ActiveRecord::Base
|
||||
|
@ -72,6 +92,12 @@ module Namespace
|
|||
end
|
||||
end
|
||||
|
||||
module Namespace
|
||||
class Article < ::Article
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
class Comment < ActiveRecord::Base
|
||||
belongs_to :article
|
||||
belongs_to :person
|
||||
|
@ -128,6 +154,7 @@ module Schema
|
|||
t.string :notable_type
|
||||
t.string :note
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
10.times do
|
||||
|
@ -148,6 +175,5 @@ module Schema
|
|||
Comment.make(
|
||||
body: 'First post!', article: Article.make(title: 'Hello, world!')
|
||||
)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Reference in New Issue