Skip to content
Browse files

Released v1.0.3 (see changelog)

  • Loading branch information...
1 parent 4fa5012 commit c06d174f9b5c93415c3fc6d2fd9a3d522d792819 @binarylogic committed
View
7 CHANGELOG.rdoc
@@ -1,7 +1,12 @@
-== 1.0.3 released 2008-09-14
+== 1.0.3 released 2008-09-18
* Updated inspect to show the current options for your search. Plays nicer in the console.
* Made sure protection state is persistent among relationship conditions.
+* Fixed bug with backwards compatibility of rails. concat requires a proc in older version.
+* Defaulted remote control types to use GET requests instead of POST.
+* Completely reengineered integration with ActiveRecord. Searchgasm is properly using scopes letting you do use serachgasm where scope are implemented. @current_users.orders.new_search, etc. If your search is scoped and you want a search object, that search object will represent a new search in the context of those scopes, meaning the scopes get merged into Searchgasm as options.
+* Dropped support for Searchgasm functionality when defining relationships: has_many :order, :conditions => {:total_gt => 100}, will not work anymore. It's a chicken and the egg thing. Searchgasm needs AR constants, some models get loaded before others, therefore the Order model may not have been loaded yet, causing an unknown constant error.
+* Clean up redundant code and moved it into the Searchgasm::Shared namespace.
== 1.0.2 released 2008-09-12
View
9 Manifest
@@ -1,6 +1,3 @@
-benchmarks/benchmark.rb
-benchmarks/benchmark_helper.rb
-benchmarks/profile.rb
CHANGELOG.rdoc
examples/README.rdoc
init.rb
@@ -42,7 +39,11 @@ lib/searchgasm/search/conditions.rb
lib/searchgasm/search/ordering.rb
lib/searchgasm/search/pagination.rb
lib/searchgasm/search/protection.rb
-lib/searchgasm/utilities.rb
+lib/searchgasm/search/scoping.rb
+lib/searchgasm/search.rb
+lib/searchgasm/shared/searching.rb
+lib/searchgasm/shared/utilities.rb
+lib/searchgasm/shared/virtual_classes.rb
lib/searchgasm/version.rb
lib/searchgasm.rb
Manifest
View
21 README.rdoc
@@ -219,14 +219,23 @@ For tree data structures you get a few nifty methods. Let's assume Users is a tr
User.all(:conditions => {:inclusive_descendant_of => User.roots.first.id})
-== Available anywhere (relationships & scopes)
+== Scope support
-Not only can you use searchgasm when searching, but you can use it when setting up relationships or scopes. Anywhere you specify conditions in ActiveRecord.
+Not only can you use searchgasm when searching, but you can use it when using scopes.
class User < ActiveRecord::Base
- has_many :expensive_pending_orders, :conditions => {:total_greater_than => 1_000_000, :state => :pending}, :per_page => 20
named_scope :sexy, :conditions => {:first_name => "Ben", email_ends_with => "binarylogic.com"}, :per_page => 20
end
+
+or
+
+ class User < ActiveRecord::Base
+ def self.find_sexy
+ with_scope(:find => {:conditions => {:first_name => "Ben", email_ends_with => "binarylogic.com"}, :per_page => 20}) do
+ all
+ end
+ end
+ end
== Always use protection...against SQL injections
@@ -328,7 +337,11 @@ Pretty nifty, huh? You can create any condition ultimately creating any SQL you
== Under the hood
-I'm a big fan of understanding what I'm using, so here's a quick explanation: The design behind this plugin is pretty simple. The search object "sanitizes" down into the options passed into ActiveRecord::Base.find(). It serves as a transparent filter between you and ActiveRecord::Base.find(). This filter provides "enhancements" that get translated into options that ActiveRecord::Base.find() can understand. It doesn't dig into the ActiveRecord internals, it only uses what is publicly available. It jumps in and helps out <em>only</em> when needed, otherwise it sits back and lets ActiveRecord do all of the work. Between that and the extensive tests, this is a solid and fast plugin.
+I'm a big fan of understanding what I'm using, so here's a quick explanation: The design behind this plugin is pretty simple and I had 1 main rule when developing this:
+
+ActiveRecord should never know about Searchgasm
+
+What that rule means is that any options you pass when searching get "sanitized" down into options ActiveRecord can understand. Searchgasm serves as a transparent filter between you and ActiveRecord. It doesn't dig into the ActiveRecord internals, it only uses what is publicly available. It jumps in and helps out <em>only</em> when needed, otherwise it sits back and stays completely out of the way. Between that and the extensive tests, this is a solid and fast plugin.
== Reporting problems / bugs
View
8 lib/searchgasm.rb
@@ -6,10 +6,14 @@
# Core Ext
require "searchgasm/core_ext/hash"
-# Utilties
+# Shared
+require "searchgasm/shared/utilities"
+require "searchgasm/shared/searching"
+require "searchgasm/shared/virtual_classes"
+
+# Base classes
require "searchgasm/version"
require "searchgasm/config"
-require "searchgasm/utilities"
# ActiveRecord
require "searchgasm/active_record/base"
View
50 lib/searchgasm/active_record/associations.rb
@@ -5,68 +5,56 @@ module ActiveRecord
# These methods hook into ActiveRecords association methods and add in searchgasm functionality.
module Associations
module AssociationCollection
- # This is an alias method chain. It hook into ActiveRecord's "find" method for associations and checks to see if Searchgasm should get involved.
+ # This needs to be implemented because AR doesn't leverage scopes with this method like it probably should
def find_with_searchgasm(*args)
options = args.extract_options!
args << sanitize_options_with_searchgasm(options)
find_without_searchgasm(*args)
end
-
+
# See build_conditions under Searchgasm::ActiveRecord::Base. This is the same thing but for associations.
def build_conditions(options = {}, &block)
- conditions = @reflection.klass.build_conditions(options, &block)
- conditions.scope = scope(:find)[:conditions]
- conditions
+ @reflection.klass.send(:with_scope, :find => construct_scope[:find]) { @reflection.klass.build_conditions(options, &block) }
end
-
+
# See build_conditions! under Searchgasm::ActiveRecord::Base. This is the same thing but for associations.
def build_conditions!(options = {}, &block)
- conditions = @reflection.klass.build_conditions!(options, &block)
- conditions.scope = scope(:find)[:conditions]
- conditions
+ @reflection.klass.send(:with_scope, :find => construct_scope[:find]) { @reflection.klass.build_conditions!(options, &block) }
end
-
+
# See build_search under Searchgasm::ActiveRecord::Base. This is the same thing but for associations.
def build_search(options = {}, &block)
- conditions = @reflection.klass.build_search(options, &block)
- conditions.scope = scope(:find)[:conditions]
- conditions
+ @reflection.klass.send(:with_scope, :find => construct_scope[:find]) { @reflection.klass.build_search(options, &block) }
end
-
+
# See build_conditions! under Searchgasm::ActiveRecord::Base. This is the same thing but for associations.
def build_search!(options = {}, &block)
- conditions = @reflection.klass.build_search!(options, &block)
- conditions.scope = scope(:find)[:conditions]
- conditions
+ @reflection.klass.send(:with_scope, :find => construct_scope[:find]) { @reflection.klass.build_search!(options, &block) }
end
end
-
- module HasManyAssociation
- # This is an alias method chain. It hook into ActiveRecord's "calculate" method for has many associations and checks to see if Searchgasm should get involved.
+
+ module Shared
def count_with_searchgasm(*args)
- column_name, options = @reflection.klass.send(:construct_count_options_from_args, *args)
- count_without_searchgasm(column_name, sanitize_options_with_searchgasm(options))
+ options = args.extract_options!
+ args << sanitize_options_with_searchgasm(options)
+ count_without_searchgasm(*args)
end
end
end
end
end
-ActiveRecord::Associations::AssociationCollection.send(:include, Searchgasm::ActiveRecord::Associations::AssociationCollection)
-
module ActiveRecord
module Associations
class AssociationCollection
+ include Searchgasm::ActiveRecord::Associations::AssociationCollection
+
alias_method_chain :find, :searchgasm
end
- end
-end
-
-ActiveRecord::Associations::HasManyAssociation.send(:include, Searchgasm::ActiveRecord::Associations::HasManyAssociation)
-
-module ActiveRecord
- module Associations
+
class HasManyAssociation
+ include Searchgasm::ActiveRecord::Associations::Shared
+
alias_method_chain :count, :searchgasm
end
end
View
49 lib/searchgasm/active_record/base.rb
@@ -18,21 +18,29 @@ def find_with_searchgasm(*args)
args << options
find_without_searchgasm(*args)
end
-
+
# This is an alias method chain. It hooks into ActiveRecord's scopes and checks to see if Searchgasm should get involved. Allowing you to use all of Searchgasms conditions and tools
# in scopes as well.
#
# === Examples
#
+ # Named scopes:
+ #
# named_scope :top_expensive, :conditions => {:total_gt => 1_000_000}, :per_page => 10
+ # named_scope :top_expensive_ordered, :conditions => {:total_gt => 1_000_000}, :per_page => 10, :order_by => {:user => :first_name}
+ #
+ # Good ole' regular scopes:
#
# with_scope(:find => {:conditions => {:total_gt => 1_000_000}, :per_page => 10}) do
# find(:all)
# end
- def scope_with_searchgasm(method, key = nil)
- scope = scope_without_searchgasm(method, key)
- return sanitize_options_with_searchgasm(scope) if key.nil? && method == :find && !scope.blank?
- scope
+ #
+ # with_scope(:find => {:conditions => {:total_gt => 1_000_000}, :per_page => 10}) do
+ # build_search
+ # end
+ def with_scope_with_searchgasm(method_scoping = {}, action = :merge, &block)
+ method_scoping[:find] = sanitize_options_with_searchgasm(method_scoping[:find]) if method_scoping[:find]
+ with_scope_without_searchgasm(method_scoping, action, &block)
end
# This is a special method that Searchgasm adds in. It returns a new conditions object on the model. So you can search by conditions *only*.
@@ -118,17 +126,40 @@ def accessible_conditions # :nodoc:
private
def sanitize_options_with_searchgasm(options = {})
return options unless Searchgasm::Search::Base.needed?(self, options)
- search = searchgasm_searcher(options)
+ search = Searchgasm::Search::Base.create_virtual_class(self).new(options) # call explicitly to avoid merging the scopes into the searcher
search.acting_as_filter = true
search.sanitize
end
def searchgasm_conditions(options = {})
- Searchgasm::Conditions::Base.create_virtual_class(self).new(options)
+ conditions = Searchgasm::Conditions::Base.create_virtual_class(self).new(options)
+ conditions.conditions = (scope(:find) || {})[:conditions]
+ conditions
end
def searchgasm_searcher(options = {})
- Searchgasm::Search::Base.create_virtual_class(self).new(options)
+ search = Searchgasm::Search::Base.create_virtual_class(self).new(options)
+ options_from_scope_for_searchgasm(options).each { |option, value| search.send("#{option}=", value) }
+ search
+ end
+
+ def options_from_scope_for_searchgasm(options)
+ # The goal here is to mimic how scope work. Merge what scopes would and don't what they wouldn't.
+ scope = scope(:find) || {}
+ scope_options = {}
+ [:group, :include, :select, :readonly].each { |option| scope_options[option] = scope[option] if !options.has_key?(option) && scope.has_key?(option) }
+
+ if scope[:joins] || options[:joins]
+ scope_options[:joins] = []
+ scope_options[:joins] += scope[:joins].is_a?(Array) ? scope[:joins] : [scope[:joins]] unless scope[:joins].blank?
+ scope_options[:joins] += options[:joins].is_a?(Array) ? options[:joins] : [options[:joins]] unless options[:joins].blank?
+ end
+
+ scope_options[:limit] = scope[:limit] if !options.has_key?(:per_page) && !options.has_key?(:limit) && scope.has_key?(:per_page)
+ scope_options[:offset] = scope[:offset] if !options.has_key?(:page) && !options.has_key?(:offset) && scope.has_key?(:offset)
+ scope_options[:order] = scope[:order] if !options.has_key?(:order_by) && !options.has_key?(:order) && scope.has_key?(:order)
+ scope_options[:conditions] = scope[:conditions] if scope.has_key?(:conditions)
+ scope_options
end
end
end
@@ -141,7 +172,7 @@ class Base
class << self
alias_method_chain :calculate, :searchgasm
alias_method_chain :find, :searchgasm
- alias_method_chain :scope, :searchgasm
+ alias_method_chain :with_scope, :searchgasm
alias_method :new_conditions, :build_conditions
alias_method :new_conditions!, :build_conditions!
alias_method :new_search, :build_search
View
2 lib/searchgasm/condition/base.rb
@@ -5,7 +5,7 @@ module Condition # :nodoc:
# The base class for creating a condition. Your custom conditions should extend this class.
# See Searchgasm::Conditions::Base.register_condition on how to write your own condition.
class Base
- include Utilities
+ include Shared::Utilities
attr_accessor :column, :klass
attr_reader :value
View
2 lib/searchgasm/condition/inclusive_descendant_of.rb
@@ -1,8 +1,6 @@
module Searchgasm
module Condition
class InclusiveDescendantOf < Tree
- include Searchgasm::Utilities
-
def to_conditions(value)
condition = DescendantOf.new(klass, column)
condition.value = value
View
2 lib/searchgasm/condition/sibling_of.rb
@@ -1,8 +1,6 @@
module Searchgasm
module Condition
class SiblingOf < Tree
- include Searchgasm::Utilities
-
def to_conditions(value)
parent_association = klass.reflect_on_association(:parent)
foreign_key_name = (parent_association && parent_association.options[:foreign_key]) || "parent_id"
View
46 lib/searchgasm/conditions/base.rb
@@ -5,9 +5,11 @@ module Conditions # :nodoc:
# Represents a collection of conditions and performs various tasks on that collection. For information on each condition see Searchgasm::Condition.
# Each condition has its own file and class and the source for each condition is pretty self explanatory.
class Base
- include Utilities
+ include Searchgasm::Shared::Utilities
+ include Searchgasm::Shared::Searching
+ include Searchgasm::Shared::VirtualClasses
- attr_accessor :relationship_name, :scope
+ attr_accessor :relationship_name, :sql
class << self
attr_accessor :added_klass_conditions, :added_column_conditions, :added_associations
@@ -64,37 +66,14 @@ def condition_names
def needed?(model_class, conditions) # :nodoc:
if conditions.is_a?(Hash)
+ column_names = model_class.column_names
conditions.stringify_keys.keys.each do |condition|
- return true unless model_class.column_names.include?(condition)
+ return true unless column_names.include?(condition)
end
end
false
end
-
- # Creates virtual classes for the class passed to it. This is a neccesity for keeping dynamically created method
- # names specific to models. It provides caching and helps a lot with performance.
- def create_virtual_class(model_class)
- class_search_name = "::Searchgasm::Cache::#{model_class.name}Conditions"
-
- begin
- eval(class_search_name)
- rescue NameError
- eval <<-end_eval
- class #{class_search_name} < ::Searchgasm::Conditions::Base
- def self.klass
- #{model_class.name}
- end
-
- def klass
- #{model_class.name}
- end
- end
-
- #{class_search_name}
- end_eval
- end
- end
end
def initialize(init_conditions = {})
@@ -127,26 +106,27 @@ def includes
def inspect
conditions_hash = conditions
+ conditions_hash[:sql] = sql if sql
conditions_hash[:protected] = true if protected?
conditions_hash.inspect
end
# Sanitizes the conditions down into conditions that ActiveRecord::Base.find can understand.
- def sanitize
+ def sanitize(for_method = nil)
conditions = merge_conditions(*objects.collect { |object| object.sanitize })
- return scope if conditions.blank?
- merge_conditions(conditions, scope)
+ return sql if conditions.blank?
+ merged_conditions = merge_conditions(conditions, sql)
+ for_method.blank? ? merged_conditions : {:conditions => merged_conditions}
end
- # Allows you to set the conditions via a hash. If you do not pass a hash it will set scope instead, so that you can continue to add conditions and ultimately
- # merge it all together at the end.
+ # Allows you to set the conditions via a hash.
def conditions=(conditions)
case conditions
when Hash
assert_valid_conditions(conditions)
remove_conditions_from_protected_assignement(conditions).each { |condition, value| send("#{condition}=", value) }
else
- self.scope = conditions
+ self.sql = conditions
end
end
View
3 lib/searchgasm/conditions/protection.rb
@@ -15,7 +15,7 @@ def conditions_with_protection=(conditions)
unless conditions.is_a?(Hash)
if protect?
return if conditions.blank?
- raise(ArgumentError, "You can not set a scope or pass SQL while the search is being protected")
+ raise(ArgumentError, "You can not pass SQL as conditions while the search is being protected, you can only pass a hash")
end
end
@@ -30,6 +30,7 @@ def protect=(value)
def protect?
protect == true
end
+ alias_method :protected?, :protect?
end
end
end
View
1 lib/searchgasm/helpers/control_types/remote_link.rb
@@ -76,6 +76,7 @@ def remote_page_link(page, options = {})
private
def add_remote_defaults!(options)
options[:remote] ||= {}
+ options[:remote][:method] ||= :get
options[:is_remote] = true
end
end
View
7 lib/searchgasm/search.rb
@@ -0,0 +1,7 @@
+module Searchgasm
+ # = Searchgasm Shared Functionality
+ #
+ # Encapsulates shared functions / code that is used throughout searchgasm, so that we dont repeat ourself
+ module Shared
+ end
+end
View
63 lib/searchgasm/search/base.rb
@@ -5,16 +5,15 @@ module Search #:nodoc:
# Please refer the README.rdoc for usage, examples, and installation.
class Base
- include Searchgasm::Utilities
+ include Searchgasm::Shared::Utilities
+ include Searchgasm::Shared::Searching
+ include Searchgasm::Shared::VirtualClasses
# Options that ActiveRecord doesn't suppport, but Searchgasm does
SPECIAL_FIND_OPTIONS = [:order_by, :order_as, :page, :per_page]
# Valid options you can use when searching
- VALID_FIND_OPTIONS = ::ActiveRecord::Base.valid_find_options + SPECIAL_FIND_OPTIONS
-
- # Use these methods just like you would in ActiveRecord
- SEARCH_METHODS = [:all, :average, :calculate, :count, :find, :first, :maximum, :minimum, :sum]
+ VALID_FIND_OPTIONS = SPECIAL_FIND_OPTIONS + ::ActiveRecord::Base.valid_find_options # the order is very important, these options get set in this order
attr_accessor *::ActiveRecord::Base.valid_find_options
@@ -25,51 +24,15 @@ def needed?(model_class, options)
SPECIAL_FIND_OPTIONS.each do |option|
return true if options.symbolize_keys.keys.include?(option)
end
-
+
Searchgasm::Conditions::Base.needed?(model_class, options[:conditions])
end
-
- # Creates virtual classes for the class passed to it. This is a neccesity for keeping dynamically created method
- # names specific to models. It provides caching and helps a lot with performance.
- def create_virtual_class(model_class)
- class_search_name = "::Searchgasm::Cache::#{model_class.name}Search"
-
- begin
- eval(class_search_name)
- rescue NameError
- # The method definitions are for performance, bottlenecks found with ruby-prof
- eval <<-end_eval
- class #{class_search_name} < ::Searchgasm::Search::Base
- def self.klass
- #{model_class.name}
- end
-
- def klass
- #{model_class.name}
- end
- end
-
- #{class_search_name}
- end_eval
- end
- end
end
def initialize(init_options = {})
self.options = init_options
end
- # Setup methods for searching
- SEARCH_METHODS.each do |method|
- class_eval <<-"end_eval", __FILE__, __LINE__
- def #{method}(*args)
- self.options = args.extract_options!
- args << sanitize(:#{method})
- klass.#{method}(*args)
- end
- end_eval
- end
-
# Flag to determine if searchgasm is acting as a filter for the ActiveRecord search methods.
# The purpose of this is to determine if Config.per_page should be implemented.
def acting_as_filter=(value)
@@ -89,6 +52,7 @@ def inspect
next if value.nil?
current_find_options[option] = value
end
+ current_find_options[:scope] = scope unless scope.blank?
"#<#{klass}Search #{current_find_options.inspect}>"
end
@@ -103,13 +67,20 @@ def limit
end
def offset=(value)
- @offset = value.to_i
+ @offset = value.blank? ? nil : value.to_i
end
def options=(values)
return unless values.is_a?(Hash)
values.symbolize_keys.fast_assert_valid_keys(VALID_FIND_OPTIONS)
- values.each { |option, value| send("#{option}=", value) }
+
+ # Do the special options first, and then the core options last, since the core options take precendence
+ VALID_FIND_OPTIONS.each do |option|
+ next unless values.has_key?(option)
+ send("#{option}=", values[option])
+ end
+
+ values
end
# Sanitizes everything down into options ActiveRecord::Base.find can understand
@@ -122,6 +93,10 @@ def sanitize(for_method = nil)
end
find_options
end
+
+ def scope
+ @scope ||= {}
+ end
end
end
end
View
5 lib/searchgasm/search/conditions.rb
@@ -53,7 +53,10 @@ def include_with_conditions
def sanitize_with_conditions(for_method = nil) # :nodoc:
find_options = sanitize_without_conditions(for_method)
- find_options[:conditions] = find_options[:conditions].sanitize if find_options[:conditions]
+ if conditions_obj = find_options.delete(:conditions)
+ new_conditions = conditions_obj.sanitize
+ find_options[:conditions] = new_conditions unless new_conditions.blank?
+ end
find_options
end
View
3 lib/searchgasm/search/pagination.rb
@@ -35,7 +35,8 @@ def page
# Lets you change the page for the next search
def page=(value)
# Have to use @offset, since self.offset= resets @page
- if value.nil?
+ if value.blank?
+ value = nil
@page = value
return @offset = value
end
View
0 lib/searchgasm/search/scoping.rb
No changes.
View
25 lib/searchgasm/shared/searching.rb
@@ -0,0 +1,25 @@
+module Searchgasm
+ module Shared
+ # = Searchgasm Searching
+ #
+ # Implements searching functionality for searchgasm. Searchgasm::Search::Base and Searchgasm::Conditions::Base can both search and include
+ # this module.
+ module Searching
+ # Use these methods just like you would in ActiveRecord
+ SEARCH_METHODS = [:all, :average, :calculate, :count, :find, :first, :maximum, :minimum, :sum]
+
+ # Setup methods for searching
+ SEARCH_METHODS.each do |method|
+ class_eval <<-"end_eval", __FILE__, __LINE__
+ def #{method}(*args)
+ options = args.extract_options!
+ klass.send(:with_scope, :find => options) do
+ args << sanitize(:#{method})
+ klass.#{method}(*args)
+ end
+ end
+ end_eval
+ end
+ end
+ end
+end
View
32 lib/searchgasm/shared/utilities.rb
@@ -0,0 +1,32 @@
+module Searchgasm
+ module Shared
+ module Utilities # :nodoc:
+ private
+ def merge_conditions(*conditions)
+ options = conditions.extract_options!
+ conditions.delete_if { |condition| condition.blank? }
+ return if conditions.blank?
+ return conditions.first if conditions.size == 1
+
+ conditions_strs = []
+ conditions_subs = []
+
+ conditions.each do |condition|
+ next if condition.blank?
+ arr_condition = [condition].flatten
+ conditions_strs << arr_condition.first
+ conditions_subs += arr_condition[1..-1]
+ end
+
+ return if conditions_strs.blank?
+
+ join = options[:any] ? "OR" : "AND"
+ conditions_str = "(#{conditions_strs.join(") #{join} (")})"
+
+ return conditions_str if conditions_subs.blank?
+
+ [conditions_str, *conditions_subs]
+ end
+ end
+ end
+end
View
39 lib/searchgasm/shared/virtual_classes.rb
@@ -0,0 +1,39 @@
+module Searchgasm
+ module Shared
+ # = Searchgasm Virtual Classes
+ #
+ # Creates virtual classes for each model, to implementing a type of caching. So that object instantiation for searchgasm searches is cached. This is lazy, meaning
+ # it will only cache when it needs. So the first instantion will be much slow than the following ones. This is cached in the RAM, so if the process is restarted the caching is cleared.
+ module VirtualClasses
+ def self.included(klass)
+ klass.extend ClassMethods
+ end
+
+ module ClassMethods
+ # Creates virtual classes for the class passed to it. This is a neccesity for keeping dynamically created method
+ # names specific to models. It provides caching and helps a lot with performance.
+ def create_virtual_class(model_class)
+ class_search_name = "::Searchgasm::Cache::#{model_class.name}" + name.split(/::/)[1]
+
+ begin
+ eval(class_search_name)
+ rescue NameError
+ eval <<-end_eval
+ class #{class_search_name} < ::#{name}
+ def self.klass
+ #{model_class.name}
+ end
+
+ def klass
+ #{model_class.name}
+ end
+ end
+
+ #{class_search_name}
+ end_eval
+ end
+ end
+ end
+ end
+ end
+end
View
30 lib/searchgasm/utilities.rb
@@ -1,30 +0,0 @@
-module Searchgasm
- module Utilities # :nodoc:
- private
- def merge_conditions(*conditions)
- options = conditions.extract_options!
- conditions.delete_if { |condition| condition.blank? }
- return if conditions.blank?
- return conditions.first if conditions.size == 1
-
- conditions_strs = []
- conditions_subs = []
-
- conditions.each do |condition|
- next if condition.blank?
- arr_condition = [condition].flatten
- conditions_strs << arr_condition.first
- conditions_subs += arr_condition[1..-1]
- end
-
- return if conditions_strs.blank?
-
- join = options[:any] ? "OR" : "AND"
- conditions_str = "(#{conditions_strs.join(") #{join} (")})"
-
- return conditions_str if conditions_subs.blank?
-
- [conditions_str, *conditions_subs]
- end
- end
-end
View
2 lib/searchgasm/version.rb
@@ -67,7 +67,7 @@ def to_a
MAJOR = 1
MINOR = 0
- TINY = 2
+ TINY = 3
# The current version as a Version instance
CURRENT = new(MAJOR, MINOR, TINY)
View
19 test/test_active_record_associations.rb
@@ -16,7 +16,7 @@ def test_build_search
search = Account.find(1).users.build_search
assert_kind_of Searchgasm::Search::Base, search
assert_equal User, search.klass
- assert_equal "\"users\".account_id = 1", search.conditions.scope
+ assert_equal "\"users\".account_id = 1", search.conditions.sql
search.conditions.first_name_contains = "Ben"
assert_equal({:conditions => ["(\"users\".\"first_name\" LIKE ?) AND (\"users\".account_id = 1)", "%Ben%"]}, search.sanitize)
@@ -35,4 +35,21 @@ def test_calculations
assert_equal 1, Account.find(1).users.sum("id", :conditions => {:first_name_begins_with => "Ben"})
assert_equal 1, Account.find(1).users.average("id", :conditions => {:first_name_begins_with => "Ben"})
end
+
+ def test_has_many_through
+ assert_equal 1, Account.find(1).orders.count
+ assert_equal 1, Account.find(1).orders.all(:conditions => {:total_gt => 100}).size
+ assert_equal 0, Account.find(1).orders.all(:conditions => {:total_gt => 1000}).size
+ assert_equal 1, Account.find(1).orders.sum("id", :conditions => {:total_gt => 100})
+ assert_equal 0, Account.find(1).orders.sum("id", :conditions => {:total_gt => 1000})
+ assert_equal 1, Account.find(1).orders.average("id", :conditions => {:total_gt => 100})
+ end
+
+ def test_habtm
+
+ end
+
+ def test_special_options
+ #order, see AR doc, etc
+ end
end
View
30 test/test_conditions_base.rb
@@ -42,10 +42,18 @@ def test_setting_conditions
end
def test_accessible_protected_conditions
- #Account.conditions_accessible << :name_contains
- #conditions = Account.new_conditions
- #conditions.conditions = {:created_after => Time.now, :name_contains => "Binary"}
- #assert({:name_contains => "Binary"}, conditions.value)
+ Account.conditions_accessible << :name_contains
+ conditions = Account.new_conditions
+ conditions.conditions = {:created_after => Time.now, :name_contains => "Binary"}
+ assert({:name_contains => "Binary"}, conditions.conditions)
+ Account.send(:write_inheritable_attribute, :conditions_accessible, nil)
+
+ Account.conditions_protected << :name_contains
+ conditions = Account.new_conditions
+ now = Time.now
+ conditions.conditions = {:created_after => now, :name_contains => "Binary"}
+ assert({:created_after => now}, conditions.conditions)
+ Account.send(:write_inheritable_attribute, :conditions_protected, nil)
end
def test_assert_valid_values
@@ -136,18 +144,18 @@ def test_conditions
conditions.conditions = v
assert_equal v, conditions.conditions
- scope = "id in (1,2,3,4)"
- conditions.conditions = scope
- assert_equal v, conditions.conditions, v
- assert_equal scope, conditions.scope
+ sql = "id in (1,2,3,4)"
+ conditions.conditions = sql
+ assert_equal v, conditions.conditions
+ assert_equal sql, conditions.sql
v2 = {:id_less_than => 5, :name_begins_with => "Beginning of string"}
conditions.conditions = v2
assert_equal v.merge(v2), conditions.conditions
- scope2 = "id > 5 and name = 'Test'"
- conditions.conditions = scope2
- assert_equal scope2, conditions.scope
+ sql2 = "id > 5 and name = 'Test'"
+ conditions.conditions = sql2
+ assert_equal sql2, conditions.sql
end
def test_searching
View
16 test/test_search_base.rb
@@ -60,7 +60,7 @@ def test_setting_first_level_options
search.limit = 50
assert_equal 2, search.page
search.offset = nil
- assert_equal 0, search.offset
+ assert_equal nil, search.offset
assert_equal 1, search.page
search.per_page = 2
@@ -137,14 +137,14 @@ def test_sanitize
def test_scope
search = Account.new_search!
- search.conditions = "some scope"
- assert_equal "some scope", search.conditions.scope
+ search.conditions = "some sql"
+ assert_equal "some sql", search.conditions.sql
search.conditions = nil
- assert_equal nil, search.conditions.scope
- search.conditions = "some scope"
- assert_equal "some scope", search.conditions.scope
- search.conditions = "some scope2"
- assert_equal "some scope2", search.conditions.scope
+ assert_equal nil, search.conditions.sql
+ search.conditions = "some sql"
+ assert_equal "some sql", search.conditions.sql
+ search.conditions = "some sql"
+ assert_equal "some sql", search.conditions.sql
end
def test_searching

0 comments on commit c06d174

Please sign in to comment.
Something went wrong with that request. Please try again.