Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Add :only/:compound options to predicate_select, refactor ands/ors ->…

… groupings
  • Loading branch information...
commit 1c41a3ea2c70e247b7d6d7d372a68ffe13768a14 1 parent cec4e3d
@ernie ernie authored
View
24 lib/ransack/helpers/form_builder.rb
@@ -77,12 +77,8 @@ def condition_fields(*args, &block)
search_fields(:c, args, block)
end
- def and_fields(*args, &block)
- search_fields(:n, args, block)
- end
-
- def or_fields(*args, &block)
- search_fields(:o, args, block)
+ def grouping_fields(*args, &block)
+ search_fields(:g, args, block)
end
def attribute_fields(*args, &block)
@@ -114,15 +110,27 @@ def search_fields(name, args, block)
end
def predicate_select(options = {}, html_options = {})
+ options[:compounds] = true if options[:compounds].nil?
+ keys = options[:compounds] ? Ransack.predicate_keys : Ransack.predicate_keys.reject {|k| k.match(/_(any|all)$/)}
+ if only = options[:only]
+ if only.respond_to? :call
+ keys = keys.select {|k| only.call(k)}
+ else
+ only = Array.wrap(only).map(&:to_s)
+ keys = keys.select {|k| only.include? k.sub(/_(any|all)$/, '')}
+ end
+ end
+
@template.collection_select(
- @object_name, :p, Predicate.collection, :first, :last,
+ @object_name, :p, keys.map {|k| [k, Translate.predicate(k)]}, :first, :last,
objectify_options(options), @default_options.merge(html_options)
)
end
def combinator_select(options = {}, html_options = {})
+ choices = Nodes::Condition === object ? [:or, :and] : [:and, :or]
@template.collection_select(
- @object_name, :m, [['or', Translate.word(:or)], ['and', Translate.word(:and)]], :first, :last,
+ @object_name, :m, choices.map {|o| [o.to_s, Translate.word(o)]}, :first, :last,
objectify_options(options), @default_options.merge(html_options)
)
end
View
3  lib/ransack/nodes.rb
@@ -4,5 +4,4 @@
require 'ransack/nodes/value'
require 'ransack/nodes/condition'
require 'ransack/nodes/sort'
-require 'ransack/nodes/and'
-require 'ransack/nodes/or'
+require 'ransack/nodes/grouping'
View
8 lib/ransack/nodes/and.rb
@@ -1,8 +0,0 @@
-require 'ransack/nodes/grouping'
-
-module Ransack
- module Nodes
- class And < Grouping
- end
- end
-end
View
85 lib/ransack/nodes/grouping.rb
@@ -2,11 +2,20 @@ module Ransack
module Nodes
class Grouping < Node
attr_reader :conditions
+ attr_accessor :combinator
+ alias :m :combinator
+ alias :m= :combinator=
+
i18n_word :condition, :and, :or
i18n_alias :c => :condition, :n => :and, :o => :or
delegate :each, :to => :values
+ def initialize(context, combinator = nil)
+ super(context)
+ self.combinator = combinator.to_s if combinator
+ end
+
def persisted?
false
end
@@ -52,7 +61,7 @@ def []=(key, value)
end
def values
- conditions + ors + ands
+ conditions + groupings
end
def respond_to?(method_id)
@@ -79,51 +88,28 @@ def new_condition(opts = {})
condition
end
- def ands
- @ands ||= []
+ def groupings
+ @groupings ||= []
end
- alias :n :ands
+ alias :g :groupings
- def ands=(ands)
- case ands
+ def groupings=(groupings)
+ case groupings
when Array
- ands.each do |attrs|
- and_object = And.new(@context).build(attrs)
- self.ands << and_object if and_object.values.any?
+ groupings.each do |attrs|
+ grouping_object = new_grouping(attrs)
+ self.groupings << grouping_object if grouping_object.values.any?
end
when Hash
- ands.each do |index, attrs|
- and_object = And.new(@context).build(attrs)
- self.ands << and_object if and_object.values.any?
+ groupings.each do |index, attrs|
+ grouping_object = new_grouping(attrs)
+ self.groupings << grouping_object if grouping_object.values.any?
end
else
- raise ArgumentError, "Invalid argument (#{ands.class}) supplied to ands="
+ raise ArgumentError, "Invalid argument (#{groupings.class}) supplied to groupings="
end
end
- alias :n= :ands=
-
- def ors
- @ors ||= []
- end
- alias :o :ors
-
- def ors=(ors)
- case ors
- when Array
- ors.each do |attrs|
- or_object = Or.new(@context).build(attrs)
- self.ors << or_object if or_object.values.any?
- end
- when Hash
- ors.each do |index, attrs|
- or_object = Or.new(@context).build(attrs)
- self.ors << or_object if or_object.values.any?
- end
- else
- raise ArgumentError, "Invalid argument (#{ors.class}) supplied to ors="
- end
- end
- alias :o= :ors=
+ alias :g= :groupings=
def method_missing(method_id, *args)
method_name = method_id.to_s
@@ -138,39 +124,28 @@ def method_missing(method_id, *args)
def attribute_method?(name)
name = strip_predicate_and_index(name)
case name
- when /^(n|o|c|ands|ors|conditions)=?$/
+ when /^(g|c|m|groupings|conditions|combinator)=?$/
true
else
name.split(/_and_|_or_/).select {|n| !@context.attribute_method?(n)}.empty?
end
end
- def build_and(params = {})
- params ||= {}
- new_and(params).tap do |new_and|
- self.ands << new_and
- end
- end
-
- def new_and(params = {})
- And.new(@context).build(params)
- end
-
- def build_or(params = {})
+ def build_grouping(params = {})
params ||= {}
- new_or(params).tap do |new_or|
- self.ors << new_or
+ new_grouping(params).tap do |new_grouping|
+ self.groupings << new_grouping
end
end
- def new_or(params = {})
- Or.new(@context).build(params)
+ def new_grouping(params = {})
+ Grouping.new(@context).build(params)
end
def build(params)
params.with_indifferent_access.each do |key, value|
case key
- when /^(n|o|c)$/
+ when /^(g|c|m)$/
self.send("#{key}=", value)
else
write_attribute(key.to_s, value)
View
8 lib/ransack/nodes/or.rb
@@ -1,8 +0,0 @@
-require 'ransack/nodes/grouping'
-
-module Ransack
- module Nodes
- class Or < Grouping
- end
- end
-end
View
8 lib/ransack/predicate.rb
@@ -4,15 +4,11 @@ class Predicate
class << self
def named(name)
- Configuration.predicates[name.to_s]
+ Ransack.predicates[name.to_s]
end
def for_attribute_name(attribute_name)
- self.named(Configuration.predicate_keys.detect {|p| attribute_name.to_s.match(/_#{p}$/)})
- end
-
- def collection
- Configuration.predicates.map {|k, v| [k, Translate.predicate(k)]}
+ self.named(Ransack.predicate_keys.detect {|p| attribute_name.to_s.match(/_#{p}$/)})
end
end
View
6 lib/ransack/search.rb
@@ -9,15 +9,15 @@ class Search
attr_reader :base, :context
delegate :object, :klass, :to => :context
- delegate :new_and, :new_or, :new_condition,
- :build_and, :build_or, :build_condition,
+ delegate :new_grouping, :new_condition,
+ :build_grouping, :build_condition,
:translate, :to => :base
def initialize(object, params = {}, options = {})
params ||= {}
@context = Context.for(object)
@context.auth_object = options[:auth_object]
- @base = Nodes::And.new(@context)
+ @base = Nodes::Grouping.new(@context, 'and')
build(params.with_indifferent_access)
end
View
2  lib/ransack/version.rb
@@ -1,3 +1,3 @@
module Ransack
- VERSION = "0.3.0"
+ VERSION = "0.4.0"
end
View
16 lib/ransack/visitor.rb
@@ -17,7 +17,11 @@ def visit_Ransack_Nodes_Condition(object)
object.arel_predicate if object.valid?
end
- def visit_Ransack_Nodes_And(object)
+ def visit_Ransack_Nodes_Grouping(object)
+ object.combinator == 'or' ? visit_or(object) : visit_and(object)
+ end
+
+ def visit_and(object)
nodes = object.values.map {|o| accept(o)}.compact
return nil unless nodes.size > 0
@@ -28,11 +32,7 @@ def visit_Ransack_Nodes_And(object)
end
end
- def visit_Ransack_Nodes_Sort(object)
- object.attr.send(object.dir) if object.valid?
- end
-
- def visit_Ransack_Nodes_Or(object)
+ def visit_or(object)
nodes = object.values.map {|o| accept(o)}.compact
return nil unless nodes.size > 0
@@ -43,6 +43,10 @@ def visit_Ransack_Nodes_Or(object)
end
end
+ def visit_Ransack_Nodes_Sort(object)
+ object.attr.send(object.dir) if object.valid?
+ end
+
def quoted?(object)
case object
when Arel::Nodes::SqlLiteral, Bignum, Fixnum
View
88 spec/ransack/helpers/form_builder_spec.rb
@@ -38,40 +38,82 @@ module Helpers
end
end
- it 'localizes labels' do
- html = @f.label :name_cont
- html.should match /Full Name contains/
- end
+ describe '#label' do
+
+ it 'localizes attribute names' do
+ html = @f.label :name_cont
+ html.should match /Full Name contains/
+ end
- it 'localizes submit' do
- html = @f.submit
- html.should match /"Search"/
end
- it 'returns ransackable attributes for attribute_select' do
- html = @f.attribute_select
- html.split(/\n/).should have(Person.ransackable_attributes.size + 1).lines
- Person.ransackable_attributes.each do |attribute|
- html.should match /<option value="#{attribute}">/
+ describe '#submit' do
+
+ it 'localizes :search when no default value given' do
+ html = @f.submit
+ html.should match /"Search"/
end
+
end
- it 'returns ransackable attributes for associations in attribute_select with associations' do
- attributes = Person.ransackable_attributes + Article.ransackable_attributes.map {|a| "articles_#{a}"}
- html = @f.attribute_select :associations => ['articles']
- html.split(/\n/).should have(attributes.size).lines
- attributes.each do |attribute|
- html.should match /<option value="#{attribute}">/
+ describe '#attribute_select' do
+
+ it 'returns ransackable attributes' do
+ html = @f.attribute_select
+ html.split(/\n/).should have(Person.ransackable_attributes.size + 1).lines
+ Person.ransackable_attributes.each do |attribute|
+ html.should match /<option value="#{attribute}">/
+ end
end
- end
- it 'returns option groups for base and associations in attribute_select with associations' do
- html = @f.attribute_select :associations => ['articles']
- [Person, Article].each do |model|
- html.should match /<optgroup label="#{model}">/
+ it 'returns ransackable attributes for associations with :associations' do
+ attributes = Person.ransackable_attributes + Article.ransackable_attributes.map {|a| "articles_#{a}"}
+ html = @f.attribute_select :associations => ['articles']
+ html.split(/\n/).should have(attributes.size).lines
+ attributes.each do |attribute|
+ html.should match /<option value="#{attribute}">/
+ end
end
+
+ it 'returns option groups for base and associations with :associations' do
+ html = @f.attribute_select :associations => ['articles']
+ [Person, Article].each do |model|
+ html.should match /<optgroup label="#{model}">/
+ end
+ end
+
end
+ describe '#predicate_select' do
+
+ it 'returns predicates with predicate_select' do
+ html = @f.predicate_select
+ Ransack.predicate_keys.each do |key|
+ html.should match /<option value="#{key}">/
+ end
+ end
+
+ it 'filters predicates with single-value :only' do
+ html = @f.predicate_select :only => 'eq'
+ Ransack.predicate_keys.reject {|k| k =~ /^eq/}.each do |key|
+ html.should_not match /<option value="#{key}">/
+ end
+ end
+
+ it 'filters predicates with multi-value :only' do
+ html = @f.predicate_select :only => [:eq, :lt]
+ Ransack.predicate_keys.reject {|k| k =~ /^(eq|lt)/}.each do |key|
+ html.should_not match /<option value="#{key}">/
+ end
+ end
+
+ it 'excludes compounds when :compounds => false' do
+ html = @f.predicate_select :compounds => false
+ Ransack.predicate_keys.select {|k| k =~ /_(any|all)$/}.each do |key|
+ html.should_not match /<option value="#{key}">/
+ end
+ end
+ end
end
end
end
View
45 spec/ransack/search_spec.rb
@@ -39,30 +39,34 @@ module Ransack
it 'accepts arrays of groupings' do
search = Search.new(Person,
- :o => [
- {:name_eq => 'Ernie', :children_name_eq => 'Ernie'},
- {:name_eq => 'Bert', :children_name_eq => 'Bert'},
+ :g => [
+ {:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'},
+ {:m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert'},
]
)
- ors = search.ors
+ ors = search.groupings
ors.should have(2).items
or1, or2 = ors
- or1.should be_a Nodes::Or
- or2.should be_a Nodes::Or
+ or1.should be_a Nodes::Grouping
+ or1.combinator.should eq 'or'
+ or2.should be_a Nodes::Grouping
+ or2.combinator.should eq 'or'
end
it 'accepts "attributes" hashes for groupings' do
search = Search.new(Person,
- :o => {
- '0' => {:name_eq => 'Ernie', :children_name_eq => 'Ernie'},
- '1' => {:name_eq => 'Bert', :children_name_eq => 'Bert'},
+ :g => {
+ '0' => {:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'},
+ '1' => {:m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert'},
}
)
- ors = search.ors
+ ors = search.groupings
ors.should have(2).items
or1, or2 = ors
- or1.should be_a Nodes::Or
- or2.should be_a Nodes::Or
+ or1.should be_a Nodes::Grouping
+ or1.combinator.should eq 'or'
+ or2.should be_a Nodes::Grouping
+ or2.combinator.should eq 'or'
end
it 'accepts "attributes" hashes for conditions' do
@@ -102,7 +106,8 @@ module Ransack
it 'evaluates nested conditions' do
search = Search.new(Person, :children_name_eq => 'Ernie',
- :o => [{
+ :g => [{
+ :m => 'or',
:name_eq => 'Ernie',
:children_children_name_eq => 'Ernie'
}]
@@ -114,9 +119,9 @@ module Ransack
it 'evaluates arrays of groupings' do
search = Search.new(Person,
- :o => [
- {:name_eq => 'Ernie', :children_name_eq => 'Ernie'},
- {:name_eq => 'Bert', :children_name_eq => 'Bert'},
+ :g => [
+ {:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'},
+ {:m => 'or', :name_eq => 'Bert', :children_name_eq => 'Bert'},
]
)
search.result.should be_an ActiveRecord::Relation
@@ -125,7 +130,7 @@ module Ransack
end
it 'returns distinct records when passed :distinct => true' do
- search = Search.new(Person, :o => [{:comments_body_cont => 'e', :articles_comments_body_cont => 'e'}])
+ search = Search.new(Person, :g => [{:m => 'or', :comments_body_cont => 'e', :articles_comments_body_cont => 'e'}])
search.result.should have(920).items
search.result(:distinct => true).should have(330).items
search.result.all.uniq.should eq search.result(:distinct => true).all
@@ -195,9 +200,9 @@ module Ransack
end
it 'allows chaining to access nested conditions' do
- @s.ors = [{:name_eq => 'Ernie', :children_name_eq => 'Ernie'}]
- @s.ors.first.name_eq.should eq 'Ernie'
- @s.ors.first.children_name_eq.should eq 'Ernie'
+ @s.groupings = [{:m => 'or', :name_eq => 'Ernie', :children_name_eq => 'Ernie'}]
+ @s.groupings.first.name_eq.should eq 'Ernie'
+ @s.groupings.first.children_name_eq.should eq 'Ernie'
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.