Skip to content

Commit

Permalink
Added any or all named scopes. See README.
Browse files Browse the repository at this point in the history
  • Loading branch information
binarylogic committed Jun 10, 2009
1 parent 0e5912a commit 9744f24
Show file tree
Hide file tree
Showing 6 changed files with 207 additions and 26 deletions.
12 changes: 11 additions & 1 deletion README.rdoc
Expand Up @@ -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.descend_by_username
User.order("ascend_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 = User.username_like("bjohnson").age_greater_than(20).ascend_by_username
scope.all scope.all
Expand Down Expand Up @@ -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. 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) == 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: 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:
Expand Down
104 changes: 79 additions & 25 deletions lib/searchlogic/named_scopes/conditions.rb
@@ -1,19 +1,36 @@
module Searchlogic module Searchlogic
module NamedScopes module NamedScopes
module Conditions module Conditions
CONDITIONS = { COMPARISON_CONDITIONS = {
:equals => [:is, :eq], :equals => [:is, :eq],
:does_not_equal => [:not_equal_to, :is_not, :not, :ne], :does_not_equal => [:not_equal_to, :is_not, :not, :ne],
:less_than => [:lt, :before], :less_than => [:lt, :before],
:less_than_or_equal_to => [:lte], :less_than_or_equal_to => [:lte],
:greater_than => [:gt, :after], :greater_than => [:gt, :after],
:greater_than_or_equal_to => [:gte], :greater_than_or_equal_to => [:gte],
}

WILDCARD_CONDITIONS = {
:like => [:contains, :includes], :like => [:contains, :includes],
:begins_with => [:bw], :begins_with => [:bw],
:ends_with => [:ew], :ends_with => [:ew],
}

BOOLEAN_CONDITIONS = {
:null => [:nil], :null => [:nil],
:empty => [] :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 PRIMARY_CONDITIONS = CONDITIONS.keys
ALIAS_CONDITIONS = CONDITIONS.values.flatten ALIAS_CONDITIONS = CONDITIONS.values.flatten


Expand Down Expand Up @@ -47,43 +64,80 @@ def method_missing(name, *args, &block)
end end


def primary_condition_details(name) 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} {:column => $1, :condition => $2}
end end
end end


def create_primary_condition(column, condition) def create_primary_condition(column, condition)
column_type = columns_hash[column].type column_type = columns_hash[column].type
scope = case condition.to_sym scope_options = case condition.to_s
when :equals when /^equals/
searchlogic_lambda(column_type) { |value| { :conditions => { column => value } } } scope_options(condition, column_type, "#{table_name}.#{column} = ?")
when :does_not_equal when /^does_not_equal/
searchlogic_lambda(column_type) { |value| { :conditions => { column => value } } } scope_options(condition, column_type, "#{table_name}.#{column} != ?")
when :less_than when /^less_than_or_equal_to/
searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} < ?", value] } } scope_options(condition, column_type, "#{table_name}.#{column} <= ?")
when:less_than_or_equal_to when /^less_than/
searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} <= ?", value] } } scope_options(condition, column_type, "#{table_name}.#{column} < ?")
when :greater_than when /^greater_than_or_equal_to/
searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} > ?", value] } } scope_options(condition, column_type, "#{table_name}.#{column} >= ?")
when :greater_than_or_equal_to when /^greater_than/
searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} >= ?", value] } } 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 when :like
searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} LIKE ?", "%#{value}%"] } } "%#{value}%"
when :begins_with when :begins_with
searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} LIKE ?", "#{value}%"] } } "#{value}%"
when :ends_with when :ends_with
searchlogic_lambda(column_type) { |value| { :conditions => ["#{table_name}.#{column} LIKE ?", "%#{value}"] } } "%#{value}"
when :null else
{ :conditions => "#{table_name}.#{column} IS NULL" } value
when :empty
{ :conditions => "#{table_name}.#{column} = ''" }
end end

named_scope("#{column}_#{condition}".to_sym, scope)
end end


def alias_condition_details(name) 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} {:column => $1, :condition => $2}
end end
end end
Expand Down
7 changes: 7 additions & 0 deletions spec/core_ext/object_spec.rb
@@ -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
9 changes: 9 additions & 0 deletions spec/core_ext/proc_spec.rb
@@ -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
98 changes: 98 additions & 0 deletions spec/named_scopes/conditions_spec.rb
Expand Up @@ -13,6 +13,11 @@
User.age_equals(6).all.should == User.find_all_by_age(6) User.age_equals(6).all.should == User.find_all_by_age(6)
end 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 it "should have less than" do
(5..7).each { |age| User.create(:age => age) } (5..7).each { |age| User.create(:age => age) }
User.age_less_than(6).all.should == User.find_all_by_age(5) User.age_less_than(6).all.should == User.find_all_by_age(5)
Expand Down Expand Up @@ -63,6 +68,99 @@
end end
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 context "alias conditions" do
it "should have is" do it "should have is" do
User.age_is(5).proxy_options.should == User.age_equals(5).proxy_options User.age_is(5).proxy_options.should == User.age_equals(5).proxy_options
Expand Down
3 changes: 3 additions & 0 deletions spec/search_proxy_spec.rb
Expand Up @@ -151,6 +151,9 @@
search.created_at_after = "Jan 1, 2009 9:33AM" search.created_at_after = "Jan 1, 2009 9:33AM"
search.created_at_after.should == Time.parse("Jan 1, 2009 9:33AM") search.created_at_after.should == Time.parse("Jan 1, 2009 9:33AM")
end end

#it "should be an Array and cast it's values given ['1', '2', '3']" do
# search
end end
end end


Expand Down

0 comments on commit 9744f24

Please sign in to comment.