Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Code cleanup

  • Loading branch information...
commit ddac0223dc2180d39932d3a86aa6bc40ee740f37 1 parent e152ea2
@binarylogic binarylogic authored
View
6 lib/searchlogic.rb
@@ -3,7 +3,8 @@
require "searchlogic/active_record/association_proxy"
require "searchlogic/active_record/consistency"
require "searchlogic/active_record/named_scope_tools"
-require "searchlogic/named_scopes/conditions"
+require "searchlogic/named_scopes/base"
+require "searchlogic/named_scopes/column_conditions"
require "searchlogic/named_scopes/ordering"
require "searchlogic/named_scopes/association_conditions"
require "searchlogic/named_scopes/association_ordering"
@@ -36,7 +37,8 @@ class << self; include Searchlogic::ActiveRecord::Consistency; end
end
ActiveRecord::Base.extend(Searchlogic::ActiveRecord::NamedScopeTools)
-ActiveRecord::Base.extend(Searchlogic::NamedScopes::Conditions)
+ActiveRecord::Base.extend(Searchlogic::NamedScopes::Base)
+ActiveRecord::Base.extend(Searchlogic::NamedScopes::ColumnConditions)
ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationConditions)
ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationOrdering)
ActiveRecord::Base.extend(Searchlogic::NamedScopes::Ordering)
View
8 lib/searchlogic/named_scopes/association_conditions.rb
@@ -11,11 +11,13 @@ def association_condition?(name)
!association_condition_details(name).nil? unless name.to_s.downcase.match("_or_")
end
+ # We need to try and create other conditions first so that we give priority to conflicting names.
+ # Such as having a column names the exact same name as an association condition.
def create_condition(name)
- if !local_condition?(name) && details = association_condition_details(name)
+ if result = super
+ result
+ elsif details = association_condition_details(name)
create_association_condition(details[:association], details[:condition], details[:poly_class])
- else
- super
end
end
View
251 lib/searchlogic/named_scopes/conditions.rb
@@ -1,251 +0,0 @@
-module Searchlogic
- module NamedScopes
- # Handles dynamically creating named scopes for columns. It allows you to do things like:
- #
- # User.first_name_like("ben")
- # User.id_lt(10)
- #
- # Notice the constants in this class, they define which conditions Searchlogic provides.
- #
- # See the README for a more detailed explanation.
- module 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],
- :not_like => [:does_not_include],
- :begins_with => [:bw],
- :not_begin_with => [:does_not_begin_with],
- :ends_with => [:ew],
- :not_end_with => [:does_not_end_with]
- }
-
- BOOLEAN_CONDITIONS = {
- :null => [:nil],
- :not_null => [:not_nil],
- :empty => [],
- :blank => [],
- :not_blank => [:present]
- }
-
- CONDITIONS = {}
-
- # Add any / all variations to every comparison and wildcard condition
- 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
-
- CONDITIONS[:equals_any] = CONDITIONS[:equals_any] + [:in]
- CONDITIONS[:does_not_equal_all] = CONDITIONS[:does_not_equal_all] + [:not_in]
-
- BOOLEAN_CONDITIONS.each { |condition, aliases| CONDITIONS[condition] = aliases }
-
- PRIMARY_CONDITIONS = CONDITIONS.keys
- ALIAS_CONDITIONS = CONDITIONS.values.flatten
-
- # Is the name of the method a valid condition that can be dynamically created?
- def condition?(name)
- local_condition?(name)
- end
-
- # We want to return true for any conditions that can be called, and while we're at it. We might as well
- # create the condition so we don't have to do it again.
- def respond_to?(*args)
- name = args.first
- result = super
- (!result && self != ::ActiveRecord::Base && !create_condition(name).blank?) || result
- end
-
- private
- def local_condition?(name)
- return false if name.blank?
- scope_names = scopes.keys.reject { |k| k == :scoped }
- scope_names.include?(name.to_sym) || !condition_details(name).nil? || boolean_condition?(name)
- end
-
- def boolean_condition?(name)
- column = columns_hash[name.to_s] || columns_hash[name.to_s.gsub(/^not_/, "")]
- column && column.type == :boolean
- end
-
- def method_missing(name, *args, &block)
- if create_condition(name)
- send(name, *args, &block)
- else
- super
- end
- end
-
- def condition_details(method_name)
- column_name_matcher = column_names.join("|")
- conditions_matcher = (PRIMARY_CONDITIONS + ALIAS_CONDITIONS).join("|")
-
- if method_name.to_s =~ /^(#{column_name_matcher})_(#{conditions_matcher})$/
- {:column => $1, :condition => $2}
- end
- end
-
- def create_condition(name)
- if details = condition_details(name)
- if PRIMARY_CONDITIONS.include?(details[:condition].to_sym)
- create_primary_condition(details[:column], details[:condition])
- elsif ALIAS_CONDITIONS.include?(details[:condition].to_sym)
- create_alias_condition(details[:column], details[:condition])
- end
-
- elsif boolean_condition?(name)
- column = name.to_s.gsub(/^not_/, "")
- named_scope name, :conditions => {column => (name.to_s =~ /^not_/).nil?}
- end
- end
-
- def create_primary_condition(column_name, condition)
- column = columns_hash[column_name.to_s]
- column_type = column.type
- skip_conversion = skip_time_zone_conversion_for_attributes.include?(column.name.to_sym)
- match_keyword = ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
-
- scope_options = case condition.to_s
- when /^equals/
- scope_options(condition, column, lambda { |a| attribute_condition("#{table_name}.#{column.name}", a) }, :skip_conversion => skip_conversion)
- when /^does_not_equal/
- scope_options(condition, column, "#{table_name}.#{column.name} != ?", :skip_conversion => skip_conversion)
- when /^less_than_or_equal_to/
- scope_options(condition, column, "#{table_name}.#{column.name} <= ?", :skip_conversion => skip_conversion)
- when /^less_than/
- scope_options(condition, column, "#{table_name}.#{column.name} < ?", :skip_conversion => skip_conversion)
- when /^greater_than_or_equal_to/
- scope_options(condition, column, "#{table_name}.#{column.name} >= ?", :skip_conversion => skip_conversion)
- when /^greater_than/
- scope_options(condition, column, "#{table_name}.#{column.name} > ?", :skip_conversion => skip_conversion)
- when /^like/
- scope_options(condition, column, "#{table_name}.#{column.name} #{match_keyword} ?", :skip_conversion => skip_conversion, :value_modifier => :like)
- when /^not_like/
- scope_options(condition, column, "#{table_name}.#{column.name} NOT #{match_keyword} ?", :skip_conversion => skip_conversion, :value_modifier => :like)
- when /^begins_with/
- scope_options(condition, column, "#{table_name}.#{column.name} #{match_keyword} ?", :skip_conversion => skip_conversion, :value_modifier => :begins_with)
- when /^not_begin_with/
- scope_options(condition, column, "#{table_name}.#{column.name} NOT #{match_keyword} ?", :skip_conversion => skip_conversion, :value_modifier => :begins_with)
- when /^ends_with/
- scope_options(condition, column, "#{table_name}.#{column.name} #{match_keyword} ?", :skip_conversion => skip_conversion, :value_modifier => :ends_with)
- when /^not_end_with/
- scope_options(condition, column, "#{table_name}.#{column.name} NOT #{match_keyword} ?", :skip_conversion => skip_conversion, :value_modifier => :ends_with)
- when "null"
- {:conditions => "#{table_name}.#{column.name} IS NULL"}
- when "not_null"
- {:conditions => "#{table_name}.#{column.name} IS NOT NULL"}
- when "empty"
- {:conditions => "#{table_name}.#{column.name} = ''"}
- when "blank"
- {:conditions => "#{table_name}.#{column.name} = '' OR #{table_name}.#{column.name} IS NULL"}
- when "not_blank"
- {:conditions => "#{table_name}.#{column.name} != '' AND #{table_name}.#{column.name} IS NOT NULL"}
- end
-
- named_scope("#{column.name}_#{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, sql, options = {})
- case condition.to_s
- when /_(any|all)$/
- any = $1 == "any"
- join_word = any ? " OR " : " AND "
- equals = condition.to_s =~ /^equals_/
- searchlogic_lambda(column.type, :skip_conversion => options[:skip_conversion]) { |*values|
- unless values.empty?
- if equals && any
- has_nil = values.include?(nil)
- values = values.flatten.compact
- sql = attribute_condition("#{table_name}.#{column.name}", values)
- subs = [values]
-
- if has_nil
- sql += " OR " + attribute_condition("#{table_name}.#{column.name}", nil)
- subs << nil
- end
-
- {:conditions => [sql, *subs]}
- else
- values.flatten!
- values.collect! { |value| value_with_modifier(value, options[:value_modifier]) }
-
- scope_sql = values.collect { |value| sql.is_a?(Proc) ? sql.call(value) : sql }.join(join_word)
-
- {:conditions => [scope_sql, *expand_range_bind_variables(values)]}
- end
- else
- {}
- end
- }
- else
- searchlogic_lambda(column.type, :skip_conversion => options[:skip_conversion]) { |*values|
- values.collect! { |value| value_with_modifier(value, options[:value_modifier]) }
-
- scope_sql = sql.is_a?(Proc) ? sql.call(*values) : sql
-
- {:conditions => [scope_sql, *expand_range_bind_variables(values)]}
- }
- end
- end
-
- def value_with_modifier(value, modifier)
- case modifier
- when :like
- "%#{value}%"
- when :begins_with
- "#{value}%"
- when :ends_with
- "%#{value}"
- else
- value
- end
- end
-
- def create_alias_condition(column_name, condition)
- primary_condition = primary_condition(condition)
- alias_name = "#{column_name}_#{condition}"
- primary_name = "#{column_name}_#{primary_condition}"
- if respond_to?(primary_name)
- (class << self; self; end).class_eval { alias_method alias_name, primary_name }
- end
- end
-
- # Returns the primary condition for the given alias. Ex:
- #
- # primary_condition(:gt) => :greater_than
- def primary_condition(alias_condition)
- CONDITIONS.find { |k, v| k == alias_condition.to_sym || v.include?(alias_condition.to_sym) }.first
- end
-
- # Returns the primary name for any condition on a column. You can pass it
- # a primary condition, alias condition, etc, and it will return the proper
- # primary condition name. This helps simply logic throughout Searchlogic. Ex:
- #
- # condition_scope_name(:id_gt) => :id_greater_than
- # condition_scope_name(:id_greater_than) => :id_greater_than
- def condition_scope_name(name)
- if details = condition_details(name)
- if PRIMARY_CONDITIONS.include?(name.to_sym)
- name
- else
- "#{details[:column]}_#{primary_condition(details[:condition])}".to_sym
- end
- else
- nil
- end
- end
- end
- end
-end
View
2  lib/searchlogic/named_scopes/or_conditions.rb
@@ -81,7 +81,7 @@ def interpolate_or_conditions(parts)
path = full_association_path(part, last_condition, association_details[:association])
conditions << "#{path[:path].join("_").to_sym}_#{path[:column]}_#{path[:condition]}"
last_condition = path[:condition] || nil
- elsif local_condition?(part)
+ elsif column_condition?(part)
# We are a custom scope
conditions << part
elsif column_names.include?(part)
View
329 spec/searchlogic/named_scopes/conditions_spec.rb
@@ -1,329 +0,0 @@
-require File.expand_path(File.dirname(__FILE__) + "/../../spec_helper")
-
-describe Searchlogic::NamedScopes::Conditions do
- it "should be dynamically created and then cached" do
- User.scopes.key?(:age_less_than).should == false
- User.age_less_than(5)
- User.scopes.key?(:age_less_than).should == true
- end
-
- it "should respond to the scope" do
- User.should respond_to(:age_less_than)
- end
-
- it "should not allow conditions on non columns" do
- lambda { User.whatever_equals(2) }.should raise_error(NoMethodError)
- end
-
- context "comparison conditions" do
- it "should have equals" do
- (5..7).each { |age| User.create(:age => age) }
- User.age_equals(6).all.should == User.find_all_by_age(6)
- User.age_equals(5..6).all.should == User.find_all_by_age(5..6)
- User.age_equals([5, 7]).all.should == User.find_all_by_age([5, 7])
- 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)
- end
-
- it "should have less than or equal to" do
- (5..7).each { |age| User.create(:age => age) }
- User.age_less_than_or_equal_to(6).all.should == User.find_all_by_age([5, 6])
- end
-
- it "should have greater than" do
- (5..7).each { |age| User.create(:age => age) }
- User.age_greater_than(6).all.should == User.find_all_by_age(7)
- end
-
- it "should have greater than or equal to" do
- (5..7).each { |age| User.create(:age => age) }
- User.age_greater_than_or_equal_to(6).all.should == User.find_all_by_age([6, 7])
- end
- end
-
- context "wildcard conditions" do
- it "should have like" do
- %w(bjohnson thunt).each { |username| User.create(:username => username) }
- User.username_like("john").all.should == User.find_all_by_username("bjohnson")
- end
-
- it "should have not like" do
- %w(bjohnson thunt).each { |username| User.create(:username => username) }
- User.username_not_like("john").all.should == User.find_all_by_username("thunt")
- end
-
- it "should have begins with" do
- %w(bjohnson thunt).each { |username| User.create(:username => username) }
- User.username_begins_with("bj").all.should == User.find_all_by_username("bjohnson")
- end
-
- it "should have not begin with" do
- %w(bjohnson thunt).each { |username| User.create(:username => username) }
- User.username_not_begin_with("bj").all.should == User.find_all_by_username("thunt")
- end
-
- it "should have ends with" do
- %w(bjohnson thunt).each { |username| User.create(:username => username) }
- User.username_ends_with("son").all.should == User.find_all_by_username("bjohnson")
- end
-
- it "should have not end with" do
- %w(bjohnson thunt).each { |username| User.create(:username => username) }
- User.username_not_end_with("son").all.should == User.find_all_by_username("thunt")
- end
- end
-
- context "boolean conditions" do
- it "should have scopes for boolean columns" do
- female = User.create(:male => false)
- male = User.create(:male => true)
- User.male.all.should == [male]
- User.not_male.all.should == [female]
- end
-
- it "should have null" do
- ["bjohnson", nil].each { |username| User.create(:username => username) }
- User.username_null.all.should == User.find_all_by_username(nil)
- end
-
- it "should have not null" do
- ["bjohnson", nil].each { |username| User.create(:username => username) }
- User.username_not_null.all.should == User.find_all_by_username("bjohnson")
- end
-
- it "should have empty" do
- ["bjohnson", ""].each { |username| User.create(:username => username) }
- User.username_empty.all.should == User.find_all_by_username("")
- end
-
- it "should have blank" do
- ["bjohnson", "", nil].each { |username| User.create(:username => username) }
- User.username_blank.all.should == [User.find_by_username(""), User.find_by_username(nil)]
- end
-
- it "should have not blank" do
- ["bjohnson", "", nil].each { |username| User.create(:username => username) }
- User.username_not_blank.all.should == User.find_all_by_username("bjohnson")
- end
- end
-
- context "any and all conditions" do
- it "should do nothing if no arguments are passed" do
- User.username_equals_any.proxy_options.should == {}
- end
-
- it "should treat an array and multiple arguments the same" do
- %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
- User.username_like_any("bjohnson", "thunt").should == User.username_like_any(["bjohnson", "thunt"])
- end
-
- it "should have equals any" do
- %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
- User.username_equals_any("bjohnson", "thunt").all.should == User.find_all_by_username(["bjohnson", "thunt"])
- end
-
- # PostgreSQL does not allow null in "in" statements
- it "should have equals any and handle nils" do
- %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
- User.username_equals_any("bjohnson", "thunt", nil).proxy_options.should == {:conditions=>["users.username IN (?) OR users.username IS ?", ["bjohnson", "thunt"], nil]}
- end
-
- it "should have equals all" do
- %w(bjohnson thunt dainor).each { |username| User.create(:username => username) }
- User.username_equals_all("bjohnson", "thunt").all.should == []
- 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.should == User.find_all_by_username(["bjohnson", "thunt", "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.should == 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.should == 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.should == 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.should == 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.should == 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.should == 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.should == 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.should == 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.should == 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.should == []
- User.username_like_all("n", "o").all.should == User.find_all_by_username(["bjohnson", "dgainor"])
- end
-
- it "should have like any" do
- %w(bjohnson thunt dgainor).each { |username| User.create(:username => username) }
- User.username_like_any("bjohnson", "thunt").all.should == 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.should == []
- 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.should == 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.should == []
- 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.should == 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
- end
-
- it "should have eq" do
- User.age_eq(5).proxy_options.should == User.age_equals(5).proxy_options
- end
-
- it "should have not_equal_to" do
- User.age_not_equal_to(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
- end
-
- it "should have is_not" do
- User.age_is_not(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
- end
-
- it "should have not" do
- User.age_not(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
- end
-
- it "should have ne" do
- User.age_ne(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
- end
-
- it "should have lt" do
- User.age_lt(5).proxy_options.should == User.age_less_than(5).proxy_options
- end
-
- it "should have lte" do
- User.age_lte(5).proxy_options.should == User.age_less_than_or_equal_to(5).proxy_options
- end
-
- it "should have gt" do
- User.age_gt(5).proxy_options.should == User.age_greater_than(5).proxy_options
- end
-
- it "should have gte" do
- User.age_gte(5).proxy_options.should == User.age_greater_than_or_equal_to(5).proxy_options
- end
-
- it "should have contains" do
- User.username_contains(5).proxy_options.should == User.username_like(5).proxy_options
- end
-
- it "should have contains" do
- User.username_includes(5).proxy_options.should == User.username_like(5).proxy_options
- end
-
- it "should have bw" do
- User.username_bw(5).proxy_options.should == User.username_begins_with(5).proxy_options
- end
-
- it "should have ew" do
- User.username_ew(5).proxy_options.should == User.username_ends_with(5).proxy_options
- end
-
- it "should have nil" do
- User.username_nil.proxy_options.should == User.username_nil.proxy_options
- end
- end
-
- context "group conditions" do
- it "should have in" do
- (5..7).each { |age| User.create(:age => age) }
- User.age_in([5,6]).all.should == User.find(:all, :conditions => ["users.age IN (?)", [5, 6]])
- end
-
- it "should have not_in" do
- (5..7).each { |age| User.create(:age => age) }
- User.age_not_in([5,6]).all.should == User.find(:all, :conditions => ["users.age NOT IN (?)", [5, 6]])
- end
- end
-
- context "searchlogic lambda" do
- it "should be a string" do
- User.username_like("test")
- User.named_scope_options(:username_like).searchlogic_options[:type].should == :string
- end
-
- it "should be an integer" do
- User.id_gt(10)
- User.named_scope_options(:id_gt).searchlogic_options[:type].should == :integer
- end
-
- it "should be a float" do
- Order.total_gt(10)
- Order.named_scope_options(:total_gt).searchlogic_options[:type].should == :float
- end
- end
-
- it "should have priorty to columns over conflicting association conditions" do
- Company.users_count_gt(10)
- User.create
- User.company_id_null.count.should == 1
- User.company_id_not_null.count.should == 0
- end
-
- it "should fix bug for issue 26" do
- count1 = User.id_ne(10).username_not_like("root").count
- count2 = User.id_ne(10).username_not_like("root").count
- count1.should == count2
- end
-end
Please sign in to comment.
Something went wrong with that request. Please try again.