Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

* Refactored association code to be much simpler and rely on recursio…

…n. This allows the underlying class to do most of the work. This also allows calling any named scopes through any level of associations.
  • Loading branch information...
commit 74d56319c9aa38163f3a70244b5c002c97d98299 1 parent 8a21abe
@binarylogic authored
View
4 CHANGELOG.rdoc
@@ -1,3 +1,7 @@
+== 2.2.0 released 2009-07-30
+
+* Refactored association code to be much simpler and rely on recursion. This allows the underlying class to do most of the work. This also allows calling any named scopes through any level of associations.
+
== 2.1.13 released 2009-07-29
* Applied bug fix from http://github.com/skanev/searchlogic to make #order work with association ordering.
View
8 README.rdoc
@@ -83,7 +83,7 @@ You also get named scopes for any of your associations:
User.ascend_by_order_total
User.descend_by_orders_line_items_price
-Again these are just named scopes. You can chain them together, call methods off of them, etc. What's great about these named scopes is that they do NOT use the :include option, making them <em>much</em> faster. Instead they create a INNER JOIN and pass it to the :joins option, which is great for performance. To prove my point here is a quick benchmark from an application I am working on:
+Again these are just named scopes. You can chain them together, call methods off of them, etc. What's great about these named scopes is that they do NOT use the :include option, making them <em>much</em> faster. Instead they leverage the :joins option, which is great for performance. To prove my point here is a quick benchmark from an application I am working on:
Benchmark.bm do |x|
x.report { 10.times { Event.tickets_id_gt(10).all(:include => :tickets) } }
@@ -97,7 +97,7 @@ If you want to use the :include option, just specify it:
User.orders_line_items_price_greater_than(20).all(:include => {:orders => :line_items})
-Obviously, only do this if you want to actually use the included objects.
+Obviously, only do this if you want to actually use the included objects. Including objects into a query can be helpful with performance, especially when solving an N+1 query problem.
== Make searching and ordering data in your application trivial
@@ -176,7 +176,7 @@ Now just throw it in your form:
= f.check_box :four_year_olds
= f.submit
-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.
+This really allows Searchlogic to extend beyond what it provides internally. If Searchlogic doesn't provide a named scope for that crazy edge case that you need, just create your own named scope and use it. The sky is the limit.
== Use any or all
@@ -199,7 +199,7 @@ If you don't like will_paginate, use another solution, or roll your own. Paginat
== Conflicts with other gems
-You will notice searchlogic wants to create a method called "search". So do other libraries like thinking sphinx, etc. So searchlogic has a no conflict resolution. If the "search" method is already taken the method will be called "searchlogic" instead. So instead of
+You will notice searchlogic wants to create a method called "search". So do other libraries like thinking-sphinx, etc. So searchlogic has a no conflict resolution. If the "search" method is already taken the method will be called "searchlogic" instead. So instead of
User.search
View
16 lib/searchlogic.rb
@@ -1,6 +1,7 @@
require "searchlogic/core_ext/proc"
require "searchlogic/core_ext/object"
-require "searchlogic/active_record_consistency"
+require "searchlogic/active_record/consistency"
+require "searchlogic/active_record/named_scopes"
require "searchlogic/named_scopes/conditions"
require "searchlogic/named_scopes/ordering"
require "searchlogic/named_scopes/association_conditions"
@@ -10,10 +11,21 @@
Proc.send(:include, Searchlogic::CoreExt::Proc)
Object.send(:include, Searchlogic::CoreExt::Object)
+
+module ActiveRecord # :nodoc: all
+ class Base
+ class << self
+ include Searchlogic::ActiveRecord::Consistency
+ end
+ end
+end
+
+ActiveRecord::Base.extend(Searchlogic::ActiveRecord::NamedScopes)
+
ActiveRecord::Base.extend(Searchlogic::NamedScopes::Conditions)
-ActiveRecord::Base.extend(Searchlogic::NamedScopes::Ordering)
ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationConditions)
ActiveRecord::Base.extend(Searchlogic::NamedScopes::AssociationOrdering)
+ActiveRecord::Base.extend(Searchlogic::NamedScopes::Ordering)
ActiveRecord::Base.extend(Searchlogic::NamedScopes::AliasScope)
ActiveRecord::Base.extend(Searchlogic::Search::Implementation)
View
22 lib/searchlogic/active_record/consistency.rb
@@ -0,0 +1,22 @@
+module Searchlogic
+ module ActiveRecord
+ # Active Record is pretty inconsistent with how their SQL is constructed. This
+ # method attempts to close the gap between the various inconsistencies.
+ module Consistency
+ def self.included(klass)
+ klass.class_eval do
+ alias_method_chain :merge_joins, :searchlogic
+ end
+ end
+
+ # In AR multiple joins are sometimes in a single join query, and other times they
+ # are not. The merge_joins method in AR should account for this, but it doesn't.
+ # This fixes that problem. This way there is one join per string, which allows
+ # the merge_joins method to delete duplicates.
+ def merge_joins_with_searchlogic(*args)
+ joins = merge_joins_without_searchlogic(*args)
+ joins.collect { |j| j.is_a?(String) ? j.split(" ") : j }.flatten.uniq
+ end
+ end
+ end
+end
View
51 lib/searchlogic/active_record/named_scopes.rb
@@ -0,0 +1,51 @@
+module Searchlogic
+ module ActiveRecord
+ # Adds methods that give extra information about a classes named scopes.
+ module NamedScopes
+ # Retrieves the options passed when creating the respective named scope. Ex:
+ #
+ # named_scope :whatever, :conditions => {:column => value}
+ #
+ # This method will return:
+ #
+ # :conditions => {:column => value}
+ #
+ # ActiveRecord hides this internally in a Proc, so we have to try and pull it out with this
+ # method.
+ def named_scope_options(name)
+ key = scopes.key?(name.to_sym) ? name.to_sym : primary_condition_name(name)
+
+ if key
+ eval("options", scopes[key].binding)
+ else
+ nil
+ end
+ end
+
+ # The arity for a named scope's proc is important, because we use the arity
+ # to determine if the condition should be ignored when calling the search method.
+ # If the condition is false and the arity is 0, then we skip it all together. Ex:
+ #
+ # User.named_scope :age_is_4, :conditions => {:age => 4}
+ # User.search(:age_is_4 => false) == User.all
+ # User.search(:age_is_4 => true) == User.all(:conditions => {:age => 4})
+ #
+ # We also use it when trying to "copy" the underlying named scope for association
+ # conditions. This way our aliased scope accepts the same number of parameters for
+ # the underlying scope.
+ def named_scope_arity(name)
+ options = named_scope_options(name)
+ options.respond_to?(:arity) ? options.arity : nil
+ end
+
+ # A convenience method for creating inner join sql to that your inner joins
+ # are consistent with how Active Record creates them. Basically a tool for
+ # you to use when writing your own named scopes. This way you know for sure
+ # that duplicate joins will be removed when chaining scopes together that
+ # use the same join.
+ def inner_joins(association_name)
+ ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, association_name, nil).join_associations.collect { |assoc| assoc.association_join }
+ end
+ end
+ end
+end
View
28 lib/searchlogic/active_record_consistency.rb
@@ -1,28 +0,0 @@
-module Searchlogic
- # Active Record is pretty inconsistent with how their SQL is constructed. This
- # method attempts to close the gap between the various inconsistencies.
- module ActiveRecordConsistency
- def self.included(klass)
- klass.class_eval do
- alias_method_chain :merge_joins, :searchlogic
- end
- end
-
- # In AR multiple joins are sometimes in a single join query, and other times they
- # are not. The merge_joins method in AR should account for this, but it doesn't.
- # This fixes that problem. This way there is one join per string, which allows
- # the merge_joins method to delete duplicates.
- def merge_joins_with_searchlogic(*args)
- joins = merge_joins_without_searchlogic(*args)
- joins.collect { |j| j.is_a?(String) ? j.split(" ") : j }.flatten.uniq
- end
- end
-end
-
-module ActiveRecord # :nodoc: all
- class Base
- class << self
- include Searchlogic::ActiveRecordConsistency
- end
- end
-end
View
1  lib/searchlogic/named_scopes/alias_scope.rb
@@ -48,6 +48,7 @@ def alias_scopes # :nodoc:
end
def alias_scope?(name) # :nodoc:
+ return false if name.blank?
alias_scopes.key?(name.to_sym)
end
View
86 lib/searchlogic/named_scopes/association_conditions.rb
@@ -3,7 +3,7 @@ module NamedScopes
# Handles dynamically creating named scopes for associations.
module AssociationConditions
def condition?(name) # :nodoc:
- super || association_condition?(name) || association_alias_condition?(name)
+ super || association_condition?(name)
end
def primary_condition_name(name) # :nodoc:
@@ -11,40 +11,19 @@ def primary_condition_name(name) # :nodoc:
result
elsif association_condition?(name)
name.to_sym
- elsif details = association_alias_condition_details(name)
- "#{details[:association]}_#{details[:column]}_#{primary_condition(details[:condition])}".to_sym
else
nil
end
end
- # Is the name of the method a valid name for an association condition?
- def association_condition?(name)
- !association_condition_details(name).nil?
- end
-
- # Is the named of the method a valid name for an association alias condition?
- # An alias being "gt" for "greater_than", etc.
- def association_alias_condition?(name)
- !association_alias_condition_details(name).nil?
- end
-
- # A convenience method for creating inner join sql to that your inner joins
- # are consistent with how Active Record creates them. Basically a tool for
- # you to use when writing your own named scopes. This way you know for sure
- # that duplicate joins will be removed when chaining scopes together that
- # use the same join.
- def inner_joins(association_name)
- ActiveRecord::Associations::ClassMethods::InnerJoinDependency.new(self, association_name, nil).join_associations.collect { |assoc| assoc.association_join }
- end
-
private
+ def association_condition?(name)
+ !association_condition_details(name).nil?
+ end
+
def method_missing(name, *args, &block)
if details = association_condition_details(name)
- create_association_condition(details[:association], details[:column], details[:condition], args)
- send(name, *args)
- elsif details = association_alias_condition_details(name)
- create_association_alias_condition(details[:association], details[:column], details[:condition], args)
+ create_association_condition(details[:association], details[:condition], args)
send(name, *args)
else
super
@@ -52,51 +31,24 @@ def method_missing(name, *args, &block)
end
def association_condition_details(name)
- assocs = non_polymorphic_associations
+ assocs = reflect_on_all_associations.reject { |assoc| assoc.options[:polymorphic] }
return nil if assocs.empty?
- regexes = [association_searchlogic_regex(assocs, Conditions::PRIMARY_CONDITIONS)]
- assocs.each do |assoc|
- scope_names = assoc.klass.scopes.keys + assoc.klass.alias_scopes.keys
- scope_names.uniq!
- scope_names.delete(:scoped)
- next if scope_names.empty?
- regexes << /^(#{assoc.name})_(#{scope_names.join("|")})$/
- end
- if !local_condition?(name) && regexes.any? { |regex| name.to_s =~ regex }
- {:association => $1, :column => $2, :condition => $3}
- end
- end
-
- def create_association_condition(association_name, column, condition, args)
- name_parts = [column, condition].compact
- condition_name = name_parts.join("_")
- named_scope("#{association_name}_#{condition_name}", association_condition_options(association_name, condition_name, args))
- end
-
- def association_alias_condition_details(name)
- assocs = non_polymorphic_associations
- return nil if assocs.empty?
-
- if !local_condition?(name) && name.to_s =~ association_searchlogic_regex(assocs, Conditions::ALIAS_CONDITIONS)
- {:association => $1, :column => $2, :condition => $3}
+ if name.to_s =~ /^(#{assocs.collect(&:name).join("|")})_(\w+)$/
+ association_name = $1
+ condition = $2
+ association = reflect_on_association(association_name.to_sym)
+ klass = association.klass
+ if klass.condition?(condition)
+ {:association => $1, :condition => $2}
+ else
+ nil
+ end
end
end
- def non_polymorphic_associations
- reflect_on_all_associations.reject { |assoc| assoc.options[:polymorphic] }
- end
-
- def association_searchlogic_regex(assocs, condition_names)
- /^(#{assocs.collect(&:name).join("|")})_(\w+)_(#{condition_names.join("|")})$/
- end
-
- def create_association_alias_condition(association, column, condition, args)
- primary_condition = primary_condition(condition)
- alias_name = "#{association}_#{column}_#{condition}"
- primary_name = "#{association}_#{column}_#{primary_condition}"
- send(primary_name, *args) # go back to method_missing and make sure we create the method
- (class << self; self; end).class_eval { alias_method alias_name, primary_name }
+ def create_association_condition(association, condition, args)
+ named_scope("#{association}_#{condition}", association_condition_options(association, condition, args))
end
def association_condition_options(association_name, association_condition, args)
View
28 lib/searchlogic/named_scopes/association_ordering.rb
@@ -2,14 +2,28 @@ module Searchlogic
module NamedScopes
# Handles dynamically creating named scopes for associations.
module AssociationOrdering
- def association_ordering_condition?(name)
- !association_ordering_condition_details(name).nil?
+ def condition?(name) # :nodoc:
+ super || association_ordering_condition?(name)
+ end
+
+ def primary_condition_name(name) # :nodoc
+ if result = super
+ result
+ elsif association_ordering_condition?(name)
+ name.to_sym
+ else
+ nil
+ end
end
private
+ def association_ordering_condition?(name)
+ !association_ordering_condition_details(name).nil?
+ end
+
def method_missing(name, *args, &block)
if details = association_ordering_condition_details(name)
- create_association_ordering_condition(details[:association], details[:order_as], details[:column], args)
+ create_association_ordering_condition(details[:association], details[:order_as], details[:condition], args)
send(name, *args)
else
super
@@ -18,13 +32,13 @@ def method_missing(name, *args, &block)
def association_ordering_condition_details(name)
associations = reflect_on_all_associations.collect { |assoc| assoc.name }
- if !local_condition?(name) && name.to_s =~ /^(ascend|descend)_by_(#{associations.join("|")})_(\w+)$/
- {:order_as => $1, :association => $2, :column => $3}
+ if name.to_s =~ /^(ascend|descend)_by_(#{associations.join("|")})_(\w+)$/
+ {:order_as => $1, :association => $2, :condition => $3}
end
end
- def create_association_ordering_condition(association_name, order_as, column, args)
- named_scope("#{order_as}_by_#{association_name}_#{column}", association_condition_options(association_name, "#{order_as}_by_#{column}", args))
+ def create_association_ordering_condition(association, order_as, condition, args)
+ named_scope("#{order_as}_by_#{association}_#{condition}", association_condition_options(association, "#{order_as}_by_#{condition}", args))
end
end
end
View
94 lib/searchlogic/named_scopes/conditions.rb
@@ -40,42 +40,6 @@ module Conditions
PRIMARY_CONDITIONS = CONDITIONS.keys
ALIAS_CONDITIONS = CONDITIONS.values.flatten
- # Retrieves the options passed when creating the respective named scope. Ex:
- #
- # named_scope :whatever, :conditions => {:column => value}
- #
- # This method will return:
- #
- # :conditions => {:column => value}
- #
- # ActiveRecord hides this internally in a Proc, so we have to try and pull it out with this
- # method.
- def named_scope_options(name)
- key = scopes.key?(name.to_sym) ? name.to_sym : primary_condition_name(name)
-
- if key
- eval("options", scopes[key].binding)
- else
- nil
- end
- end
-
- # The arity for a named scope's proc is important, because we use the arity
- # to determine if the condition should be ignored when calling the search method.
- # If the condition is false and the arity is 0, then we skip it all together. Ex:
- #
- # User.named_scope :age_is_4, :conditions => {:age => 4}
- # User.search(:age_is_4 => false) == User.all
- # User.search(:age_is_4 => true) == User.all(:conditions => {:age => 4})
- #
- # We also use it when trying to "copy" the underlying named scope for association
- # conditions. This way our aliased scope accepts the same number of parameters for
- # the underlying scope.
- def named_scope_arity(name)
- options = named_scope_options(name)
- options.respond_to?(:arity) ? options.arity : nil
- end
-
# Returns the primary condition for the given alias. Ex:
#
# primary_condition(:gt) => :greater_than
@@ -90,10 +54,12 @@ def primary_condition(alias_condition)
# primary_condition_name(:id_gt) => :id_greater_than
# primary_condition_name(:id_greater_than) => :id_greater_than
def primary_condition_name(name)
- if primary_condition?(name)
- name.to_sym
- elsif details = alias_condition_details(name)
- "#{details[:column]}_#{primary_condition(details[:condition])}".to_sym
+ 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
@@ -101,48 +67,38 @@ def primary_condition_name(name)
# Is the name of the method a valid condition that can be dynamically created?
def condition?(name)
- local_condition?(name)
- end
-
- # Is the condition for a local column, not an association
- def local_condition?(name)
- primary_condition?(name) || alias_condition?(name)
- end
-
- # Is the name of the method a valid condition that can be dynamically created,
- # AND is it a primary condition (not an alias). "greater_than" not "gt".
- def primary_condition?(name)
- !primary_condition_details(name).nil?
- end
-
- # Is the name of the method a valid condition that can be dynamically created,
- # AND is it an alias condition. "gt" not "greater_than".
- def alias_condition?(name)
- !alias_condition_details(name).nil?
+ return false if name.blank?
+ scope_names = scopes.keys.reject { |k| k == :scoped }
+ scope_names.include?(name.to_sym) || !condition_details(name).nil?
end
private
def method_missing(name, *args, &block)
- if details = primary_condition_details(name)
- create_primary_condition(details[:column], details[:condition])
- send(name, *args)
- elsif details = alias_condition_details(name)
- create_alias_condition(details[:column], details[:condition], args)
+ if details = condition_details(name)
+ create_condition(details[:column], details[:condition], args)
send(name, *args)
else
super
end
end
- def primary_condition_details(name)
- if name.to_s =~ /^(#{column_names.join("|")})_(#{PRIMARY_CONDITIONS.join("|")})$/
+ def condition_details(name)
+ if name.to_s =~ /^(#{column_names.join("|")})_(#{(PRIMARY_CONDITIONS + ALIAS_CONDITIONS).join("|")})$/
{:column => $1, :condition => $2}
end
end
+ def create_condition(column, condition, args)
+ if PRIMARY_CONDITIONS.include?(condition.to_sym)
+ create_primary_condition(column, condition)
+ elsif ALIAS_CONDITIONS.include?(condition.to_sym)
+ create_alias_condition(column, condition, args)
+ end
+ end
+
def create_primary_condition(column, condition)
column_type = columns_hash[column.to_s].type
- match_keyword = ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
+ match_keyword = ::ActiveRecord::Base.connection.adapter_name == "PostgreSQL" ? "ILIKE" : "LIKE"
scope_options = case condition.to_s
when /^equals/
@@ -218,12 +174,6 @@ def value_with_modifier(value, modifier)
end
end
- def alias_condition_details(name)
- if name.to_s =~ /^(#{column_names.join("|")})_(#{ALIAS_CONDITIONS.join("|")})$/
- {:column => $1, :condition => $2}
- end
- end
-
def create_alias_condition(column, condition, args)
primary_condition = primary_condition(condition)
alias_name = "#{column}_#{condition}"
View
34 lib/searchlogic/named_scopes/ordering.rb
@@ -2,59 +2,49 @@ module Searchlogic
module NamedScopes
# Handles dynamically creating named scopes for orderin by columns.
module Ordering
- def local_condition?(name) # :nodoc:
- super || order_condition?(name)
+ def condition?(name) # :nodoc:
+ super || ordering_condition?(name)
end
def primary_condition_name(name) # :nodoc
if result = super
result
- elsif order_condition?(name)
+ elsif ordering_condition?(name)
name.to_sym
else
nil
end
end
- def order_condition?(name) # :nodoc:
- !order_condition_details(name).nil?
- end
-
- def custom_order_condition?(name) # :nodoc:
- !custom_order_condition_details(name).nil?
- end
-
private
+ def ordering_condition?(name) # :nodoc:
+ !ordering_condition_details(name).nil?
+ end
+
def method_missing(name, *args, &block)
if name == :order
named_scope name, lambda { |scope_name|
- return {} if !order_condition?(scope_name) && !custom_order_condition?(scope_name) && !association_ordering_condition?(scope_name)
+ return {} if !condition?(scope_name)
send(scope_name).proxy_options
}
send(name, *args)
- elsif details = order_condition_details(name)
- create_order_conditions(details[:column])
+ elsif details = ordering_condition_details(name)
+ create_ordering_conditions(details[:column])
send(name, *args)
else
super
end
end
- def order_condition_details(name)
+ def ordering_condition_details(name)
if name.to_s =~ /^(ascend|descend)_by_(#{column_names.join("|")})$/
{:order_as => $1, :column => $2}
elsif name.to_s =~ /^order$/
{}
end
end
-
- def custom_order_condition_details(name)
- if name.to_s =~ /^(ascend|descend)_by_(.+)$/
- {:order_as => $1, :scope => name.to_sym}
- end
- end
- def create_order_conditions(column)
+ def create_ordering_conditions(column)
named_scope("ascend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} ASC"})
named_scope("descend_by_#{column}".to_sym, {:order => "#{table_name}.#{column} DESC"})
end
View
4 lib/searchlogic/search.rb
@@ -17,7 +17,7 @@ module Searchlogic
class Search
# Responsible for adding a "search" method into your models.
module Implementation
- # Additional method, gets aliases as "search" if that method
+ # Additional method, gets aliased as "search" if that method
# is available. A lot of other libraries like to use "search"
# as well, so if you have a conflict like this, you can use
# this method directly.
@@ -141,7 +141,7 @@ def type_cast(value, type)
else
# Let's leverage ActiveRecord's type casting, so that casting is consistent
# with the other models.
- column_for_type_cast = ActiveRecord::ConnectionAdapters::Column.new("", nil)
+ column_for_type_cast = ::ActiveRecord::ConnectionAdapters::Column.new("", nil)
column_for_type_cast.instance_variable_set(:@type, type)
value = column_for_type_cast.type_cast(value)
Time.zone && value.is_a?(Time) ? value.in_time_zone : value
View
4 spec/named_scopes/alias_scope_spec.rb
@@ -1,6 +1,10 @@
require File.expand_path(File.dirname(__FILE__) + "/../spec_helper")
describe "AliasScope" do
+ before(:each) do
+ User.alias_scope :username_has, lambda { |value| User.username_like(value) }
+ end
+
it "should allow alias scopes" do
User.create(:username => "bjohnson")
User.create(:username => "thunt")
View
8 spec/named_scopes/association_conditions_spec.rb
@@ -10,14 +10,22 @@
end
it "should allow the use of foreign pre-existing named scopes" do
+ User.named_scope :uname, lambda { |value| {:conditions => ["users.username = ?", value]} }
Company.users_uname("bjohnson").proxy_options.should == User.uname("bjohnson").proxy_options.merge(:joins => :users)
end
+ it "should allow the use of deep foreign pre-existing named scopes" do
+ Order.named_scope :big_id, :conditions => "orders.id > 100"
+ Company.users_orders_big_id.proxy_options.should == Order.big_id.proxy_options.merge(:joins => {:users => :orders})
+ end
+
it "should allow the use of foreign pre-existing alias scopes" do
+ User.alias_scope :username_has, lambda { |value| User.username_like(value) }
Company.users_username_has("bjohnson").proxy_options.should == User.username_has("bjohnson").proxy_options.merge(:joins => :users)
end
it "should not raise errors for scopes that don't return anything" do
+ User.alias_scope :blank_scope, lambda { |value| }
Company.users_blank_scope("bjohnson").proxy_options.should == {:joins => :users}
end
View
3  spec/named_scopes/conditions_spec.rb
@@ -203,9 +203,6 @@
end
it "should have is_not" do
- # This is matching "not" first. How do you give priority in a regex? Because it's matching the
- # 'not' condition and thinking the column is 'age_is'.
- pending
User.age_is_not(5).proxy_options.should == User.age_does_not_equal(5).proxy_options
end
View
7 spec/search_spec.rb
@@ -110,12 +110,14 @@
end
it "should allow setting pre-existing association conditions" do
+ User.named_scope :uname, lambda { |value| {:conditions => ["users.username = ?", value]} }
search = Company.search
search.users_uname = "bjohnson"
search.users_uname.should == "bjohnson"
end
it "should allow setting pre-existing association alias conditions" do
+ User.alias_scope :username_has, lambda { |value| User.username_like(value) }
search = Company.search
search.users_username_has = "bjohnson"
search.users_username_has.should == "bjohnson"
@@ -158,6 +160,11 @@
lambda { search.unknown = true }.should raise_error(Searchlogic::Search::UnknownConditionError)
end
+ it "should not allow setting conditions on sensitive methods" do
+ search = User.search
+ lambda { search.destroy = true }.should raise_error(Searchlogic::Search::UnknownConditionError)
+ end
+
it "should not use the ruby implementation of the id method" do
search = User.search
search.id.should be_nil
View
3  spec/spec_helper.rb
@@ -71,9 +71,6 @@ class Company < ActiveRecord::Base
class User < ActiveRecord::Base
belongs_to :company, :counter_cache => true
has_many :orders, :dependent => :destroy
- named_scope :uname, lambda { |value| {:conditions => ["users.username = ?", value]} }
- alias_scope :username_has, lambda { |value| username_like(value) }
- alias_scope :blank_scope, lambda { |value| }
end
class Order < ActiveRecord::Base
Please sign in to comment.
Something went wrong with that request. Please try again.