Permalink
Browse files

Added any or all named scopes. See README.

  • Loading branch information...
binarylogic committed Jun 10, 2009
1 parent 0e5912a commit 9744f24e055cea2d7ac881af566e2a85e46b7fbf
View
@@ -29,7 +29,7 @@ Instead of explaining what Searchlogic can do, let me show you. Let's start at t
User.descend_by_username
User.order("ascend_by_username")
-Keep in mind, these are just named scopes, you can chain them, call methods off of them, etc:
+Any named scope Searchlogic creates is dynamic and created via method_missing. Meaning it will only create what you need. Also, keep in mind, these are just named scopes, you can chain them, call methods off of them, etc:
scope = User.username_like("bjohnson").age_greater_than(20).ascend_by_username
scope.all
@@ -120,6 +120,16 @@ Now just throw it in your form:
What's great about this is that you can do just about anything you want. If Searchlogic doesn't provide a named scope for that crazy edge case that you need, just create your own named scope. The sky is the limit.
+== Use any or all
+
+Every condition you've seen in this readme also has 2 related conditions that you can use. Example:
+
+ User.username_like_any("bjohnson", "thunt") # will return any users that have either of those strings in their username
+ User.username_like_all("bjohnson", "thunt") # will return any users that have all of those string in their username
+ User.username_like_any(["bjohnson", "thunt"]) # also accept an array
+
+This is great for checkbox filters, etc. Where you can pass an array right from your form to this condition.
+
== Pagination (leverage will_paginate)
Instead of recreating the wheel with pagination, Searchlogic works great with will_paginate. All that Searchlogic is doing is creating named scopes, and will_paginate works great with named scopes:
@@ -1,19 +1,36 @@
module Searchlogic
module NamedScopes
module Conditions
- CONDITIONS = {
+ COMPARISON_CONDITIONS = {
:equals => [:is, :eq],
:does_not_equal => [:not_equal_to, :is_not, :not, :ne],
:less_than => [:lt, :before],
:less_than_or_equal_to => [:lte],
:greater_than => [:gt, :after],
:greater_than_or_equal_to => [:gte],
+ }
+
+ WILDCARD_CONDITIONS = {
:like => [:contains, :includes],
:begins_with => [:bw],
:ends_with => [:ew],
+ }
+
+ BOOLEAN_CONDITIONS = {
:null => [:nil],
:empty => []
}
+
+ CONDITIONS = {}
+
+ COMPARISON_CONDITIONS.merge(WILDCARD_CONDITIONS).each do |condition, aliases|
+ CONDITIONS[condition] = aliases
+ CONDITIONS["#{condition}_any".to_sym] = aliases.collect { |a| "#{a}_any".to_sym }
+ CONDITIONS["#{condition}_all".to_sym] = aliases.collect { |a| "#{a}_all".to_sym }
+ end
+
+ BOOLEAN_CONDITIONS.each { |condition, aliases| CONDITIONS[condition] = aliases }
+
PRIMARY_CONDITIONS = CONDITIONS.keys
ALIAS_CONDITIONS = CONDITIONS.values.flatten
@@ -47,43 +64,80 @@ def method_missing(name, *args, &block)
end
def primary_condition_details(name)
- if name.to_s =~ /(\w+)_(#{PRIMARY_CONDITIONS.join("|")})$/
+ if name.to_s =~ /^(\w+)_(#{PRIMARY_CONDITIONS.join("|")})$/
{:column => $1, :condition => $2}
end
end
def create_primary_condition(column, condition)
column_type = columns_hash[column].type
- scope = case condition.to_sym
- when :equals
- searchlogic_lambda(column_type) { |value| { :conditions => { column => value } } }
- when :does_not_equal
- searchlogic_lambda(column_type) { |value| { :conditions => { column => value } } }
- when :less_than
- searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} < ?", value] } }
- when:less_than_or_equal_to
- searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} <= ?", value] } }
- when :greater_than
- searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} > ?", value] } }
- when :greater_than_or_equal_to
- searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} >= ?", value] } }
+ scope_options = case condition.to_s
+ when /^equals/
+ scope_options(condition, column_type, "#{table_name}.#{column} = ?")
+ when /^does_not_equal/
+ scope_options(condition, column_type, "#{table_name}.#{column} != ?")
+ when /^less_than_or_equal_to/
+ scope_options(condition, column_type, "#{table_name}.#{column} <= ?")
+ when /^less_than/
+ scope_options(condition, column_type, "#{table_name}.#{column} < ?")
+ when /^greater_than_or_equal_to/
+ scope_options(condition, column_type, "#{table_name}.#{column} >= ?")
+ when /^greater_than/
+ scope_options(condition, column_type, "#{table_name}.#{column} > ?")
+ when /^like/
+ scope_options(condition, column_type, "#{table_name}.#{column} LIKE ?", :like)
+ when /^begins_with/
+ scope_options(condition, column_type, "#{table_name}.#{column} LIKE ?", :begins_with)
+ when /^ends_with/
+ scope_options(condition, column_type, "#{table_name}.#{column} LIKE ?", :ends_with)
+ when "null"
+ {:conditions => "#{table_name}.#{column} IS NULL"}
+ when "empty"
+ {:conditions => "#{table_name}.#{column} = ''"}
+ end
+
+ named_scope("#{column}_#{condition}".to_sym, scope_options)
+ end
+
+ # This method helps cut down on defining scope options for conditions that allow *_any or *_all conditions.
+ # Kepp in mind that the lambdas get cached in a method, so you want to keep the contents of the lambdas as
+ # fast as possible, which is why I didn't do the case statement inside of the lambda.
+ def scope_options(condition, column_type, sql, value_modifier = nil)
+ case condition.to_s
+ when /_(any|all)$/
+ searchlogic_lambda(column_type) { |*values|
+ values = values.flatten
+
+ values_to_sub = nil
+ if value_modifier.nil?
+ values_to_sub = values
+ else
+ values_to_sub = values.collect { |value| value_with_modifier(value, value_modifier) }
+ end
+
+ join = $1 == "any" ? " OR " : " AND "
+ {:conditions => [values.collect { |value| sql }.join(join), *values_to_sub]}
+ }
+ else
+ searchlogic_lambda(column_type) { |value| {:conditions => [sql, value_with_modifier(value, value_modifier)]} }
+ end
+ end
+
+ def value_with_modifier(value, modifier)
+ case modifier
when :like
- searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} LIKE ?", "%#{value}%"] } }
+ "%#{value}%"
when :begins_with
- searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} LIKE ?", "#{value}%"] } }
+ "#{value}%"
when :ends_with
- searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} LIKE ?", "%#{value}"] } }
- when :null
- { :conditions => "#{table_name}.#{column} IS NULL" }
- when :empty
- { :conditions => "#{table_name}.#{column} = ''" }
+ "%#{value}"
+ else
+ value
end
-
- named_scope("#{column}_#{condition}".to_sym, scope)
end
def alias_condition_details(name)
- if name.to_s =~ /(\w+)_(#{ALIAS_CONDITIONS.join("|")})$/
+ if name.to_s =~ /^(\w+)_(#{ALIAS_CONDITIONS.join("|")})$/
{:column => $1, :condition => $2}
end
end
@@ -0,0 +1,7 @@
+require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
+
+describe "Object" do
+ it "should accept and pass the argument to the searchlogic_arg_type" do
+ searchlogic_lambda(:integer) {}.searchlogic_arg_type.should == :integer
+ end
+end
@@ -0,0 +1,9 @@
+require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
+
+describe "Proc" do
+ it "should have a searchlogic_arg_type accessor" do
+ p = Proc.new {}
+ p.searchlogic_arg_type = :integer
+ p.searchlogic_arg_type.should == :integer
+ end
+end
@@ -13,6 +13,11 @@
User.age_equals(6).all.should == User.find_all_by_age(6)
end
+ it "should have does not equal" do
+ (5..7).each { |age| User.create(:age => age) }
+ User.age_does_not_equal(6).all.should == User.find_all_by_age([5,7])
+ end
+
it "should have less than" do
(5..7).each { |age| User.create(:age => age) }
User.age_less_than(6).all.should == User.find_all_by_age(5)
@@ -63,6 +68,99 @@
end
end
+ context "any and all conditions" do
+ it "should have equals any" do
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
+ User.username_equals_any("bjohnson", "thunt").all == User.find_all_by_username(["bjohnson", "thunt"])
+ end
+
+ it "should have equals all" do
+ %w(bjohnson thunt dainor).each { |username| User.create(:username => username) }
+ User.username_equals_all("bjohnson", "thunt").all == []
+ end
+
+ it "should have does not equal any" do
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
+ User.username_does_not_equal_any("bjohnson", "thunt").all == User.find_all_by_username("dgainor")
+ end
+
+ it "should have does not equal all" do
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
+ User.username_does_not_equal_all("bjohnson", "thunt").all == User.find_all_by_username("dgainor")
+ end
+
+ it "should have less than any" do
+ (5..7).each { |age| User.create(:age => age) }
+ User.age_less_than_any(7,6).all == User.find_all_by_age([5, 6])
+ end
+
+ it "should have less than all" do
+ (5..7).each { |age| User.create(:age => age) }
+ User.age_less_than_all(7,6).all == User.find_all_by_age(5)
+ end
+
+ it "should have less than or equal to any" do
+ (5..7).each { |age| User.create(:age => age) }
+ User.age_less_than_or_equal_to_any(7,6).all == User.find_all_by_age([5, 6, 7])
+ end
+
+ it "should have less than or equal to all" do
+ (5..7).each { |age| User.create(:age => age) }
+ User.age_less_than_or_equal_to_all(7,6).all == User.find_all_by_age([5, 6])
+ end
+
+ it "should have less than any" do
+ (5..7).each { |age| User.create(:age => age) }
+ User.age_greater_than_any(5,6).all == User.find_all_by_age([6, 7])
+ end
+
+ it "should have greater than all" do
+ (5..7).each { |age| User.create(:age => age) }
+ User.age_greater_than_all(5,6).all == User.find_all_by_age(7)
+ end
+
+ it "should have greater than or equal to any" do
+ (5..7).each { |age| User.create(:age => age) }
+ User.age_greater_than_or_equal_to_any(5,6).all == User.find_all_by_age([5, 6, 7])
+ end
+
+ it "should have greater than or equal to all" do
+ (5..7).each { |age| User.create(:age => age) }
+ User.age_greater_than_or_equal_to_all(5,6).all == User.find_all_by_age([6, 7])
+ end
+
+ it "should have like all" do
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
+ User.username_like_all("bjohnson", "thunt").all == []
+ User.username_like_all("n", "o").all == User.find_all_by_username(["bjohnson", "thunt"])
+ end
+
+ it "should have like any" do
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
+ User.username_like_all("bjohnson", "thunt").all == User.find_all_by_username(["bjohnson", "thunt"])
+ end
+
+ it "should have begins with all" do
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
+ User.username_begins_with_all("bjohnson", "thunt").all == []
+ end
+
+ it "should have begins with any" do
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
+ User.username_begins_with_any("bj", "th").all == User.find_all_by_username(["bjohnson", "thunt"])
+ end
+
+ it "should have ends with all" do
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
+ User.username_ends_with_all("n", "r").all == []
+ end
+
+ it "should have ends with any" do
+ %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
+ User.username_ends_with_any("n", "r").all == User.find_all_by_username(["bjohnson", "dgainor"])
+ end
+ end
+
context "alias conditions" do
it "should have is" do
User.age_is(5).proxy_options.should == User.age_equals(5).proxy_options
@@ -151,6 +151,9 @@
search.created_at_after = "Jan 1, 2009 9:33AM"
search.created_at_after.should == Time.parse("Jan 1, 2009 9:33AM")
end
+
+ #it "should be an Array and cast it's values given ['1', '2', '3']" do
+ # search
end
end

0 comments on commit 9744f24

Please sign in to comment.