diff --git a/CHANGELOG.rdoc b/CHANGELOG.rdoc index 810d4aea..bbadb990 100644 --- a/CHANGELOG.rdoc +++ b/CHANGELOG.rdoc @@ -1,9 +1,11 @@ -== 1.1.4 released 2008-09-24 +== 1.2.0 released 2008-09-24 * Added searchgasm_params and searchgasm_url helper to use outside of the control type helpers. * Added dup and clone methods that work properly for searchgasm objects * Fixed bug to remove nil scope values, HABTM likes to add :limit => nil * Removed unnecessary build_search methods for associations +* Removed support for searching with conditions only. This just made things much more complicated when you can accomplish the same thing by starting a new search and only setting conditions. +* Fixed bug when searching with *any* conditions to use left outer joins instead of inner joins. == 1.1.3 released 2008-09-23 diff --git a/README.rdoc b/README.rdoc index 365d1d3e..b07c8d52 100644 --- a/README.rdoc +++ b/README.rdoc @@ -177,19 +177,6 @@ Any of the options used in the above example can be used in these, but for the s search.conditions.first_name_contains = "Ben" search.per_page = 20 search.all - -== Search with conditions only - -Don't need pagination, ordering, or any of the other options? Search with conditions only. - - conditions = User.new_conditions(:age_gt => 18) - conditions.first_name_contains = "Ben" - conditions.all - # ... all operations above are available - -Pass a conditions object right into ActiveRecord: - - User.all(:conditions => conditions) == Match ANY or ALL of the conditions @@ -250,12 +237,11 @@ or == Always use protection...against SQL injections -If there is one thing we all know, it's to always use protection against SQL injections. That's why searchgasm protects you by default. The new\_search and new\_conditions methods protect mass assignments by default (instantiation and search.options = {}). This means that various checks are done to ensure it is not possible to perform any type of SQL injection during mass assignments. But this also limits how you can search, meaning you can't write raw SQL. If you want to be daring and search without protection, all that you have to do is add ! to the end of the methods: new\_search! and new\_conditions!. +If there is one thing we all know, it's to always use protection against SQL injections. That's why searchgasm protects you by default. The new\_search methods protect mass assignments by default (instantiation and search.options = {}). This means that various checks are done to ensure it is not possible to perform any type of SQL injection during mass assignments. But this also limits how you can search, meaning you can't write raw SQL. If you want to be daring and search without protection, all that you have to do is add ! to the end of the method: new\_search!. === Protected from SQL injections search = Account.new_search(params[:search]) - conditions = Account.new_conditions(params[:conditions]) === *NOT* protected from SQL injections @@ -263,11 +249,10 @@ If there is one thing we all know, it's to always use protection against SQL inj accounts = Account.all(params[:search]) account = Account.first(params[:search]) search = Account.new_search!(params[:search]) - conditions = Account.new_conditions!(params[:conditions]) I'm sure you already knew this, but it's tempting to do this when you can pass the params hash right into these methods. -Lesson learned: use new\_search and new\_conditions when passing in params as *ANY* of the options. +Lesson learned: use new\_search when passing in params as *ANY* of the options. == Available Conditions diff --git a/lib/searchgasm.rb b/lib/searchgasm.rb index ad25ce18..80d52718 100644 --- a/lib/searchgasm.rb +++ b/lib/searchgasm.rb @@ -8,7 +8,6 @@ # Shared require "searchgasm/shared/utilities" -require "searchgasm/shared/searching" require "searchgasm/shared/virtual_classes" # Base classes @@ -23,6 +22,7 @@ require "searchgasm/search/ordering" require "searchgasm/search/pagination" require "searchgasm/search/conditions" +require "searchgasm/search/searching" require "searchgasm/search/base" require "searchgasm/search/protection" @@ -68,6 +68,7 @@ class Base include Ordering include Protection include Pagination + include Searching end end diff --git a/lib/searchgasm/active_record/base.rb b/lib/searchgasm/active_record/base.rb index 0cc0494d..1d094718 100644 --- a/lib/searchgasm/active_record/base.rb +++ b/lib/searchgasm/active_record/base.rb @@ -11,6 +11,7 @@ module Base def calculate_with_searchgasm(*args) options = args.extract_options! options = filter_options_with_searchgasm(options, false) + args[1] = primary_key if options[:distinct] && [nil, :all].include?(args[1]) # quick fix for adding a column name if distinct is true and no specific column is provided args << options calculate_without_searchgasm(*args) end @@ -47,30 +48,6 @@ def with_scope_with_searchgasm(method_scoping = {}, action = :merge, &block) 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*. - # - # This method is "protected". Meaning it checks the passed options for SQL injections. So trying to write raw SQL in *any* of the option will result in a raised exception. It's safe to pass a params object when instantiating. - # - # === Examples - # - # conditions = User.new_conditions - # conditions.first_name_contains = "Ben" - # conditions.all # can call any search method: first, find(:all), find(:first), sum("id"), etc... - def build_conditions(values = {}, &block) - conditions = searchgasm_conditions - conditions.protect = true - conditions.conditions = values - yield conditions if block_given? - conditions - end - - # See build_conditions. This is the same method but *without* protection. Do *NOT* pass in a params object to this method. - def build_conditions!(values = {}, &block) - conditions = searchgasm_conditions(values) - yield conditions if block_given? - conditions - end - # This is a special method that Searchgasm adds in. It returns a new search object on the model. So you can search via an object. # # This method is "protected". Meaning it checks the passed options for SQL injections. So trying to write raw SQL in *any* of the option will result in a raised exception. It's safe to pass a params object when instantiating. @@ -86,7 +63,7 @@ def build_conditions!(values = {}, &block) # search.order_by = {:user_group => :name} # search.all # can call any search method: first, find(:all), find(:first), sum("id"), etc... def build_search(options = {}, &block) - search = searchgasm_searcher + search = searchgasm_search search.protect = true search.options = options yield search if block_given? @@ -97,7 +74,7 @@ def build_search(options = {}, &block) # # This also has an alias "new_search!" def build_search!(options = {}, &block) - search = searchgasm_searcher(options) + search = searchgasm_search(options) yield search if block_given? search end @@ -120,7 +97,7 @@ def protected_conditions # :nodoc: # This is the reverse of conditions_protected. You can specify conditions here and *only* these conditions will be allowed in mass assignment. Any condition not specified here will be blocked. def conditions_accessible(*conditions) - write_inheritable_attribute(:conditions_accessible, Set.new(conditions.map(&:to_s)) + (protected_conditions || [])) + write_inheritable_attribute(:conditions_accessible, Set.new(conditions.map(&:to_s)) + (accessible_conditions || [])) end def accessible_conditions # :nodoc: @@ -130,7 +107,7 @@ def accessible_conditions # :nodoc: private def filter_options_with_searchgasm(options = {}, searching = true) return options unless Searchgasm::Search::Base.needed?(self, options) - search = Searchgasm::Search::Base.create_virtual_class(self).new # call explicitly to avoid merging the scopes into the searcher + search = Searchgasm::Search::Base.create_virtual_class(self).new # call explicitly to avoid merging the scopes into the search search.acting_as_filter = true conditions = options.delete(:conditions) || options.delete("conditions") || {} if conditions @@ -145,15 +122,7 @@ def filter_options_with_searchgasm(options = {}, searching = true) search.sanitize(searching) end - def searchgasm_conditions(options = {}) - searcher = Searchgasm::Conditions::Base.create_virtual_class(self).new - conditions = scope(:find) && scope(:find)[:conditions] - searcher.scope = {:conditions => conditions} if conditions - searcher.conditions = options - searcher - end - - def searchgasm_searcher(options = {}) + def searchgasm_search(options = {}) scope = {} current_scope = scope(:find) && scope(:find).deep_dup if current_scope @@ -168,11 +137,11 @@ def searchgasm_searcher(options = {}) current_scope.each { |k, v| new_scope[k] = v unless v.nil? } current_scope = new_scope end - searcher = Searchgasm::Search::Base.create_virtual_class(self).new - searcher.scope = scope - searcher.options = current_scope - searcher.options = options - searcher + search = Searchgasm::Search::Base.create_virtual_class(self).new + search.scope = scope + search.options = current_scope + search.options = options + search end end end @@ -186,8 +155,6 @@ class << self alias_method_chain :calculate, :searchgasm alias_method_chain :find, :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 alias_method :new_search!, :build_search! diff --git a/lib/searchgasm/conditions/base.rb b/lib/searchgasm/conditions/base.rb index 7bc3ae4a..ac4ce51b 100644 --- a/lib/searchgasm/conditions/base.rb +++ b/lib/searchgasm/conditions/base.rb @@ -6,7 +6,6 @@ module Conditions # :nodoc: # Each condition has its own file and class and the source for each condition is pretty self explanatory. class Base include Shared::Utilities - include Shared::Searching include Shared::VirtualClasses attr_accessor :any, :relationship_name @@ -65,6 +64,8 @@ def condition_names end def needed?(model_class, conditions) # :nodoc: + return false if conditions.blank? + if conditions.is_a?(Hash) return true if conditions[:any] column_names = model_class.column_names @@ -105,11 +106,11 @@ def any? end # A list of joins to use when searching, includes relationships - def joins + def auto_joins j = [] associations.each do |association| next if association.conditions.blank? - association_joins = association.joins + association_joins = association.auto_joins j << (association_joins.blank? ? association.relationship_name.to_sym : {association.relationship_name.to_sym => association_joins}) end j.blank? ? nil : (j.size == 1 ? j.first : j) @@ -262,7 +263,7 @@ def objects end def reset_objects! - objects.each { |object| eval("@#{object.name} = nil") } + objects.each { |object| object.class < ::Searchgasm::Conditions::Base ? eval("@#{object.relationship_name} = nil") : eval("@#{object.name} = nil") } objects.clear end diff --git a/lib/searchgasm/search/base.rb b/lib/searchgasm/search/base.rb index a6c29642..a9b1317e 100644 --- a/lib/searchgasm/search/base.rb +++ b/lib/searchgasm/search/base.rb @@ -3,10 +3,8 @@ module Search #:nodoc: # = Searchgasm # # Please refer the README.rdoc for usage, examples, and installation. - class Base include Searchgasm::Shared::Utilities - include Searchgasm::Shared::Searching include Searchgasm::Shared::VirtualClasses # Options ActiveRecord allows when searching @@ -24,11 +22,14 @@ class Base OPTIONS = SPECIAL_FIND_OPTIONS + AR_OPTIONS # the order is very important, these options get set in this order attr_accessor *AR_OPTIONS + attr_reader :auto_joins class << self # Used in the ActiveRecord methods to determine if Searchgasm should get involved or not. # This keeps Searchgasm out of the way unless it is needed. def needed?(model_class, options) + return false if options.blank? + SPECIAL_FIND_OPTIONS.each do |option| return true if options.symbolize_keys.keys.include?(option) end @@ -78,13 +79,8 @@ def inspect "#<#{klass}Search #{current_find_options.inspect}>" end - def joins=(value) - @memoized_joins = nil - @joins = value - end - def joins - @memoized_joins ||= @joins + merge_joins(@joins, auto_joins) end def limit=(value) @@ -128,9 +124,7 @@ def sanitize(searching = true) if searching find_options[:group] ||= "#{quote_table_name(klass.table_name)}.#{quote_column_name(klass.primary_key)}" else - # If we are calculating use includes because they use joins that grab uniq records. When calculating, includes don't have the - # performance hit that they have when searching. Plus it's cleaner. - find_options[:include] = merge_joins(find_options[:include], find_options.delete(:joins)) + find_options[:distinct] = true end end diff --git a/lib/searchgasm/search/conditions.rb b/lib/searchgasm/search/conditions.rb index bb1f83e7..df621936 100644 --- a/lib/searchgasm/search/conditions.rb +++ b/lib/searchgasm/search/conditions.rb @@ -10,6 +10,7 @@ def self.included(klass) alias_method_chain :initialize, :conditions alias_method_chain :conditions=, :conditions alias_method_chain :conditions, :conditions + alias_method_chain :auto_joins, :conditions alias_method_chain :joins, :conditions alias_method_chain :sanitize, :conditions end @@ -34,7 +35,7 @@ def initialize_with_conditions(init_options = {}) # now you can create the rest of your search and your "scope" will get merged into your final SQL. # What this does is determine if the value a hash or a conditions object, if not it sets it up as a scope. def conditions_with_conditions=(values) - @memoized_joins = nil + @memoized_auto_joins = nil case values when Searchgasm::Conditions::Base @conditions = values @@ -44,7 +45,7 @@ def conditions_with_conditions=(values) end def conditions_with_conditions - @memoized_joins = nil # have to assume they are calling a condition on a relationship + @memoized_auto_joins = nil # have to assume they are calling a condition on a relationship conditions_without_conditions end @@ -52,8 +53,18 @@ def conditions_with_conditions # # Be careful! # ActiveRecord associations can be an SQL train wreck. Make sure you think about what you are searching and that you aren't joining a table with a million records. + def auto_joins_with_conditions + @memoized_auto_joins ||= merge_joins(auto_joins_without_conditions, conditions.auto_joins) + end + + # Changes joins to use left outer joins if conditions.any == true. def joins_with_conditions - @memoized_joins ||= merge_joins(joins_without_conditions, conditions.joins) + if conditions.any? + join_dependency = ::ActiveRecord::Associations::ClassMethods::JoinDependency.new(klass, joins_without_conditions, nil) + join_dependency.join_associations.collect { |assoc| assoc.association_join }.join + else + joins_without_conditions + end end def sanitize_with_conditions(searching = true) # :nodoc: diff --git a/lib/searchgasm/search/ordering.rb b/lib/searchgasm/search/ordering.rb index b6683b2c..a3ed8ddc 100644 --- a/lib/searchgasm/search/ordering.rb +++ b/lib/searchgasm/search/ordering.rb @@ -18,20 +18,20 @@ module Search module Ordering def self.included(klass) klass.class_eval do - alias_method_chain :joins, :ordering + alias_method_chain :auto_joins, :ordering alias_method_chain :order=, :ordering end end - def joins_with_ordering # :nodoc: - @memoized_joins ||= merge_joins(joins_without_ordering, order_by_joins) + def auto_joins_with_ordering # :nodoc: + @memoized_auto_joins ||= merge_joins(auto_joins_without_ordering, order_by_auto_joins) end def order_with_ordering=(value) # :nodoc @order_by = nil @order_as = nil - self.order_by_joins.clear - @memoized_joins = nil + self.order_by_auto_joins.clear + @memoized_auto_joins = nil self.order_without_ordering = value end @@ -101,19 +101,19 @@ def order_by # order_by = [:id, name] # => users.id ASC, user.name ASC # order_by = [:id, {:user_group => :name}] # => users.id ASC, user_groups.name ASC def order_by=(value) - self.order_by_joins.clear - @memoized_joins = nil + self.order_by_auto_joins.clear + @memoized_auto_joins = nil @order_by = get_order_by_value(value) @order = order_by_to_order(@order_by, order_as) @order_by end # Returns the joins neccessary for the "order" statement so that we don't get an SQL error - def order_by_joins - @order_by_joins ||= [] - @order_by_joins.compact! - @order_by_joins.uniq! - @order_by_joins + def order_by_auto_joins + @order_by_auto_joins ||= [] + @order_by_auto_joins.compact! + @order_by_auto_joins.uniq! + @order_by_auto_joins end private @@ -133,20 +133,20 @@ def order_by_to_order(order_by, order_as, alt_klass = nil, new_joins = []) new_joins << key.to_sym sql_parts << order_by_to_order(value, order_as, reflection.klass, new_joins) when Symbol, String - new_join = build_order_by_joins(new_joins) - self.order_by_joins << new_join if new_join + new_join = build_order_by_auto_joins(new_joins) + self.order_by_auto_joins << new_join if new_join sql_parts << "#{quote_table_name(table_name)}.#{quote_column_name(order_by)} #{order_as}" end sql_parts.join(", ") end - def build_order_by_joins(joins) + def build_order_by_auto_joins(joins) return joins.first if joins.size <= 1 joins = joins.dup key = joins.shift - {key => build_order_by_joins(joins)} + {key => build_order_by_auto_joins(joins)} end def get_order_by_value(value) diff --git a/lib/searchgasm/search/searching.rb b/lib/searchgasm/search/searching.rb new file mode 100644 index 00000000..e9715c52 --- /dev/null +++ b/lib/searchgasm/search/searching.rb @@ -0,0 +1,32 @@ +module Searchgasm + module Search + # = 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, :find, :first] + CALCULATION_METHODS = [:average, :calculate, :count, :maximum, :minimum, :sum] + + def self.included(klass) + klass.class_eval do + attr_accessor :scope + end + end + + (SEARCH_METHODS + CALCULATION_METHODS).each do |method| + class_eval <<-"end_eval", __FILE__, __LINE__ + def #{method}(*args) + find_options = {} + options = args.extract_options! # can't pass options, your options are in the search + klass.send(:with_scope, :find => scope) do + args << sanitize(#{SEARCH_METHODS.include?(method)}) + klass.#{method}(*args) + end + end + end_eval + end + end + end +end \ No newline at end of file diff --git a/lib/searchgasm/shared/searching.rb b/lib/searchgasm/shared/searching.rb deleted file mode 100644 index b635a306..00000000 --- a/lib/searchgasm/shared/searching.rb +++ /dev/null @@ -1,43 +0,0 @@ -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, :find, :first] - CALCULATION_METHODS = [:average, :calculate, :count, :maximum, :minimum, :sum] - - def self.included(klass) - klass.class_eval do - attr_accessor :scope - end - end - - (SEARCH_METHODS + CALCULATION_METHODS).each do |method| - class_eval <<-"end_eval", __FILE__, __LINE__ - def #{method}(*args) - find_options = {} - options = args.extract_options! - with_scopes = [scope, (self.class < Searchgasm::Conditions::Base ? {:conditions => sanitize} : sanitize(#{SEARCH_METHODS.include?(method)})), options].compact - with_scopes.each do |with_scope| - klass.send(:with_scope, :find => find_options) do - klass.send(:with_scope, :find => with_scope) do - find_options = klass.send(:scope, :find) - end - end - end - - if self.class < Searchgasm::Search::Base - (find_options.symbolize_keys.keys - #{SEARCH_METHODS.include?(method) ? "Search::Base::AR_FIND_OPTIONS" : "Search::Base::AR_CALCULATIONS_OPTIONS"}).each { |option| find_options.delete(option) } - end - - args << find_options - klass.#{method}(*args) - end - end_eval - end - end - end -end \ No newline at end of file diff --git a/lib/searchgasm/version.rb b/lib/searchgasm/version.rb index 6538055b..08609a8b 100644 --- a/lib/searchgasm/version.rb +++ b/lib/searchgasm/version.rb @@ -66,8 +66,8 @@ def to_a end MAJOR = 1 - MINOR = 1 - TINY = 4 + MINOR = 2 + TINY = 0 # The current version as a Version instance CURRENT = new(MAJOR, MINOR, TINY) diff --git a/test/fixtures/user_groups.yml b/test/fixtures/user_groups.yml new file mode 100644 index 00000000..e4c8e0ff --- /dev/null +++ b/test/fixtures/user_groups.yml @@ -0,0 +1,13 @@ +neco: + id: 1 + name: NECO + user_ids: + - 1 + - 2 + +johnsons: + id: 2 + name: Johnsons + user_ids: + - 1 + - 3 diff --git a/test/test_active_record_associations.rb b/test/test_active_record_associations.rb index c50a7705..3103acc2 100644 --- a/test/test_active_record_associations.rb +++ b/test/test_active_record_associations.rb @@ -1,61 +1,81 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestActiveRecordAssociations < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - - def test_build_search - search = Account.find(1).users.build_search + def test_has_many + search = Account.find(1).users.new_search assert_kind_of Searchgasm::Search::Base, search assert_equal User, search.klass assert_equal({:conditions => "\"users\".account_id = 1"}, search.scope) + assert_equal User.find(1, 3), search.all + assert_equal User.find(1), search.first + assert_equal 2, search.average("id") + assert_equal 2, search.count + search.conditions.first_name_contains = "Ben" - assert_equal({:conditions => ["\"users\".\"first_name\" LIKE ?", "%Ben%"], :limit => 25}, search.sanitize) - end - - def test_searching - assert_equal [User.find(1)], Account.find(1).users.all(:conditions => {:first_name_begins_with => "Ben"}) - assert_equal [User.find(1)], Account.find(1).users.find(:all, :conditions => {:first_name_begins_with => "Ben"}) - assert_equal User.find(1), Account.find(1).users.first(:conditions => {:first_name_begins_with => "Ben"}) - assert_equal User.find(1), Account.find(1).users.find(:first, :conditions => {:first_name_begins_with => "Ben"}) - assert_equal [], Account.find(1).users.all(:conditions => {:first_name_begins_with => "Ben"}, :per_page => 20, :page => 5) - search = Account.find(1).users.new_search - assert_equal User.find(1, 3), search.all + assert_equal [User.find(1)], search.all assert_equal User.find(1), search.first - end - - def test_calculations - assert_equal 1, Account.find(1).users.count(:conditions => {:first_name_begins_with => "Ben"}) - 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"}) + assert_equal 1, search.average("id") + assert_equal 1, search.count + + assert_equal 2, Account.find(1).users.count + assert_equal 1, Account.find(1).users.all(:conditions => {:first_name_contains => "Ben"}).size + assert_equal 0, Account.find(1).users.all(:conditions => {:first_name_contains => "No one"}).size + assert_equal 1, Account.find(1).users.sum("id", :conditions => {:first_name_contains => "Ben"}) + assert_equal 0, Account.find(1).users.sum("id", :conditions => {:first_name_contains => "No one"}) + assert_equal 1, Account.find(1).users.average("id", :conditions => {:first_name_contains => "Ben"}) end def test_has_many_through + search = Account.find(1).orders.new_search + assert_kind_of Searchgasm::Search::Base, search + assert_equal Order, search.klass + assert_equal({:joins => "INNER JOIN users ON orders.user_id = users.id ", :conditions => "(\"users\".account_id = 1)"}, search.scope) + + assert_equal [Order.find(1)], search.all + assert_equal Order.find(1), search.first + assert_equal 1, search.average("id") + assert_equal 1, search.count + + search.conditions.total_gt = 100 + + assert_equal [Order.find(1)], search.all + assert_equal Order.find(1), search.first + assert_equal 1, search.average("id") + assert_equal 1, search.count + 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}) - - search = Account.find(1).orders.new_search - assert_equal [Order.find(1)], search.all - assert_equal Order.find(1), search.first - assert_equal 1, search.average("id") - assert_equal 1, search.count end def test_habtm + search = UserGroup.find(1).users.new_search + assert_kind_of Searchgasm::Search::Base, search + assert_equal User, search.klass + assert_equal({:conditions => "\"user_groups_users\".user_group_id = 1 ", :joins => "INNER JOIN \"user_groups_users\" ON \"users\".id = \"user_groups_users\".user_id"}, search.scope) + + assert_equal User.find(1, 2), search.all + assert_equal User.find(1), search.first + assert_equal 1.5, search.average("id") + assert_equal 2, search.count + + search.conditions.first_name_contains = "Ben" + + assert_equal [User.find(1)], search.all + assert_equal User.find(1), search.first + assert_equal 1, search.average("id") + assert_equal 1, search.count + assert_equal 2, UserGroup.find(1).users.count + assert_equal 1, UserGroup.find(1).users.all(:conditions => {:first_name_contains => "Ben"}).size + assert_equal 0, UserGroup.find(1).users.all(:conditions => {:first_name_contains => "No one"}).size + assert_equal 1, UserGroup.find(1).users.sum("id", :conditions => {:first_name_contains => "Ben"}) + assert_equal 0, UserGroup.find(1).users.sum("id", :conditions => {:first_name_contains => "No one"}) + assert_equal 1, UserGroup.find(1).users.average("id", :conditions => {:first_name_contains => "Ben"}) end end diff --git a/test/test_active_record_base.rb b/test/test_active_record_base.rb index 0832cabd..d105d020 100644 --- a/test/test_active_record_base.rb +++ b/test/test_active_record_base.rb @@ -1,67 +1,46 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestActiveRecordBase < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db + def test_standard_find + assert_equal [1,2,3], Account.all.map(&:id) + assert_equal 1, Account.first.id + assert_equal [1,2,3], Account.find(:all).map(&:id) + assert_equal [1], Account.find(:all, :conditions => {:name => "Binary Logic"}).map(&:id) + assert_equal [1], Account.find(:all, :conditions => ["name = ?", "Binary Logic"]).map(&:id) + assert_equal [1], Account.find(:all, :conditions => "name = 'Binary Logic'").map(&:id) + assert_equal 1, Account.find(:first).id + assert_equal [1,2,3], Account.find(:all, nil).map(&:id) + assert_equal [1,2,3], Account.find(:all, {}).map(&:id) end - def test_standard_searches - assert_nothing_raised { Account.all } - assert_nothing_raised { Account.first } - assert_nothing_raised { Account.find(:all) } - assert_nothing_raised { Account.find(:all, :conditions => {:name => "Ben"}) } - assert_nothing_raised { Account.find(:all, :conditions => ["name = ?", "Ben"]) } - assert_nothing_raised { Account.find(:all, :conditions => "name = 'Ben'") } - assert_nothing_raised { Account.find(:first) } - assert_nothing_raised { Account.find(:all, nil) } - assert_nothing_raised { Account.find(:all, {}) } - assert_nothing_raised { Account.count({}) } - assert_nothing_raised { Account.count(nil) } - assert_nothing_raised { Account.sum("id") } - assert_nothing_raised { Account.sum("id", {}) } + def test_standard_calculations + assert_equal 3, Account.count({}) + assert_equal 3, Account.count(nil) + assert_equal 3, Account.count(:limit => 1) + assert_equal 0, Account.count(:limit => 10, :offset => 10) + assert_equal 6, Account.sum("id") + assert_equal 6, Account.sum("id", {}) + assert_equal 2, Account.average("id") + assert_equal 3, Account.maximum("id") + assert_equal 1, Account.minimum("id") end - def test_ar_options + def test_valid_ar_options assert_equal [ :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :from, :lock ], ActiveRecord::Base.valid_find_options assert_equal [:conditions, :joins, :order, :select, :group, :having, :distinct, :limit, :offset, :include, :from], ActiveRecord::Base.valid_calculations_options end def test_build_search - search = Account.new_search - assert_kind_of Searchgasm::Search::Base, search - - search = Account.build_search(:conditions => {:name_keywords => "awesome"}, :page => 2, :per_page => 15) + search = Account.new_search(:conditions => {:name_keywords => "awesome"}, :page => 2, :per_page => 15) assert_kind_of Searchgasm::Search::Base, search + assert_equal({}, search.scope) assert_equal Account, search.klass assert_equal "awesome", search.conditions.name_keywords assert_equal 2, search.page assert_equal 15, search.per_page - - search = Account.new_search(:conditions => {:name_keywords => "awesome"}, :page => 2, :per_page => 15) - assert_equal Account, search.klass end - def test_build_conditions - search = Account.new_conditions - assert_kind_of Searchgasm::Conditions::Base, search - - search = Account.build_conditions(:name_keywords => "awesome") - assert_kind_of Searchgasm::Conditions::Base, search - assert_equal Account, search.klass - assert_equal "awesome", search.name_keywords - - search = Account.new_conditions(:name_keywords => "awesome") - assert_equal Account, search.klass - end - - def test_searching + def test_searchgasm_searching assert_equal Account.find(1, 3), Account.all(:conditions => {:name_contains => "Binary"}) assert_equal [Account.find(1)], Account.all(:conditions => {:name_contains => "Binary", :users => {:first_name_starts_with => "Ben"}}) assert_equal [], Account.all(:conditions => {:name_contains => "Binary", :users => {:first_name_starts_with => "Ben", :last_name => "Mills"}}) @@ -73,23 +52,34 @@ def test_searching assert_equal [], Account.all(:conditions => {:name_contains => "Binary"}, :page => 2, :per_page => 20) end - def test_counting + def test_searchgasm_counting assert_equal 2, Account.count(:conditions => {:name_contains => "Binary"}) assert_equal 1, Account.count(:conditions => {:name_contains => "Binary", :users => {:first_name_contains => "Ben"}}) + assert_equal 1, Account.count(:conditions => {:name_contains => "Binary", :users => {:first_name_contains => "Ben"}}, :limit => 10, :offset => 10, :order_by => "id", :group => "id") end def test_scoping - assert_equal nil, Account.send(:scope, :find) + assert_equal({:conditions => {:name => "Binary"}, :limit => 10, :readonly => true}, Account.send(:with_scope, :find => {:conditions => {:name => "Binary"}, :limit => 10, :readonly => true}) { Account.send(:scope, :find) }) + assert_equal({:conditions => ["\"accounts\".\"name\" LIKE ?", "%Binary%"], :limit => 10, :offset => 20}, Account.send(:with_scope, :find => {:conditions => {:name_contains => "Binary"}, :per_page => 10, :page => 3}) { Account.send(:scope, :find) }) end - def test_count - assert_equal 3, Account.count - assert_equal 3, Account.count(:limit => 1) - assert_equal 0, Account.count(:limit => 1, :offset => 1) # not sure why AR doesn't ignore offset like it does for limit - search = Account.new_search - assert_equal 3, search.count - search.per_page = 10 - search.page = 10 - assert_equal 3, search.count + def test_accessible_conditions + Account.conditions_accessible :name_contains + assert_equal Set.new(["name_contains"]), Account.accessible_conditions + Account.conditions_accessible :id_gt + assert_equal Set.new(["id_gt", "name_contains"]), Account.accessible_conditions + Account.conditions_accessible :id_gt, :name_contains + assert_equal Set.new(["id_gt", "name_contains"]), Account.accessible_conditions + Account.send(:write_inheritable_attribute, :conditions_accessible, nil) + end + + def test_protected_conditions + Account.conditions_protected :name_contains + assert_equal Set.new(["name_contains"]), Account.protected_conditions + Account.conditions_protected :id_gt + assert_equal Set.new(["id_gt", "name_contains"]), Account.protected_conditions + Account.conditions_protected :id_gt, :name_contains + assert_equal Set.new(["id_gt", "name_contains"]), Account.protected_conditions + Account.send(:write_inheritable_attribute, :conditions_protected, nil) end end diff --git a/test/test_condition_base.rb b/test/test_condition_base.rb index 2a9c39b0..30a07331 100644 --- a/test/test_condition_base.rb +++ b/test/test_condition_base.rb @@ -1,29 +1,42 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestConditionBase < Test::Unit::TestCase - fixtures :accounts, :users, :orders + def test_condition_name + assert_equal "equals", Searchgasm::Condition::Equals.condition_name + assert_equal "keywords", Searchgasm::Condition::Keywords.condition_name + assert_equal "greater_than_or_equal_to", Searchgasm::Condition::GreaterThanOrEqualTo.condition_name + end - def setup - setup_db - load_fixtures + def test_name_for_column + assert_equal "id_equals", Searchgasm::Condition::Equals.name_for_column(Account.columns_hash["id"]) + assert_equal nil, Searchgasm::Condition::Keywords.name_for_column(Account.columns_hash["id"]) end - def teardown - teardown_db + def test_ignore_blanks? + assert !Searchgasm::Condition::Equals.ignore_blanks? + assert Searchgasm::Condition::Keywords.ignore_blanks? end - def test_condition_name - assert_equal "equals", Searchgasm::Condition::Equals.condition_name - assert_equal "keywords", Searchgasm::Condition::Keywords.condition_name - assert_equal "greater_than_or_equal_to", Searchgasm::Condition::GreaterThanOrEqualTo.condition_name + def test_type_cast_value? + assert Searchgasm::Condition::Equals.type_cast_value? + assert Searchgasm::Condition::Keywords.type_cast_value? + assert !Searchgasm::Condition::IsNil.type_cast_value? + assert !Searchgasm::Condition::IsBlank.type_cast_value? end def test_string_column - + assert !Searchgasm::Condition::Base.string_column?(Account.columns_hash["id"]) + assert Searchgasm::Condition::Base.string_column?(Account.columns_hash["name"]) + assert !Searchgasm::Condition::Base.string_column?(Account.columns_hash["active"]) + assert Searchgasm::Condition::Base.string_column?(User.columns_hash["bio"]) end def test_comparable_column - + assert Searchgasm::Condition::Base.comparable_column?(Account.columns_hash["id"]) + assert !Searchgasm::Condition::Base.comparable_column?(Account.columns_hash["name"]) + assert !Searchgasm::Condition::Base.comparable_column?(Account.columns_hash["active"]) + assert !Searchgasm::Condition::Base.comparable_column?(User.columns_hash["bio"]) + assert Searchgasm::Condition::Base.comparable_column?(Order.columns_hash["total"]) end def test_initialize @@ -35,19 +48,29 @@ def test_initialize assert_equal condition.column, Account.columns_hash["id"] end - def test_ignore_blanks? - condition = Searchgasm::Condition::Equals.new(Account, Account.columns_hash["id"]) - assert !condition.class.ignore_blanks? - + def test_explicitly_set_value condition = Searchgasm::Condition::Keywords.new(Account, Account.columns_hash["name"]) - assert condition.class.ignore_blanks? + assert !condition.explicitly_set_value? + condition.value = "test" + assert condition.explicitly_set_value? end - def test_value + def test_name + condition = Searchgasm::Condition::Keywords.new(Account, Account.columns_hash["name"]) + assert_equal "name_keywords", condition.name + + condition = Searchgasm::Condition::DescendantOf.new(User) + assert_equal "descendant_of", condition.name + condition = Searchgasm::Condition::DescendantOf.new(Account) + assert_equal nil, condition.name end - def test_method_creation_in_scope - # test ot make sure methods are not created across the board for all models + def test_sanitize + # This is tested thoroughly in test_condition_types + end + + def test_value + # This is tested thoroughly in test_condition_types end end diff --git a/test/test_condition_types.rb b/test/test_condition_types.rb index 432faa31..09cb0d41 100644 --- a/test/test_condition_types.rb +++ b/test/test_condition_types.rb @@ -1,17 +1,6 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestConditionTypes < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - def test_sanitize condition = Searchgasm::Condition::BeginsWith.new(Account, Account.columns_hash["name"]) condition.value = "Binary" diff --git a/test/test_conditions_base.rb b/test/test_conditions_base.rb index 7608a2be..e46e8d4d 100644 --- a/test/test_conditions_base.rb +++ b/test/test_conditions_base.rb @@ -1,39 +1,130 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestConditionsBase < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - def test_register_conditions Searchgasm::Conditions::Base.register_condition(Searchgasm::Condition::Keywords) assert [Searchgasm::Condition::Keywords], Searchgasm::Conditions::Base.conditions Searchgasm::Conditions::Base.register_condition(Searchgasm::Condition::Contains) assert [Searchgasm::Condition::Keywords, Searchgasm::Condition::Contains], Searchgasm::Conditions::Base.conditions - + end + + def test_association_names + assert_equal ["account", "parent", "orders", "user_groups", "children"], Searchgasm::Cache::UserConditions.association_names + assert_equal ["admin", "orders", "users"], Searchgasm::Cache::AccountConditions.association_names + end + + def test_condition_names + # This is tested thoroughly through the tests + end + + def test_needed + assert !Searchgasm::Conditions::Base.needed?(User, {}) + assert !Searchgasm::Conditions::Base.needed?(User, {:first_name => "Ben"}) + assert Searchgasm::Conditions::Base.needed?(User, {:first_name_contains => "Awesome"}) end def test_initialize - conditions = Account.new_conditions(:name_contains => "Binary") + conditions = Searchgasm::Cache::AccountConditions.new(:name_contains => "Binary") assert_equal conditions.klass, Account assert_equal conditions.name_contains, "Binary" end - def test_conditions_added - # test to make sure all of the proper methods were add, testing condition_names basically + def test_any + conditions = Searchgasm::Cache::AccountConditions.new + assert !conditions.any? + + conditions = Searchgasm::Cache::AccountConditions.new(:any => true) + assert conditions.any? + conditions.any = "false" + assert !conditions.any? + + conditions = Searchgasm::Cache::AccountConditions.new + conditions.name_contains = "Binary" + assert_equal ["\"accounts\".\"name\" LIKE ?", "%Binary%"], conditions.sanitize + conditions.id = 1 + assert_equal ["(\"accounts\".\"name\" LIKE ?) AND (\"accounts\".\"id\" = 1)", "%Binary%"], conditions.sanitize + conditions.any = true + assert_equal ["(\"accounts\".\"name\" LIKE ?) OR (\"accounts\".\"id\" = 1)", "%Binary%"], conditions.sanitize + conditions.any = false + assert_equal ["(\"accounts\".\"name\" LIKE ?) AND (\"accounts\".\"id\" = 1)", "%Binary%"], conditions.sanitize + end + + def test_auto_joins + conditions = Searchgasm::Cache::AccountConditions.new + assert_equal conditions.auto_joins, nil + + conditions.name_like = "Binary" + assert_equal conditions.auto_joins, nil + + conditions.users.first_name_like = "Ben" + assert_equal conditions.auto_joins, :users + + conditions.users.orders.description_like = "apple" + assert_equal conditions.auto_joins, {:users => :orders} + end + + def test_inspect + conditions = Searchgasm::Cache::AccountConditions.new + assert_nothing_raised { conditions.inspect } + end + + def test_sanitize + conditions = Searchgasm::Cache::AccountConditions.new + conditions.name_contains = "Binary" + conditions.id_gt = 5 + now = Time.now + conditions.created_after = now + assert_equal conditions.sanitize, ["(\"accounts\".\"name\" LIKE ?) AND (\"accounts\".\"id\" > ?) AND (\"accounts\".\"created_at\" > ?)", "%Binary%", 5, now] + + # test out associations + conditions.users.first_name_like = "Ben" + conditions.users.id_gt = 10 + conditions.users.orders.total_lt = 500 + assert_equal conditions.sanitize, ["(\"accounts\".\"name\" LIKE ?) AND (\"accounts\".\"id\" > ?) AND (\"accounts\".\"created_at\" > ?) AND ((\"users\".\"first_name\" LIKE ?) AND (\"users\".\"id\" > ?) AND (\"orders\".\"total\" < ?))", "%Binary%", 5, now, "%Ben%", 10, 500] + + # test that raw sql is returned + conditions.conditions = "awesome" + assert_equal "awesome", conditions.sanitize + end + + def test_conditions + conditions = Searchgasm::Cache::AccountConditions.new + now = Time.now + v = {:name_contains => "Binary", :created_at_greater_than => now} + conditions.conditions = v + assert_equal v, conditions.conditions + + sql = "id in (1,2,3,4)" + conditions.conditions = sql + assert_equal sql, conditions.conditions + assert_equal [], conditions.send(:objects) + + v2 = {:id_less_than => 5, :name_begins_with => "Beginning of string"} + conditions.conditions = v2 + assert_equal v2, conditions.conditions + + v = {:name_contains => "Binary", :created_at_greater_than => now} + conditions.conditions = v + assert_equal v2.merge(v), conditions.conditions + + sql2 = "id > 5 and name = 'Test'" + conditions.conditions = sql2 + assert_equal sql2, conditions.conditions + assert_equal [], conditions.send(:objects) + + conditions.name_contains = "awesome" + assert_equal({:name_contains => "awesome"}, conditions.conditions) + + conditions.conditions = {:id_gt => "", :name_starts_with => "Ben"} + assert_equal({:name_contains => "awesome", :name_begins_with => "Ben"}, conditions.conditions) end + # Other general usage tests, need to clean these up + def test_setting_conditions [Account, User, Order].each do |klass| - conditions = klass.new_conditions + conditions = "Searchgasm::Cache::#{klass}Conditions".constantize.new conditions.class.condition_names.each do |condition_name| conditions.send("#{condition_name}=", 1) assert_equal 1, conditions.send(condition_name) @@ -43,13 +134,13 @@ def test_setting_conditions def test_accessible_protected_conditions Account.conditions_accessible << :name_contains - conditions = Account.new_conditions + conditions = Searchgasm::Cache::AccountConditions.new 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 + conditions = Searchgasm::Cache::AccountConditions.new now = Time.now conditions.conditions = {:created_after => now, :name_contains => "Binary"} assert({:created_after => now}, conditions.conditions) @@ -57,36 +148,22 @@ def test_accessible_protected_conditions end def test_assert_valid_values - conditions = User.new_conditions + conditions = Searchgasm::Cache::UserConditions.new assert_raise(ArgumentError) { conditions.conditions = {:unknown => "blah"} } assert_nothing_raised { conditions.conditions = {:first_name => "blah"} } assert_nothing_raised { conditions.conditions = {:first_name_contains => "blah"} } end def test_setting_associations - conditions = Account.new_conditions(:users => {:first_name_like => "Ben"}) + conditions = Searchgasm::Cache::AccountConditions.new(:users => {:first_name_like => "Ben"}) assert_equal conditions.users.first_name_like, "Ben" conditions.users.last_name_begins_with = "Ben" assert_equal conditions.users.last_name_begins_with, "Ben" end - def test_joins - conditions = Account.new_conditions - assert_equal conditions.joins, nil - - conditions.name_like = "Binary" - assert_equal conditions.joins, nil - - conditions.users.first_name_like = "Ben" - assert_equal conditions.joins, :users - - conditions.users.orders.description_like = "apple" - assert_equal conditions.joins, {:users => :orders} - end - def test_objects - conditions = Account.new_conditions + conditions = Searchgasm::Cache::AccountConditions.new assert_equal conditions.send(:objects), [] conditions.name_contains = "Binary" @@ -97,7 +174,7 @@ def test_objects end def test_reset - conditions = Account.new_conditions + conditions = Searchgasm::Cache::AccountConditions.new conditions.name_contains = "Binary" assert_equal 1, conditions.send(:objects).size @@ -121,88 +198,4 @@ def test_reset conditions.reset_users! assert_equal [], conditions.send(:objects) end - - def test_sanitize - conditions = Account.new_conditions - conditions.name_contains = "Binary" - conditions.id_gt = 5 - now = Time.now - conditions.created_after = now - assert_equal conditions.sanitize, ["(\"accounts\".\"name\" LIKE ?) AND (\"accounts\".\"id\" > ?) AND (\"accounts\".\"created_at\" > ?)", "%Binary%", 5, now] - - # test out associations - conditions.users.first_name_like = "Ben" - conditions.users.id_gt = 10 - conditions.users.orders.total_lt = 500 - assert_equal conditions.sanitize, ["(\"accounts\".\"name\" LIKE ?) AND (\"accounts\".\"id\" > ?) AND (\"accounts\".\"created_at\" > ?) AND ((\"users\".\"first_name\" LIKE ?) AND (\"users\".\"id\" > ?) AND (\"orders\".\"total\" < ?))", "%Binary%", 5, now, "%Ben%", 10, 500] - end - - def test_conditions - conditions = Account.new_conditions! - now = Time.now - v = {:name_contains => "Binary", :created_at_greater_than => now} - conditions.conditions = v - assert_equal v, conditions.conditions - - sql = "id in (1,2,3,4)" - conditions.conditions = sql - assert_equal sql, conditions.conditions - - v2 = {:id_less_than => 5, :name_begins_with => "Beginning of string"} - conditions.conditions = v2 - assert_equal v2, conditions.conditions - - v = {:name_contains => "Binary", :created_at_greater_than => now} - conditions.conditions = v - assert_equal v2.merge(v), conditions.conditions - - sql2 = "id > 5 and name = 'Test'" - conditions.conditions = sql2 - assert_equal sql2, conditions.conditions - - conditions.name_contains = "awesome" - assert_equal({:name_contains => "awesome"}, conditions.conditions) - end - - def test_searching - conditions = Account.new_conditions - conditions.name_contains = "Binary" - assert_equal Account.find(1, 3), conditions.all - assert_equal Account.find(1, 3), conditions.find(:all) - assert_equal Account.find(1), conditions.first - assert_equal Account.find(1), conditions.find(:first) - assert_equal 2, conditions.average('id') - assert_equal 2, conditions.calculate(:avg, 'id') - assert_equal 3, conditions.calculate(:max, 'id') - assert_equal 2, conditions.count - assert_equal 3, conditions.maximum('id') - assert_equal 1, conditions.minimum('id') - assert_equal 4, conditions.sum('id') - end - - def test_any - conditions = Account.new_conditions - conditions.name_contains = "Binary" - assert_equal Account.find(1, 3), conditions.all - conditions.id = 1 - assert_equal [Account.find(1)], conditions.all - conditions.any = true - assert_equal Account.find(1, 3), conditions.all - conditions.any = false - assert_equal [Account.find(1)], conditions.all - end - - def test_ignoring_blanks - conditions = Account.new_conditions(:name => "", :created_after => nil) - assert_equal(nil, conditions.conditions) - conditions.name = "" - assert_equal({:name_equals => ""}, conditions.conditions) - conditions.created_after = "" - assert_equal({:name_equals => ""}, conditions.conditions) - end - - def test_inspect - conditions = Account.new_conditions - assert_nothing_raised { conditions.inspect } - end end diff --git a/test/test_conditions_protection.rb b/test/test_conditions_protection.rb index 7ec620d9..94d0d3fb 100644 --- a/test/test_conditions_protection.rb +++ b/test/test_conditions_protection.rb @@ -1,27 +1,16 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestConditionsProtection < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - def test_protection - assert_raise(ArgumentError) { Account.new_conditions("(DELETE FROM users)") } - assert_nothing_raised { Account.build_conditions!("(DELETE FROM users)") } + assert_raise(ArgumentError) { Account.new_search(:conditions => "(DELETE FROM users)") } + assert_nothing_raised { Account.new_search!(:conditions => "(DELETE FROM users)") } account = Account.first - assert_raise(ArgumentError) { account.users.build_conditions("(DELETE FROM users)") } - assert_nothing_raised { account.users.build_conditions!("(DELETE FROM users)") } + assert_raise(ArgumentError) { account.users.new_search(:conditions => "(DELETE FROM users)") } + assert_nothing_raised { account.users.new_search!(:conditions => "(DELETE FROM users)") } - #search = Account.new_search - #assert_nothing_raised { search.conditions = "(DELETE FROM users)" } + search = Account.new_search + assert_raise(ArgumentError) { search.conditions = "(DELETE FROM users)" } end end diff --git a/test/test_config.rb b/test/test_config.rb index 3da0a7d9..552b8610 100644 --- a/test/test_config.rb +++ b/test/test_config.rb @@ -1,17 +1,6 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestConfig < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - def test_per_page Searchgasm::Config.per_page = 1 @@ -31,4 +20,6 @@ def test_per_page assert User.new_search.all.size > 1 assert User.new_search(:per_page => 1).all.size == 1 end + + # test that config options do not mess up regular AR searches end \ No newline at end of file diff --git a/test/test_helper.rb b/test/test_helper.rb index f7ae5695..536dc118 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -14,11 +14,15 @@ class Account < ActiveRecord::Base has_many :orders, :through => :users end +class UserGroup < ActiveRecord::Base + has_and_belongs_to_many :users +end + class User < ActiveRecord::Base acts_as_tree belongs_to :account has_many :orders, :dependent => :destroy - has_one :cool_order, :class_name => "Order", :conditions => {:total => 100} + has_and_belongs_to_many :user_groups end class Order < ActiveRecord::Base @@ -26,11 +30,6 @@ class Order < ActiveRecord::Base end class Test::Unit::TestCase - def self.fixtures(*fixtures) - @fixtures ||= [] - @fixtures += [fixtures].flatten - end - def setup_db ActiveRecord::Schema.define(:version => 1) do create_table :accounts do |t| @@ -40,6 +39,17 @@ def setup_db t.boolean :active end + create_table :user_groups do |t| + t.datetime :created_at + t.datetime :updated_at + t.string :name + end + + create_table :user_groups_users, :id => false do |t| + t.integer :user_group_id + t.integer :user_id + end + create_table :users do |t| t.datetime :created_at t.datetime :updated_at @@ -63,7 +73,8 @@ def setup_db end def load_fixtures - self.class.fixtures.each do |fixture| + fixtures = [:accounts, :orders, :users, :user_groups] + fixtures.each do |fixture| records = YAML.load(File.read(File.dirname(__FILE__) + "/fixtures/#{fixture.to_s}.yml")) records.each do |name, attributes| record = fixture.to_s.singularize.classify.constantize.new @@ -71,6 +82,17 @@ def load_fixtures record.save! end end + + # Not sure why I have to do this, but sick of dealing with it + UserGroup.all.each do |user_group| + user_ids = user_group.user_ids.uniq! + user_group.users.clear + user_group.user_ids = user_ids + user_group.save! + end + + # Create the cached virtual classes + fixtures.each { |fixture| fixture.to_s.classify.constantize.new_search } end def teardown_db @@ -78,4 +100,13 @@ def teardown_db ActiveRecord::Base.connection.drop_table(table) end end + + def setup + setup_db + load_fixtures + end + + def teardown + teardown_db + end end \ No newline at end of file diff --git a/test/test_search_base.rb b/test/test_search_base.rb index e635cff5..0db83258 100644 --- a/test/test_search_base.rb +++ b/test/test_search_base.rb @@ -1,17 +1,6 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestSearchBase < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - def test_needed assert Searchgasm::Search::Base.needed?(Account, :page => 2, :conditions => {:name => "Ben"}) assert !Searchgasm::Search::Base.needed?(Account, :conditions => {:name => "Ben"}) @@ -109,20 +98,20 @@ def test_setting_first_level_options assert_equal search.lock, true end - def test_joins + def test_auto_joins search = Account.new_search - assert_equal nil, search.joins + assert_equal nil, search.auto_joins search.conditions.name_contains = "Binary" - assert_equal nil, search.joins + assert_equal nil, search.auto_joins search.conditions.users.first_name_contains = "Ben" - assert_equal(:users, search.joins) + assert_equal(:users, search.auto_joins) search.conditions.users.orders.id_gt = 2 - assert_equal({:users => :orders}, search.joins) + assert_equal({:users => :orders}, search.auto_joins) search.conditions.users.reset_orders! - assert_equal(:users, search.joins) + assert_equal(:users, search.auto_joins) search.conditions.users.orders.id_gt = 2 search.conditions.reset_users! - assert_equal nil, search.joins + assert_equal nil, search.auto_joins end def test_limit diff --git a/test/test_search_conditions.rb b/test/test_search_conditions.rb index 2559c37d..03cd02a9 100644 --- a/test/test_search_conditions.rb +++ b/test/test_search_conditions.rb @@ -1,17 +1,6 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestSearchConditions < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - def test_conditions search = Account.new_search assert_kind_of Searchgasm::Conditions::Base, search.conditions @@ -19,9 +8,32 @@ def test_conditions search.conditions = {:name_like => "Binary"} assert_kind_of Searchgasm::Conditions::Base, search.conditions + + search = Account.new_search(:conditions => {:name_like => "Ben"}) + assert_equal({:name_contains => "Ben"}, search.conditions.conditions) + end + + def test_auto_joins + search = Account.new_search + search.conditions = {:name_like => "Binary"} + assert_equal nil, search.auto_joins + search.conditions.users.first_name_like = "Ben" + assert_equal :users, search.auto_joins + search.conditions.reset_users! + assert_equal nil, search.auto_joins + end + + def test_joins + search = Account.new_search + search.conditions = {:id_lte => 2, :users => {:first_name_like => "Ben"}} + assert_equal :users, search.joins + assert_equal [Account.find(1)], search.all + search.conditions.any = true + assert_equal " LEFT OUTER JOIN \"users\" ON users.account_id = accounts.id ", search.joins + assert_equal Account.find(1, 2), search.all + end - conditions = Account.new_conditions(:id_greater_than => 8) - search.conditions = conditions - assert_equal conditions, search.conditions + def test_sanitize + # This is tested in test_search_base end end \ No newline at end of file diff --git a/test/test_search_ordering.rb b/test/test_search_ordering.rb index cd6e8280..a40536f9 100644 --- a/test/test_search_ordering.rb +++ b/test/test_search_ordering.rb @@ -1,17 +1,6 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestSearchOrdering < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - def test_order_as search = Account.new_search assert_equal nil, search.order diff --git a/test/test_search_pagination.rb b/test/test_search_pagination.rb index ce1cb5a0..54c9e757 100644 --- a/test/test_search_pagination.rb +++ b/test/test_search_pagination.rb @@ -1,17 +1,6 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestSearchPagination < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - def test_limit search = Account.new_search search.limit = 10 diff --git a/test/test_search_protection.rb b/test/test_search_protection.rb index c256159e..921488c9 100644 --- a/test/test_search_protection.rb +++ b/test/test_search_protection.rb @@ -1,17 +1,6 @@ require File.dirname(__FILE__) + '/test_helper.rb' class TestSearchProtection < Test::Unit::TestCase - fixtures :accounts, :users, :orders - - def setup - setup_db - load_fixtures - end - - def teardown - teardown_db - end - def test_protection assert_raise(ArgumentError) { Account.build_search(:conditions => "(DELETE FROM users)", :page => 2, :per_page => 15) } Searchgasm::Search::Base::VULNERABLE_FIND_OPTIONS.each { |option| assert_raise(ArgumentError) { Account.build_search(option => "(DELETE FROM users)") } } diff --git a/test/text_config.rb b/test/text_config.rb deleted file mode 100644 index 2cd6bc67..00000000 --- a/test/text_config.rb +++ /dev/null @@ -1 +0,0 @@ -# need to test that setting config doesn't mess up regular searches, only protected ones, etc \ No newline at end of file