Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Updated searchgasm

  • Loading branch information...
commit 46b67220a8d4d2d73b14f71b780f8768283195cf 1 parent bcf6b5f
@binarylogic authored
Showing with 735 additions and 257 deletions.
  1. +1 −0  app/controllers/non_ajax/users_controller.rb
  2. +0 −7 config/database.example.yml
  3. +5 −5 config/environment.rb
  4. +21 −0 vendor/plugins/searchgasm/CHANGELOG.rdoc
  5. +9 −4 vendor/plugins/searchgasm/Manifest
  6. +1 −1  vendor/plugins/searchgasm/README.rdoc
  7. +2 −3 vendor/plugins/searchgasm/Rakefile
  8. +5 −0 vendor/plugins/searchgasm/TODO.rdoc
  9. +4 −3 vendor/plugins/searchgasm/lib/searchgasm.rb
  10. +15 −0 vendor/plugins/searchgasm/lib/searchgasm/active_record/connection_adapters/mysql_adapter.rb
  11. 0  vendor/plugins/searchgasm/lib/searchgasm/active_record/connection_adapters/postgresql_adapter.rb
  12. 0  vendor/plugins/searchgasm/lib/searchgasm/active_record/connection_adapters/sqlite_adapter.rb
  13. +26 −10 vendor/plugins/searchgasm/lib/searchgasm/condition/base.rb
  14. +5 −6 vendor/plugins/searchgasm/lib/searchgasm/condition/{is_blank.rb → blank.rb}
  15. +1 −1  vendor/plugins/searchgasm/lib/searchgasm/condition/does_not_equal.rb
  16. +32 −0 vendor/plugins/searchgasm/lib/searchgasm/condition/during_evening.rb
  17. +1 −1  vendor/plugins/searchgasm/lib/searchgasm/condition/equals.rb
  18. +2 −1  vendor/plugins/searchgasm/lib/searchgasm/condition/greater_than.rb
  19. +7 −1 vendor/plugins/searchgasm/lib/searchgasm/condition/greater_than_or_equal_to.rb
  20. +0 −23 vendor/plugins/searchgasm/lib/searchgasm/condition/is_nil.rb
  21. +2 −1  vendor/plugins/searchgasm/lib/searchgasm/condition/less_than.rb
  22. +7 −1 vendor/plugins/searchgasm/lib/searchgasm/condition/less_than_or_equal_to.rb
  23. +21 −0 vendor/plugins/searchgasm/lib/searchgasm/condition/nil.rb
  24. +6 −3 vendor/plugins/searchgasm/lib/searchgasm/conditions/base.rb
  25. +24 −2 vendor/plugins/searchgasm/lib/searchgasm/config.rb
  26. +37 −3 vendor/plugins/searchgasm/lib/searchgasm/core_ext/hash.rb
  27. +80 −25 vendor/plugins/searchgasm/lib/searchgasm/helpers/control_types/link.rb
  28. +1 −1  vendor/plugins/searchgasm/lib/searchgasm/helpers/control_types/remote_select.rb
  29. +4 −4 vendor/plugins/searchgasm/lib/searchgasm/helpers/control_types/select.rb
  30. +1 −1  vendor/plugins/searchgasm/lib/searchgasm/helpers/form.rb
  31. +102 −14 vendor/plugins/searchgasm/lib/searchgasm/helpers/utilities.rb
  32. +12 −16 vendor/plugins/searchgasm/lib/searchgasm/search/base.rb
  33. +133 −50 vendor/plugins/searchgasm/lib/searchgasm/search/ordering.rb
  34. +3 −3 vendor/plugins/searchgasm/lib/searchgasm/search/protection.rb
  35. +10 −0 vendor/plugins/searchgasm/lib/searchgasm/shared/utilities.rb
  36. +1 −1  vendor/plugins/searchgasm/lib/searchgasm/version.rb
  37. +1 −0  vendor/plugins/searchgasm/test/test_active_record_base.rb
  38. +10 −8 vendor/plugins/searchgasm/test/test_condition_base.rb
  39. +30 −30 vendor/plugins/searchgasm/test/test_condition_types.rb
  40. +1 −0  vendor/plugins/searchgasm/test/test_conditions_base.rb
  41. +4 −0 vendor/plugins/searchgasm/test/test_search_base.rb
  42. +108 −28 vendor/plugins/searchgasm/test/test_search_ordering.rb
View
1  app/controllers/non_ajax/users_controller.rb
@@ -1,6 +1,7 @@
class NonAjax::UsersController < ApplicationController
def index
@search = User.new_search(params[:search])
+ @search.order_by ||= :first_name
@users, @users_count = @search.all, @search.count
end
end
View
7 config/database.example.yml
@@ -1,24 +1,17 @@
login: &login
username: root
- password:
development:
adapter: mysql
database: searchgasm_development
- encoding: utf8
- socket: /tmp/mysql.sock
<<: *login
test:
adapter: mysql
database: searchgasm_test
- encoding: utf8
- socket: /tmp/mysql.sock
<<: *login
production:
adapter: mysql
database: searchgasm_production
- encoding: utf8
- socket: /tmp/mysql.sock
<<: *login
View
10 config/environment.rb
@@ -45,11 +45,11 @@
# Run "rake -D time" for a list of tasks for finding time zone names. Uncomment to use default local time.
config.time_zone = 'UTC'
- #if RAILS_ENV == "development" # putting this in environments/development.rb doesn't work, not sure why
- # config.plugin_paths = ["#{RAILS_ROOT}/../../Libs"]
- # config.plugins = [:searchgasm]
- # require "ruby-debug"
- #end
+ if RAILS_ENV == "development" # putting this in environments/development.rb doesn't work, not sure why
+ config.plugin_paths = ["#{RAILS_ROOT}/../../Libs"]
+ config.plugins = [:searchgasm]
+ require "ruby-debug"
+ end
# Your secret key for verifying cookie session data integrity.
# If you change this key, all old sessions will become invalid!
View
21 vendor/plugins/searchgasm/CHANGELOG.rdoc
@@ -1,3 +1,24 @@
+== 1.2.2 released 2008-09-29
+
+* Fixed bug when reverse engineering order to order_by, assumed ASC and DESC would always be present when they are not.
+* False is a meaningful value for some conditions, and false.blank? == true. So instead of using value.blank? to ignore conditions we use meaningless?(value), which returns false if it is false.
+* Fixed aliases for lt, lte, gt, and gte.
+* Fixed bug when writing conditions on associations via a hash with string keys
+* Added Config.remove_duplicates to turn off the "automatic" removing of duplicates if desired.
+* Updated searchgasm_state helper to insert the entire state all at once.
+* Added CSS class "ordering" to order_by_link if the search is being ordered by that.
+
+== 1.2.1 released 2008-09-25
+
+* Fixed problem when determining if an order_by_link is currently being ordered. Just "stringified" both comparable values.
+* Removed default order_by and order_as. They will ONLY have values if you specify how to order, otherwise they are nil.
+* Removed order_as requirement. order_as is optional.
+* Added in deep_merge methods for hash, copied over from ActiveSupport 2.1
+* Improved order by auto joins to be based off of what order_by returns instead of setting it when setting order_by.
+* Added priority_order_by. Useful if you want to order featured products first and then order as usual. See documentation in Searchgasm::Search::Ordering for more info.
+* Added in base64 support for order_by and priority_order_by so that it's value is safe in the URL
+* Added priority_order_by_link
+
== 1.2.0 released 2008-09-24
* Added searchgasm_params and searchgasm_url helper to use outside of the control type helpers.
View
13 vendor/plugins/searchgasm/Manifest
@@ -3,22 +3,26 @@ examples/README.rdoc
init.rb
lib/searchgasm/active_record/associations.rb
lib/searchgasm/active_record/base.rb
+lib/searchgasm/active_record/connection_adapters/mysql_adapter.rb
+lib/searchgasm/active_record/connection_adapters/postgresql_adapter.rb
+lib/searchgasm/active_record/connection_adapters/sqlite_adapter.rb
lib/searchgasm/condition/base.rb
lib/searchgasm/condition/begins_with.rb
+lib/searchgasm/condition/blank.rb
lib/searchgasm/condition/child_of.rb
lib/searchgasm/condition/contains.rb
lib/searchgasm/condition/descendant_of.rb
lib/searchgasm/condition/does_not_equal.rb
+lib/searchgasm/condition/during_evening.rb
lib/searchgasm/condition/ends_with.rb
lib/searchgasm/condition/equals.rb
lib/searchgasm/condition/greater_than.rb
lib/searchgasm/condition/greater_than_or_equal_to.rb
lib/searchgasm/condition/inclusive_descendant_of.rb
-lib/searchgasm/condition/is_blank.rb
-lib/searchgasm/condition/is_nil.rb
lib/searchgasm/condition/keywords.rb
lib/searchgasm/condition/less_than.rb
lib/searchgasm/condition/less_than_or_equal_to.rb
+lib/searchgasm/condition/nil.rb
lib/searchgasm/condition/sibling_of.rb
lib/searchgasm/condition/tree.rb
lib/searchgasm/conditions/base.rb
@@ -38,7 +42,7 @@ lib/searchgasm/search/conditions.rb
lib/searchgasm/search/ordering.rb
lib/searchgasm/search/pagination.rb
lib/searchgasm/search/protection.rb
-lib/searchgasm/shared/searching.rb
+lib/searchgasm/search/searching.rb
lib/searchgasm/shared/utilities.rb
lib/searchgasm/shared/virtual_classes.rb
lib/searchgasm/version.rb
@@ -49,6 +53,7 @@ Rakefile
README.rdoc
test/fixtures/accounts.yml
test/fixtures/orders.yml
+test/fixtures/user_groups.yml
test/fixtures/users.yml
test/libs/acts_as_tree.rb
test/libs/rexml_fix.rb
@@ -65,4 +70,4 @@ test/test_search_conditions.rb
test/test_search_ordering.rb
test/test_search_pagination.rb
test/test_search_protection.rb
-test/text_config.rb
+TODO.rdoc
View
2  vendor/plugins/searchgasm/README.rdoc
@@ -345,7 +345,7 @@ What that rule means is that any options you pass when searching get "sanitized"
Author: {Ben Johnson}[http://github.com/binarylogic] of {Binary Logic}[http://www.binarylogic.com]
-Credit to {Zack Ham}[http://github.com/zackham] and {Robert Malko}[http://github.com/malkomalko/] for helping with feature suggestions.
+Credit to {Zack Ham}[http://github.com/zackham] for helping with feature suggestions.
Copyright (c) 2008 {Ben Johnson}[http://github.com/binarylogic] of {Binary Logic}[http://www.binarylogic.com], released under the MIT license
View
5 vendor/plugins/searchgasm/Rakefile
@@ -10,7 +10,6 @@ Echoe.new 'searchgasm' do |p|
p.project = 'searchgasm'
p.summary = "Object based ActiveRecord searching, ordering, pagination, and more!"
p.url = "http://github.com/binarylogic/searchgasm"
- p.dependencies = ['activerecord', 'activesupport >= 2.1.0']
+ p.dependencies = %w(activerecord activesupport)
p.include_rakefile = true
-end
-
+end
View
5 vendor/plugins/searchgasm/TODO.rdoc
@@ -0,0 +1,5 @@
+= To Do
+
+1. Add month condition for date and datetime columns. So you can specify a specific month, by name, number, etc.
+2. Add day condition for time and datetime columns. So you can specify that the time is during the day or the night.
+3. Add weekend condition for date and datetime columns. So you can specify if the date is on a friday, saturday, or sunday
View
7 vendor/plugins/searchgasm/lib/searchgasm.rb
@@ -1,6 +1,7 @@
$:.unshift(File.dirname(__FILE__)) unless $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
require "active_record"
+require "active_record/version"
require "active_support"
# Core Ext
@@ -33,17 +34,17 @@
# Condition
require "searchgasm/condition/base"
require "searchgasm/condition/begins_with"
+require "searchgasm/condition/blank"
require "searchgasm/condition/contains"
require "searchgasm/condition/does_not_equal"
require "searchgasm/condition/ends_with"
require "searchgasm/condition/equals"
require "searchgasm/condition/greater_than"
require "searchgasm/condition/greater_than_or_equal_to"
-require "searchgasm/condition/is_blank"
-require "searchgasm/condition/is_nil"
require "searchgasm/condition/keywords"
require "searchgasm/condition/less_than"
require "searchgasm/condition/less_than_or_equal_to"
+require "searchgasm/condition/nil"
require "searchgasm/condition/tree"
require "searchgasm/condition/child_of"
require "searchgasm/condition/descendant_of"
@@ -77,7 +78,7 @@ class Base
include Protection
end
- [:begins_with, :child_of, :contains, :descendant_of, :does_not_equal, :ends_with, :equals, :greater_than, :greater_than_or_equal_to, :inclusive_descendant_of, :is_blank, :is_nil, :keywords, :less_than, :less_than_or_equal_to, :sibling_of].each do |condition|
+ [:begins_with, :blank, :child_of, :contains, :descendant_of, :does_not_equal, :ends_with, :equals, :greater_than, :greater_than_or_equal_to, :inclusive_descendant_of, :nil, :keywords, :less_than, :less_than_or_equal_to, :sibling_of].each do |condition|
Base.register_condition("Searchgasm::Condition::#{condition.to_s.camelize}".constantize)
end
end
View
15 vendor/plugins/searchgasm/lib/searchgasm/active_record/connection_adapters/mysql_adapter.rb
@@ -0,0 +1,15 @@
+module Searchgasm
+ module ActiveRecord
+ module ConnectionAdapters
+ module MysqlAdapter
+ def hour_sql
+ "HOUR(?)"
+ end
+
+ def month_sql
+ "MONTH(?)"
+ end
+ end
+ end
+ end
+end
View
0  vendor/plugins/searchgasm/lib/searchgasm/active_record/connection_adapters/postgresql_adapter.rb
No changes.
View
0  vendor/plugins/searchgasm/lib/searchgasm/active_record/connection_adapters/sqlite_adapter.rb
No changes.
View
36 vendor/plugins/searchgasm/lib/searchgasm/condition/base.rb
@@ -8,9 +8,8 @@ class Base
include Shared::Utilities
attr_accessor :column, :klass
- class_inheritable_accessor :ignore_blanks, :type_cast_value
- self.ignore_blanks = true
- self.type_cast_value = true
+ class_inheritable_accessor :ignore_meaningless, :type_cast_sql_type
+ self.ignore_meaningless = true
class << self
# Name of the condition inferred from the class name
@@ -29,12 +28,8 @@ def aliases_for_column(column)
[]
end
- def ignore_blanks? # :nodoc:
- ignore_blanks == true
- end
-
- def type_cast_value? # :nodoc:
- type_cast_value == true
+ def ignore_meaningless? # :nodoc:
+ ignore_meaningless == true
end
# Sane as name_for_column but for the class as a whole. For example the tree methods apply to the class as a whole and not
@@ -48,6 +43,16 @@ def aliases_for_klass(klass)
[]
end
+ # A utility method for using in name_for_column. Determines if a column contains a date.
+ def date_column?(column)
+ [:datetime, :date, :timestamp].include?(column.type)
+ end
+
+ # A utility method for using in name_for_column. Determines if a column contains a date and a time.
+ def datetime_column?(column)
+ [:datetime, :timestamp, :time, :date].include?(column.type)
+ end
+
# A utility method for using in name_for_column. For example the keywords condition only applied to string columns, the great than condition doesnt.
def string_column?(column)
[:string, :text].include?(column.type)
@@ -57,6 +62,11 @@ def string_column?(column)
def comparable_column?(column)
[:integer, :float, :decimal, :datetime, :timestamp, :time, :date].include?(column.type)
end
+
+ # A utility method for using in name_for_column. Determines if a column contains a time.
+ def time_column?(column)
+ [:datetime, :timestamp, :time].include?(column.type)
+ end
end
def initialize(klass, column = nil)
@@ -118,14 +128,20 @@ def sanitize(alt_value = nil) # :nodoc:
# The value for the condition
def value
- self.class.type_cast_value? && @value.is_a?(String) ? column.type_cast(@value) : @value
+ @value.is_a?(String) ? column_for_type_cast.type_cast(@value) : @value
end
# Sets the value for the condition
def value=(v)
+ return if self.class.ignore_meaningless? && meaningless?(v)
self.explicitly_set_value = true
@value = v
end
+
+ private
+ def column_for_type_cast
+ @column_for_type_cast ||= self.class.type_cast_sql_type ? self.column.class.new(column.name, column.default.to_s, self.class.type_cast_sql_type.to_s, column.null) : column
+ end
end
end
end
View
11 ...s/searchgasm/lib/searchgasm/condition/is_blank.rb → ...gins/searchgasm/lib/searchgasm/condition/blank.rb
@@ -1,20 +1,19 @@
module Searchgasm
module Condition
- class IsBlank < Base
- self.ignore_blanks = false
- self.type_cast_value = false
+ class Blank < Base
+ self.type_cast_sql_type = "boolean"
class << self
def aliases_for_column(column)
- ["#{column.name}_blank"]
+ ["#{column.name}_is_blank"]
end
end
def to_conditions(value)
# Some databases handle null values differently, let AR handle this
- if value == true || value == "true" || value == 1 || value == "1"
+ if value == true
"#{quoted_table_name}.#{quoted_column_name} is NULL or #{quoted_table_name}.#{quoted_column_name} = ''"
- elsif value == false || value == "false" || value == 0 || value == "0"
+ elsif value == false
"#{quoted_table_name}.#{quoted_column_name} is NOT NULL and #{quoted_table_name}.#{quoted_column_name} != ''"
end
end
View
2  vendor/plugins/searchgasm/lib/searchgasm/condition/does_not_equal.rb
@@ -1,7 +1,7 @@
module Searchgasm
module Condition
class DoesNotEqual < Base
- self.ignore_blanks = false
+ self.ignore_meaningless = false
class << self
def aliases_for_column(column)
View
32 vendor/plugins/searchgasm/lib/searchgasm/condition/during_evening.rb
@@ -0,0 +1,32 @@
+module Searchgasm
+ module Condition
+ class DuringEvening < Base
+ class << self
+ def name_for_column(column)
+ return unless time_column?(column)
+ super
+ end
+
+ def aliases_for_column(column)
+ column_names = [column.name]
+ column_names << column.name.gsub(/_(at|on)$/, "") if column.name =~ /_(at|on)$/
+
+ aliases = []
+ column_names.each { |column_name| aliases += ["#{column_name}_in_the_evening", "#{column_name}_in_evening", "#{column_name}_evening"] }
+ aliases << "#{column_names.last}_during_evening" if column_names.size > 1
+ aliases
+ end
+ end
+
+ def to_conditions(value)
+ evening_start = 17
+ evening_end = 22
+
+ # Need to set up a funcion in each adapter for dealing with dates. Mysql uses HOUR(), sqlite uses strftime(), postgres uses date_part('hour', date). Could potentially be a pain in the ass.
+ # Also, you could set up an hour = condition, and leverage that to do this.
+ if value == true
+ ["#{quoted_table_name}.#{quoted_column_name} >= ? AND #{quoted_table_name}.#{quoted_column_name} <= ?", value]
+ end
+ end
+ end
+end
View
2  vendor/plugins/searchgasm/lib/searchgasm/condition/equals.rb
@@ -1,7 +1,7 @@
module Searchgasm
module Condition
class Equals < Base
- self.ignore_blanks = false
+ self.ignore_meaningless = false
class << self
def aliases_for_column(column)
View
3  vendor/plugins/searchgasm/lib/searchgasm/condition/greater_than.rb
@@ -9,10 +9,11 @@ def name_for_column(column)
def aliases_for_column(column)
column_names = [column.name]
- column_names << column.name.gsub(/_at$/, "") if [:datetime, :timestamp, :time, :date].include?(column.type) && column.name =~ /_at$/
+ column_names << column.name.gsub(/_(at|on)$/, "") if datetime_column?(column) && column.name =~ /_(at|on)$/
aliases = []
column_names.each { |column_name| aliases += ["#{column_name}_gt", "#{column_name}_after"] }
+ aliases << "#{column_names.last}_greater_than" if column_names.size > 1
aliases
end
end
View
8 vendor/plugins/searchgasm/lib/searchgasm/condition/greater_than_or_equal_to.rb
@@ -8,7 +8,13 @@ def name_for_column(column)
end
def aliases_for_column(column)
- ["#{column.name}_gte", "#{column.name}_at_least"]
+ column_names = [column.name]
+ column_names << column.name.gsub(/_(at|on)$/, "") if datetime_column?(column) && column.name =~ /_(at|on)$/
+
+ aliases = []
+ column_names.each { |column_name| aliases += ["#{column_name}_gte", "#{column_name}_at_least"] }
+ aliases << "#{column_names.last}_greater_than_or_equal_to" if column_names.size > 1
+ aliases
end
end
View
23 vendor/plugins/searchgasm/lib/searchgasm/condition/is_nil.rb
@@ -1,23 +0,0 @@
-module Searchgasm
- module Condition
- class IsNil < Base
- self.ignore_blanks = false
- self.type_cast_value = false
-
- class << self
- def aliases_for_column(column)
- ["#{column.name}_nil", "#{column.name}_is_null", "#{column.name}_null"]
- end
- end
-
- def to_conditions(value)
- # Some databases handle null values differently, let AR handle this
- if value == true || value == "true" || value == 1 || value == "1"
- "#{quoted_table_name}.#{quoted_column_name} is NULL"
- elsif value == false || value == "false" || value == 0 || value == "0"
- "#{quoted_table_name}.#{quoted_column_name} is NOT NULL"
- end
- end
- end
- end
-end
View
3  vendor/plugins/searchgasm/lib/searchgasm/condition/less_than.rb
@@ -9,10 +9,11 @@ def name_for_column(column)
def aliases_for_column(column)
column_names = [column.name]
- column_names << column.name.gsub(/_at$/, "") if [:datetime, :timestamp, :time, :date].include?(column.type) && column.name =~ /_at$/
+ column_names << column.name.gsub(/_(at|on)$/, "") if datetime_column?(column) && column.name =~ /_(at|on)$/
aliases = []
column_names.each { |column_name| aliases += ["#{column_name}_lt", "#{column_name}_before"] }
+ aliases << "#{column_names.last}_less_than" if column_names.size > 1
aliases
end
end
View
8 vendor/plugins/searchgasm/lib/searchgasm/condition/less_than_or_equal_to.rb
@@ -8,7 +8,13 @@ def name_for_column(column)
end
def aliases_for_column(column)
- ["#{column.name}_lte", "#{column.name}_at_most"]
+ column_names = [column.name]
+ column_names << column.name.gsub(/_(at|on)$/, "") if datetime_column?(column) && column.name =~ /_(at|on)$/
+
+ aliases = []
+ column_names.each { |column_name| aliases += ["#{column_name}_lte", "#{column_name}_at_most"] }
+ aliases << "#{column_names.last}_less_than_or_equal_to" if column_names.size > 1
+ aliases
end
end
View
21 vendor/plugins/searchgasm/lib/searchgasm/condition/nil.rb
@@ -0,0 +1,21 @@
+module Searchgasm
+ module Condition
+ class Nil < Base
+ self.type_cast_sql_type = "boolean"
+
+ class << self
+ def aliases_for_column(column)
+ ["#{column.name}_is_nil", "#{column.name}_is_null", "#{column.name}_null"]
+ end
+ end
+
+ def to_conditions(value)
+ if value == true
+ "#{quoted_table_name}.#{quoted_column_name} is NULL"
+ elsif value == false
+ "#{quoted_table_name}.#{quoted_column_name} is NOT NULL"
+ end
+ end
+ end
+ end
+end
View
9 vendor/plugins/searchgasm/lib/searchgasm/conditions/base.rb
@@ -68,8 +68,11 @@ def needed?(model_class, conditions) # :nodoc:
if conditions.is_a?(Hash)
return true if conditions[:any]
+ stringified_conditions = conditions.stringify_keys
+ stringified_conditions.keys.each { |condition| return false if condition.include?(".") } # setting conditions on associations, which is just another way of writing SQL, and we ignore SQL
+
column_names = model_class.column_names
- conditions.stringify_keys.keys.each do |condition|
+ stringified_conditions.keys.each do |condition|
return true unless column_names.include?(condition)
end
end
@@ -132,7 +135,7 @@ def conditions=(value)
when Hash
assert_valid_conditions(value)
remove_conditions_from_protected_assignement(value).each do |condition, condition_value|
- next if condition_value.blank? # ignore blanks on mass assignments
+ next if meaningless?(condition_value) # ignore blanks on mass assignments
send("#{condition}=", condition_value)
end
else
@@ -216,7 +219,7 @@ def #{name}_object
def #{name}; #{name}_object.value; end
def #{name}=(value)
- if value.blank? && #{name}_object.class.ignore_blanks?
+ if meaningless?(value) && #{name}_object.class.ignore_meaningless?
reset_#{name}!
else
@conditions = nil
View
26 vendor/plugins/searchgasm/lib/searchgasm/config.rb
@@ -44,7 +44,7 @@ def desc_indicator=(value)
end
def hidden_fields # :nodoc:
- @hidden_fields ||= (Search::Base::SPECIAL_FIND_OPTIONS - [:page])
+ @hidden_fields ||= (Search::Base::SPECIAL_FIND_OPTIONS - [:page, :priority_order])
end
# Which hidden fields to automatically include when creating a form with a Searchgasm object. See Searchgasm::Helpers::Form for more info.
@@ -135,7 +135,7 @@ def per_page # :nodoc:
# The reason for this not to disturb regular queries such as Whatever.find(:all). You would not expect that to be limited.
#
# * <tt>Default:</tt> The 3rd option in your per_page_choices, default of 50
- # * <tt>Accepts:</tt> Any value in your per_page choices, nil means "show all"
+ # * <tt>Accepts:</tt> Any value in your per_page choices, nil or a blank string means "show all"
def per_page=(value)
@per_page = value
end
@@ -153,6 +153,28 @@ def per_page_choices # :nodoc:
def per_page_choices=(value)
@per_page_choices = value
end
+
+ def remove_duplicates # :nodoc:
+ return @remove_duplicates if @set_remove_duplicates
+ @remove_duplicates ||= ::ActiveRecord::VERSION::MAJOR < 2 || (::ActiveRecord::VERSION::MAJOR == 2 && ::ActiveRecord::VERSION::MINOR < 2)
+ end
+
+ def remove_duplicates? # :nodoc:
+ remove_duplicates == true
+ end
+
+ # If you are using ActiveRecord < 2.2.0 then ActiveRecord does not remove duplicates when using the :joins option, when it should. To fix this problem searchgasm does this for you. Searchgasm tries to act
+ # just like ActiveRecord, but in this instance it doesn't make sense.
+ #
+ # As a result, Searchgasm removes all duplicates results in *ALL* search / calculation queries. It does this by forcing the DISTINCT or GROUP BY operation in your SQL. Which might come as a surprise to you
+ # since it is not the "norm". If you don't want searchgasm to do this, set this to false.
+ #
+ # * <tt>Default:</tt> true
+ # * <tt>Accepts:</tt> Boolean
+ def remove_duplicates=(value)
+ @set_remove_duplicates = true
+ @remove_duplicates = value
+ end
end
end
end
View
40 vendor/plugins/searchgasm/lib/searchgasm/core_ext/hash.rb
@@ -16,19 +16,53 @@ def deep_dup
new_hash
end
- def deep_delete_duplicates(hash)
+ def deep_delete_duplicate_keys(hash)
hash.each do |k, v|
if v.is_a?(Hash) && self[k]
self[k].deep_delete_duplicates(v)
- self.delete(k) if self[k].blank?
+ delete(k) if self[k].blank?
else
- self.delete(k)
+ delete(k)
end
end
self
end
+ def deep_delete(value)
+ case value
+ when Array
+ value.each { |v| deep_delete(v) }
+ when Hash
+ value.each do |k, v|
+ next unless self[k].is_a?(Hash)
+
+ case v
+ when Hash, Array
+ self[k].deep_delete(v)
+ when String, Symbol
+ self[k].delete(v)
+ end
+ end
+ when String, Symbol
+ delete(value)
+ end
+ end
+
+ def deep_merge(other_hash)
+ self.merge(other_hash) do |key, oldval, newval|
+ oldval = oldval.to_hash if oldval.respond_to?(:to_hash)
+ newval = newval.to_hash if newval.respond_to?(:to_hash)
+ oldval.class.to_s == 'Hash' && newval.class.to_s == 'Hash' ? oldval.deep_merge(newval) : newval
+ end
+ end
+
+ # Returns a new hash with +self+ and +other_hash+ merged recursively.
+ # Modifies the receiver in place.
+ def deep_merge!(other_hash)
+ replace(deep_merge(other_hash))
+ end
+
# assert_valid_keys was killing performance. Array.flatten was the culprit, so I rewrote this method, got a 35% performance increase
def fast_assert_valid_keys(valid_keys)
unknown_keys = keys - valid_keys
View
105 vendor/plugins/searchgasm/lib/searchgasm/helpers/control_types/link.rb
@@ -80,11 +80,13 @@ module Link
# === Advanced Options
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
- # * <tt>:url_params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
+ # * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
+ # * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
def order_by_link(order_by, options = {})
- order_by = deep_stringify(order_by)
add_order_by_link_defaults!(order_by, options)
- html = searchgasm_state_for(:order_by, options) + searchgasm_state_for(:order_as, options)
+ html = searchgasm_state(options)
if !options[:is_remote]
html += link_to(options[:text], options[:url], options[:html])
@@ -110,10 +112,54 @@ def order_by_link(order_by, options = {})
# === Advanced Options
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
- # * <tt>:url_params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
+ # * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
+ # * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
def order_as_link(order_as, options = {})
add_order_as_link_defaults!(order_as, options)
- html = searchgasm_state_for(:order_as, options)
+ html = searchgasm_state(options)
+
+ if !options[:is_remote]
+ html += link_to(options[:text], options[:url], options[:html])
+ else
+ html += link_to_remote(options[:text], options[:remote].merge(:url => options[:url]), options[:html])
+ end
+
+ html
+ end
+
+ # This is similar to order_by_link but with a small difference. The best way to explain priority ordering is with an example. Let's say you wanted to list products on a page. You have "featured" products
+ # that you want to show up first, no matter what. This is what this is all about. It makes ordering by featured products a priority, then searching by price, quantity, etc. is the same as it has always been.
+ #
+ # The difference between order_by_link and priority_order_by_link is that priority_order_by_link it just a switch. Turn it on or turn it off. You don't neccessarily want to flip between ASC and DESC. If you do
+ # then you should just incorporate this into your regular order_by, like: order_by_link [:featured, :price]
+ #
+ # === Example uses for a User class that has many orders
+ #
+ # priority_order_by_link(:featured, "DESC")
+ # order_by_link([:featured, :created_at], "ASC")
+ # order_by_link({:orders => :featured}, "ASC")
+ # order_by_link([{:orders => :featured}, :featured], "ASC")
+ # order_by_link(:featured, "ASC", :text => "Featured", :html => {:class => "featured_link"})
+ #
+ # === Options
+ # * <tt>:activate_text</tt> -- default: "Show #{column_name.to_s.humanize} first"
+ # * <tt>:deactivate_text</tt> -- default: "Don't show #{column_name.to_s.humanize} first", text for the link, text for the link
+ # * <tt>:column_name</tt> -- default: column_name.to_s.humanize, automatically inferred by what you are ordering by and is added into the active_text and deactive_text strings.
+ # * <tt>:text</tt> -- default: :activate_text or :deactivate_text depending on if its active or not, Overwriting this will make this text stay the same, no matter way. A good alternative would be "Toggle featured first"
+ # * <tt>:html</tt> -- html arrtributes for the <a> tag.
+ #
+ # === Advanced Options
+ # * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
+ # * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
+ # * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
+ # * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
+ # * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
+ def priority_order_by_link(priority_order_by, priority_order_as, options = {})
+ add_priority_order_by_link_defaults!(priority_order_by, priority_order_as, options)
+ html = searchgasm_state(options)
if !options[:is_remote]
html += link_to(options[:text], options[:url], options[:html])
@@ -141,10 +187,13 @@ def order_as_link(order_as, options = {})
# === Advanced Options
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
- # * <tt>:url_params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
+ # * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
+ # * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
def per_page_link(per_page, options = {})
add_per_page_link_defaults!(per_page, options)
- html = searchgasm_state_for(:per_page, options)
+ html = searchgasm_state(options)
if !options[:is_remote]
html += link_to(options[:text], options[:url], options[:html])
@@ -170,10 +219,13 @@ def per_page_link(per_page, options = {})
# === Advanced Options
# * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
# * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
- # * <tt>:url_params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
+ # * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
+ # * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
def page_link(page, options = {})
add_page_link_defaults!(page, options)
- html = searchgasm_state_for(:page, options)
+ html = searchgasm_state(options)
if !options[:is_remote]
html += link_to(options[:text], options[:url], options[:html])
@@ -187,10 +239,12 @@ def page_link(page, options = {})
private
def add_order_by_link_defaults!(order_by, options = {})
add_searchgasm_control_defaults!(:order_by, options)
+ ordering_by_this = searchgasm_ordering_by?(order_by, options)
+ searchgasm_add_class!(options[:html], "ordering") if ordering_by_this
options[:text] ||= determine_order_by_text(order_by)
options[:asc_indicator] ||= Config.asc_indicator
options[:desc_indicator] ||= Config.desc_indicator
- options[:text] += options[:search_obj].desc? ? options[:desc_indicator] : options[:asc_indicator] if options[:search_obj].order_by == order_by
+ options[:text] += options[:search_obj].desc? ? options[:desc_indicator] : options[:asc_indicator] if ordering_by_this
options[:url] = searchgasm_params(options.merge(:search_params => {:order_by => order_by}))
options
end
@@ -202,6 +256,22 @@ def add_order_as_link_defaults!(order_as, options = {})
options
end
+ def add_priority_order_by_link_defaults!(priority_order_by, priority_order_as, options = {})
+ add_searchgasm_control_defaults!(:priority_order_by, options)
+ options[:column_name] ||= determine_order_by_text(priority_order_by).downcase
+ options[:activate_text] ||= "Show #{options[:column_name]} first"
+ options[:deactivate_text] ||= "Don't show #{options[:column_name]} first"
+ active = deep_stringify(options[:search_obj].priority_order_by) == deep_stringify(priority_order_by) && options[:search_obj].priority_order_as == priority_order_as
+ options[:text] ||= active ? options[:deactivate_text] : options[:activate_text]
+ if active
+ options.merge!(:search_params => {:priority_order_by => nil, :priority_order_as => nil})
+ else
+ options.merge!(:search_params => {:priority_order_by => priority_order_by, :priority_order_as => priority_order_as})
+ end
+ options[:url] = searchgasm_params(options)
+ options
+ end
+
def add_per_page_link_defaults!(per_page, options = {})
add_searchgasm_control_defaults!(:per_page, options)
options[:text] ||= per_page.blank? ? "Show all" : "#{per_page} per page"
@@ -228,21 +298,6 @@ def determine_order_by_text(column_name, relationship_name = nil)
determine_order_by_text(v, k)
end
end
-
- def deep_stringify(obj)
- case obj
- when String
- obj
- when Symbol
- obj = obj.to_s
- when Array
- obj = obj.collect { |item| deep_stringify(item) }
- when Hash
- new_obj = {}
- obj.each { |key, value| new_obj[key.to_s] = deep_stringify(value) }
- new_obj
- end
- end
end
end
end
View
2  vendor/plugins/searchgasm/lib/searchgasm/helpers/control_types/remote_select.rb
@@ -23,7 +23,7 @@ def remote_per_page_select(options = {})
per_page_select(options)
end
- # Please see page_links. All options are the same and applicable here, excep the :prev, :next, :first, and :last options. The only difference is that instead of a group of links, this gets returned as a select form element that will perform the same function when the value is changed.
+ # Please see page_links. All options are the same and applicable here, except the :prev, :next, :first, and :last options. The only difference is that instead of a group of links, this gets returned as a select form element that will perform the same function when the value is changed.
def remote_page_select(options = {})
add_remote_defaults!(options)
page_select(options)
View
8 vendor/plugins/searchgasm/lib/searchgasm/helpers/control_types/select.rb
@@ -8,25 +8,25 @@ module Select
# Please see order_by_links. All options are the same and applicable here. The only difference is that instead of a group of links, this gets returned as a select form element that will perform the same function when the value is changed.
def order_by_select(options = {})
add_order_by_select_defaults!(options)
- searchgasm_state_for(:order_by, options) + select(options[:params_scope], :order_by, options[:choices], options[:tag], options[:html] || {})
+ searchgasm_state(options) + select(options[:params_scope], :order_by, options[:choices], options[:tag], options[:html] || {})
end
# Please see order_as_links. All options are the same and applicable here. The only difference is that instead of a group of links, this gets returned as a select form element that will perform the same function when the value is changed.
def order_as_select(options = {})
add_order_as_select_defaults!(options)
- searchgasm_state_for(:order_as, options) + select(options[:params_scope], :order_as, options[:choices], options[:tag], options[:html])
+ searchgasm_state(options) + select(options[:params_scope], :order_as, options[:choices], options[:tag], options[:html])
end
# Please see per_page_links. All options are the same and applicable here. The only difference is that instead of a group of links, this gets returned as a select form element that will perform the same function when the value is changed.
def per_page_select(options = {})
add_per_page_select_defaults!(options)
- searchgasm_state_for(:per_page, options) + select(options[:params_scope], :per_page, options[:choices], options[:tag], options[:html])
+ searchgasm_state(options) + select(options[:params_scope], :per_page, options[:choices], options[:tag], options[:html])
end
# Please see page_links. All options are the same and applicable here, excep the :prev, :next, :first, and :last options. The only difference is that instead of a group of links, this gets returned as a select form element that will perform the same function when the value is changed.
def page_select(options = {})
add_page_select_defaults!(options)
- searchgasm_state_for(:page, options) + select(options[:params_scope], :page, (options[:first_page]..options[:last_page]), options[:tag], options[:html])
+ searchgasm_state(options) + select(options[:params_scope], :page, (options[:first_page]..options[:last_page]), options[:tag], options[:html])
end
private
View
2  vendor/plugins/searchgasm/lib/searchgasm/helpers/form.rb
@@ -122,7 +122,7 @@ def insert_searchgasm_fields(args, search_object, search_options, &block)
options = args.extract_options!
options
search_options[:hidden_fields].each do |field|
- html = hidden_field(name, field, :object => search_object, :id => "#{name}_#{field}_hidden", :value => (field == :order_by ? searchgasm_order_by_value(search_object.order_by) : search_object.send(field)))
+ html = hidden_field(name, field, :object => search_object, :id => "#{name}_#{field}_hidden", :value => (field == :order_by ? searchgasm_base64_value(search_object.order_by) : search_object.send(field)))
# For edge rails and older version compatibility, passing a binding to concat was deprecated
begin
View
116 vendor/plugins/searchgasm/lib/searchgasm/helpers/utilities.rb
@@ -1,11 +1,31 @@
module Searchgasm
module Helpers #:nodoc:
module Utilities # :nodoc:
- # Builds a hash of params for creating a url.
+ # Builds a hash of params for creating a url and preserves any existing params. You can pass this into url_for and build your url. Although most rails helpers accept a hash.
+ #
+ # Let's take the page_link helper. Here is the code behind that helper:
+ #
+ # link_to("Page 2", searchgasm_params(:search_params => {:page => 2}))
+ #
+ # That's pretty much it. So if you wanted to roll your own link to execute a search, go for it. It's pretty simple. Pass conditions instead of the page, set how the search will be ordered, etc.
+ #
+ # <b>Be careful</b> when taking this approach though. Searchgasm helps you out when you use form_for. For example, when you use the per_page_select helper, it adds in a hidden form field with the value of the page. So when
+ # your search form is submitted it searches the document for that element, finds the current value, which is the current per_page value, and includes that in the search. So when a user searches the per_page
+ # value stays consistent. If you use the searchgasm_params you are on your own. I am always curious how people are using searchgasm. So if you are building your own helpers contact me and maybe I can help you
+ # and add in a helper for you, making it an *official* feature.
+ #
+ # === Options
+ # * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
+ # * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
+ # * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
+ # * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
+ # * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
def searchgasm_params(options = {})
add_searchgasm_defaults!(options)
options[:search_params] ||= {}
options[:literal_search_params] ||= {}
+
options[:params] ||= {}
params_copy = params.deep_dup.with_indifferent_access
search_params = options[:params_scope].blank? ? params_copy : params_copy.delete(options[:params_scope])
@@ -13,18 +33,24 @@ def searchgasm_params(options = {})
search_params = search_params.with_indifferent_access
search_params.delete(:commit)
search_params.delete(:page)
- search_params.deep_delete_duplicates(options[:literal_search_params])
+ search_params.deep_delete_duplicate_keys(options[:literal_search_params])
+ search_params.deep_delete(options[:exclude_search_params])
if options[:search_params]
+
+ #raise params_copy.inspect if options[:search_params][:order_by] == :id
search_params.deep_merge!(options[:search_params])
if options[:search_params][:order_by] && !options[:search_params][:order_as]
- search_params[:order_as] = (options[:search_obj].order_by == options[:search_params][:order_by] && options[:search_obj].asc?) ? "DESC" : "ASC"
+ search_params[:order_as] = (searchgasm_ordering_by?(options[:search_params][:order_by], options) && options[:search_obj].asc?) ? "DESC" : "ASC"
end
+
+ [:order_by, :priority_order_by].each { |base64_field| search_params[base64_field] = searchgasm_base64_value(search_params[base64_field]) if search_params.has_key?(base64_field) }
end
new_params = params_copy
new_params.deep_merge!(options[:params])
+ new_params.deep_delete(options[:exclude_params])
if options[:params_scope].blank? || search_params.blank?
new_params
@@ -33,6 +59,32 @@ def searchgasm_params(options = {})
end
end
+ # Similar to searchgasm_hash, but instead returns a string url. The reason this exists is to assist in creating urls in javascript. It's the muscle behind all of the select helpers that searchgasm provides.
+ # Take the instance where you want to do:
+ #
+ # :onchange => "window.location = '#{url_for(searchgasm_params)}&my_param=' + this.value;"
+ #
+ # Well the above obviously won't work. Do you need to apped the url with a ? or a &? What about that tricky :params_scope? That's where this is handy, beacuse it does all of the params string building for you. Check it out:
+ #
+ # :onchange => "window.location = '" + searchgasm_url(:literal_search_params => {:per_page => "' + escape(this.value) + '"}) + "';"
+ #
+ # or what about something a little more tricky?
+ #
+ # :onchange => "window.location = '" + searchgasm_url(:literal_search_params => {:conditions => {:name_contains => "' + escape(this.value) + '"}}) + "';"
+ #
+ # I have personally used this for an event calendar. Above the calendar there was a drop down for each month. Here is the code:
+ #
+ # :onchange => "window.location = '" + searchgasm_url(:literal_search_params => {:conditions => {:occurs_at_after => "' + escape(this.value) + '"}}) + "';"
+ #
+ # Now when the user changes the month in the drop down it just runs a new search that sets my conditions to occurs_at_after = selected month. Then in my controller I set occurs_at_before = occurs_at_after.at_end_of_month.
+ #
+ # === Options
+ # * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
+ # * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
+ # * <tt>:params</tt> -- default: nil, Additional params to add to the url, must be a hash
+ # * <tt>:exclude_params</tt> -- default: nil, params you want to exclude. This is nifty because it does a "deep delete". So you can pass {:param1 => {:param2 => :param3}} and it will make sure param3 does not get include. param1 and param2 will not be touched. This also accepts an array or just a symbol or string.
+ # * <tt>:search_params</tt> -- default: nil, Additional search params to add to the url, must be a hash. Adds the options into the :params_scope.
+ # * <tt>:exclude_search_params</tt> -- default: nil, Same as :exclude_params but for the :search_params.
def searchgasm_url(options = {})
search_params = searchgasm_params(options)
url = url_for(search_params)
@@ -40,6 +92,30 @@ def searchgasm_url(options = {})
url += (url.last == "?" ? "" : (url.include?("?") ? "&amp;" : "?")) + literal_param_strings.join("&amp;")
end
+ # When you set up a search form using form_for for remote_form_for searchgasm adds in some *magic* for you.
+ #
+ # Take the instance where a user orders the data by something other than the default, and then does a search. The user would expect the search to remember what the user selected to order the data by, right?
+ # What searchgasm does is add in some hidden fields, somewhere in the page, the represent the searchgasm "state". These are automatically added for you when you use the searchgasm helpers.
+ # Such as: page_links, page_link, order_by_link, per_page_select, etc. So if you are using those you do not need to worry about this helper.
+ #
+ # If for some reason you do not use any of these you need to put the searchgasm state on your page somewhere. Somewhere where the state will *always* be up-to-date, which would be most likely be in the
+ # partial that renders your search results (assuming you are using AJAX). Otherwise when the user starts a new search, the state will be reset. Meaning the order_by, per_page, etc will all be reset.
+ #
+ # === Options
+ # * <tt>:params_scope</tt> -- default: :search, this is the scope in which your search params will be preserved (params[:search]). If you don't want a scope and want your options to be at base leve in params such as params[:page], params[:per_page], etc, then set this to nil.
+ # * <tt>:search_obj</tt> -- default: @#{params_scope}, this is your search object, everything revolves around this. It will try to infer the name from your params_scope. If your params_scope is :search it will try to get @search, etc. If it can not be inferred by this, you need to pass the object itself.
+ def searchgasm_state(options)
+ return "" if @added_searchgasm_state
+ add_searchgasm_defaults!(options)
+ html = ""
+ (Search::Base::SPECIAL_FIND_OPTIONS - [:page, :priority_order]).each do |option|
+ value = options[:search_obj].send(option)
+ html += hidden_field(options[:params_scope], option, :value => (option == :order_by ? searchgasm_base64_value(value) : value))
+ end
+ @added_searchgasm_state = true
+ html
+ end
+
private
# Adds default options for all helper methods.
def add_searchgasm_defaults!(options)
@@ -65,24 +141,19 @@ def searchgasm_add_class!(html_options, new_class)
html_options[:class] = classes.join(" ")
end
- def searchgasm_order_by_value(order_by)
+ def searchgasm_base64_value(order_by)
case order_by
- when String
+ when String, Symbol
order_by
when Array, Hash
[Marshal.dump(order_by)].pack("m")
end
end
- def searchgasm_state_for(option, options)
- @added_state_for ||= []
- html = ""
- unless @added_state_for.include?(option)
- value = options[:search_obj].send(option)
- html = hidden_field(options[:params_scope], option, :value => (option == :order_by ? searchgasm_order_by_value(value) : value))
- @added_state_for << option
- end
- html
+ def searchgasm_ordering_by?(order_by, options)
+ stringified_search_order_by = deep_stringify(options[:search_obj].order_by)
+ stringified_order_by = deep_stringify(order_by)
+ (options[:search_obj].order_by.blank? && options[:search_obj].klass.primary_key == stringified_order_by) || stringified_search_order_by == stringified_order_by
end
def literal_param_strings(literal_params, prefix)
@@ -102,6 +173,23 @@ def literal_param_strings(literal_params, prefix)
param_strings
end
+
+ def deep_stringify(obj)
+ case obj
+ when String
+ obj
+ when Symbol
+ obj.to_s
+ when Array
+ obj.collect { |item| deep_stringify(item) }
+ when Hash
+ new_obj = {}
+ obj.each { |key, value| new_obj[key.to_s] = deep_stringify(value) }
+ new_obj
+ else
+ obj
+ end
+ end
end
end
end
View
28 vendor/plugins/searchgasm/lib/searchgasm/search/base.rb
@@ -16,7 +16,7 @@ class Base
AR_OPTIONS = (AR_FIND_OPTIONS + AR_CALCULATIONS_OPTIONS).uniq
# Options that ActiveRecord doesn't suppport, but Searchgasm does
- SPECIAL_FIND_OPTIONS = [:order_by, :order_as, :page, :per_page]
+ SPECIAL_FIND_OPTIONS = [:order_by, :order_as, :page, :per_page, :priority_order, :priority_order_by, :priority_order_as]
# Valid options you can use when searching
OPTIONS = SPECIAL_FIND_OPTIONS + AR_OPTIONS # the order is very important, these options get set in this order
@@ -100,13 +100,7 @@ def offset=(value)
def options=(values)
return unless values.is_a?(Hash)
values.symbolize_keys.fast_assert_valid_keys(OPTIONS)
-
- OPTIONS.each do |option|
- next unless values.has_key?(option)
- send("#{option}=", values[option])
- end
-
- values
+ values.each { |key, value| send("#{key}=", value) }
end
# Sanitizes everything down into options ActiveRecord::Base.find can understand
@@ -118,16 +112,18 @@ def sanitize(searching = true)
next if value.blank?
find_options[find_option] = value
end
-
- unless find_options[:joins].blank?
- # The following is to return uniq records since we are using joins instead of includes
- if searching
- find_options[:group] ||= "#{quote_table_name(klass.table_name)}.#{quote_column_name(klass.primary_key)}"
- else
- find_options[:distinct] = true
+
+ if Config.remove_duplicates?
+ unless find_options[:joins].blank?
+ # The following is to return uniq records since we are using joins instead of includes
+ if searching
+ find_options[:group] ||= "#{quote_table_name(klass.table_name)}.#{quote_column_name(klass.primary_key)}"
+ else
+ find_options[:distinct] = true
+ end
end
end
-
+
find_options
end
View
183 vendor/plugins/searchgasm/lib/searchgasm/search/ordering.rb
@@ -20,17 +20,19 @@ def self.included(klass)
klass.class_eval do
alias_method_chain :auto_joins, :ordering
alias_method_chain :order=, :ordering
+ alias_method_chain :sanitize, :ordering
+ attr_reader :priority_order
end
end
def auto_joins_with_ordering # :nodoc:
- @memoized_auto_joins ||= merge_joins(auto_joins_without_ordering, order_by_auto_joins)
+ @memoized_auto_joins ||= merge_joins(auto_joins_without_ordering, order_by_auto_joins, priority_order_by_auto_joins)
end
def order_with_ordering=(value) # :nodoc
@order_by = nil
@order_as = nil
- self.order_by_auto_joins.clear
+ @order_by_auto_joins = nil
@memoized_auto_joins = nil
self.order_without_ordering = value
end
@@ -42,53 +44,42 @@ def asc?
# Convenience method for determining if the ordering is descending
def desc?
+ return false if order_as.nil?
order_as == "DESC"
end
# Determines how the search is being ordered: as DESC or ASC
def order_as
- @order_as ||= (order.blank? || order =~ /ASC$/i) ? "ASC" : "DESC"
+ return if order.blank?
+ return @order_as if @order_as
+
+ case order
+ when /ASC$/i
+ @order_as = "ASC"
+ when /DESC$/i
+ @order_as = "DESC"
+ else
+ nil
+ end
end
# Sets how the results will be ordered: ASC or DESC
def order_as=(value)
- value = value.to_s.upcase
- raise(ArgumentError, "order_as only accepts a string as ASC or DESC") unless ["ASC", "DESC"].include?(value)
-
- if order.blank?
- @order = order_by_to_order(order_by, value)
- else
+ value = value.blank? ? nil : value.to_s.upcase
+ raise(ArgumentError, "order_as only accepts a blank string / nil or a string as 'ASC' or 'DESC'") if !value.blank? && !["ASC", "DESC"].include?(value)
+ if @order_by
+ @order = order_by_to_order(@order_by, value)
+ elsif order
@order.gsub!(/(ASC|DESC)/i, value)
end
-
@order_as = value
end
# Determines by what columns the search is being ordered. This is nifty in that is reverse engineers the order SQL to determine this, only
# if you haven't explicitly set the order_by option yourself.
def order_by
- return @order_by if @order_by
-
- if !order.blank?
- # Reversege engineer order, only go 1 level deep with relationships, anything beyond that is probably excessive and not good for performance
- order_parts = order.split(",").collect do |part|
- part.strip!
- part.gsub!(/ (ASC|DESC)$/i, "").gsub!(/(.*)\./, "")
- table_name = ($1 ? $1.gsub(/[^a-z0-9_]/i, "") : nil)
- part.gsub!(/[^a-z0-9_]/i, "")
- reflection = nil
- if table_name && table_name != klass.table_name
- reflection = klass.reflect_on_association(table_name.to_sym) || klass.reflect_on_association(table_name.singularize.to_sym)
- next unless reflection
- {reflection.name.to_s => part}
- else
- part
- end
- end.compact
- @order_by = order_parts.size <= 1 ? order_parts.first : order_parts
- else
- @order_by = klass.primary_key
- end
+ return if order.blank?
+ @order_by ||= order_to_order_by(order)
end
# Lets you set how to order the data
@@ -101,23 +92,86 @@ 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_auto_joins.clear
+ @order_by_auto_joins = nil
@memoized_auto_joins = nil
@order_by = get_order_by_value(value)
- @order = order_by_to_order(@order_by, order_as)
+ @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_auto_joins
- @order_by_auto_joins ||= []
- @order_by_auto_joins.compact!
- @order_by_auto_joins.uniq!
- @order_by_auto_joins
+ @order_by_auto_joins ||= build_order_by_auto_joins(order_by)
+ end
+
+ # Let's you set a priority order. Meaning this will get ordered first before anything else, but is unnoticeable and abstracted out from your regular order. For example, lets say you have a model called Product
+ # that had a "featured" boolean column. You want to order the products by the price, quantity, etc., but you want the featured products to always be first.
+ #
+ # Without a priority order your controller would get cluttered and your code would be much more complicated. All of your order_by_link methods would have to be order_by_link [:featured, :price], :text => "Price"
+ # Your order_by_link methods alternate between ASC and DESC, so the featured products would jump from the top the bottom. It presents a lot of "work arounds". So priority_order solves this.
+ def priority_order=(value)
+ @priority_order = value
+ end
+
+ # Same as order_by but for your priority order. See priority_order= for more informaton on priority_order.
+ def priority_order_by
+ return if priority_order.blank?
+ @priority_order_by ||= order_to_order_by(priority_order)
+ end
+
+ # Same as order_by= but for your priority order. See priority_order= for more informaton on priority_order.
+ def priority_order_by=(value)
+ @priority_order_by_auto_joins = nil
+ @memoized_auto_joins = nil
+ @priority_order_by = get_order_by_value(value)
+ @priority_order = order_by_to_order(@priority_order_by, @priority_order_as)
+ @priority_order_by
+ end
+
+ # Same as order_as but for your priority order. See priority_order= for more informaton on priority_order.
+ def priority_order_as
+ return if priority_order.blank?
+ return @priority_order_as if @priority_order_as
+
+ case priority_order
+ when /ASC$/i
+ @priority_order_as = "ASC"
+ when /DESC$/i
+ @priority_order_as = "DESC"
+ else
+ nil
+ end
+ end
+
+ # Same as order_as= but for your priority order. See priority_order= for more informaton on priority_order.
+ def priority_order_as=(value)
+ value = value.blank? ? nil : value.to_s.upcase
+ raise(ArgumentError, "priority_order_as only accepts a blank string / nil or a string as 'ASC' or 'DESC'") if !value.blank? && !["ASC", "DESC"].include?(value)
+ if @priority_order_by
+ @priority_order = order_by_to_order(@priority_order_by, value)
+ elsif priority_order
+ @priority_order.gsub!(/(ASC|DESC)/i, value)
+ end
+ @priority_order_as = value
+ end
+
+ def priority_order_by_auto_joins
+ @priority_order_by_auto_joins ||= build_order_by_auto_joins(priority_order_by)
+ end
+
+ def sanitize_with_ordering(searching = true)
+ find_options = sanitize_without_ordering(searching)
+ unless priority_order.blank?
+ order_parts = [priority_order, find_options[:order]].compact
+ find_options[:order] = order_parts.join(", ")
+ end
+ find_options
end
private
- def order_by_to_order(order_by, order_as, alt_klass = nil, new_joins = [])
+ def order_by_to_order(order_by, order_as, alt_klass = nil)
+ return if order_by.blank?
+
k = alt_klass || klass
table_name = k.table_name
sql_parts = []
@@ -130,23 +184,52 @@ def order_by_to_order(order_by, order_as, alt_klass = nil, new_joins = [])
key = order_by.keys.first
reflection = k.reflect_on_association(key.to_sym)
value = order_by.values.first
- new_joins << key.to_sym
- sql_parts << order_by_to_order(value, order_as, reflection.klass, new_joins)
+ sql_parts << order_by_to_order(value, order_as, reflection.klass)
when Symbol, String
- 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}"
+ part = "#{quote_table_name(table_name)}.#{quote_column_name(order_by)}"
+ part += " #{order_as}" unless order_as.blank?
+ sql_parts << part
end
sql_parts.join(", ")
end
- def build_order_by_auto_joins(joins)
- return joins.first if joins.size <= 1
- joins = joins.dup
-
- key = joins.shift
- {key => build_order_by_auto_joins(joins)}
+ def order_to_order_by(order)
+ # Reversege engineer order, only go 1 level deep with relationships, anything beyond that is probably excessive and not good for performance
+ order_parts = order.split(",").collect do |part|
+ part.strip!
+ part.gsub!(/ (ASC|DESC)$/i, "")
+ part.gsub!(/(.*)\./, "")
+ table_name = ($1 ? $1.gsub(/[^a-z0-9_]/i, "") : nil)
+ part.gsub!(/[^a-z0-9_]/i, "")
+ reflection = nil
+ if table_name && table_name != klass.table_name
+ reflection = klass.reflect_on_association(table_name.to_sym) || klass.reflect_on_association(table_name.singularize.to_sym)
+ next unless reflection
+ {reflection.name.to_s => part}
+ else
+ part
+ end
+ end.compact
+ order_parts.size <= 1 ? order_parts.first : order_parts
+ end
+
+ def build_order_by_auto_joins(order_by_value)
+ case order_by_value
+ when Array
+ order_by_value.collect { |value| build_order_by_auto_joins(value) }.uniq.compact
+ when Hash
+ key = order_by_value.keys.first
+ value = order_by_value.values.first
+ case value
+ when Hash
+ {key.to_sym => build_order_by_auto_joins(value)}
+ else
+ key.to_sym
+ end
+ else
+ nil
+ end
end
def get_order_by_value(value)
View
6 vendor/plugins/searchgasm/lib/searchgasm/search/protection.rb
@@ -22,11 +22,11 @@ module Search
# User.all(params[:search])
module Protection
# Options that are allowed when protecting against SQL injections (still checked though)
- SAFE_OPTIONS = Base::SPECIAL_FIND_OPTIONS + [:conditions, :limit, :offset]
+ SAFE_OPTIONS = Base::SPECIAL_FIND_OPTIONS + [:conditions, :limit, :offset] - [:priority_order]
- VULNERABLE_FIND_OPTIONS = Base::AR_FIND_OPTIONS - SAFE_OPTIONS
+ VULNERABLE_FIND_OPTIONS = Base::AR_FIND_OPTIONS - SAFE_OPTIONS + [:priority_order]
- VULNERABLE_CALCULATIONS_OPTIONS = Base::AR_CALCULATIONS_OPTIONS - SAFE_OPTIONS
+ VULNERABLE_CALCULATIONS_OPTIONS = Base::AR_CALCULATIONS_OPTIONS - SAFE_OPTIONS + [:priority_order]
# Options that are not allowed, at all, when protecting against SQL injections
VULNERABLE_OPTIONS = Base::OPTIONS - SAFE_OPTIONS
View
10 vendor/plugins/searchgasm/lib/searchgasm/shared/utilities.rb
@@ -45,6 +45,16 @@ def merge_joins(*joins)
new_joins.compact.uniq
end
+
+ # "meaningful" is subjective which is why this is not a core extension like .blank?
+ def meaningless?(value)
+ return false if value == false
+ value.blank?
+ end
+
+ def meaningful?(value)
+ !meaningless?
+ end
end
end
end
View
2  vendor/plugins/searchgasm/lib/searchgasm/version.rb
@@ -67,7 +67,7 @@ def to_a
MAJOR = 1
MINOR = 2
- TINY = 0
+ TINY = 2
# The current version as a Version instance
CURRENT = new(MAJOR, MINOR, TINY)
View
1  vendor/plugins/searchgasm/test/test_active_record_base.rb
@@ -11,6 +11,7 @@ def test_standard_find
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)
+ assert_equal [1,2,3], Account.find(:all, :select => "id, name").map(&:id)
end
def test_standard_calculations
View
18 vendor/plugins/searchgasm/test/test_condition_base.rb
@@ -12,16 +12,18 @@ def test_name_for_column
assert_equal nil, Searchgasm::Condition::Keywords.name_for_column(Account.columns_hash["id"])
end
- def test_ignore_blanks?
- assert !Searchgasm::Condition::Equals.ignore_blanks?
- assert Searchgasm::Condition::Keywords.ignore_blanks?
+ def test_ignore_meaningless?
+ assert !Searchgasm::Condition::Equals.ignore_meaningless?
+ assert Searchgasm::Condition::Keywords.ignore_meaningless?
+ assert !Searchgasm::Condition::DoesNotEqual.ignore_meaningless?
end
- 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?
+ def test_type_cast_sql_type
+ assert_equal nil, Searchgasm::Condition::Equals.type_cast_sql_type
+ assert_equal nil, Searchgasm::Condition::Keywords.type_cast_sql_type
+ assert_equal "boolean", Searchgasm::Condition::Nil.type_cast_sql_type
+ assert_equal "boolean", Searchgasm::Condition::Blank.type_cast_sql_type
+ assert_equal nil, Searchgasm::Condition::GreaterThan.type_cast_sql_type
end
def test_string_column
View
60 vendor/plugins/searchgasm/test/test_condition_types.rb
@@ -6,6 +6,30 @@ def test_sanitize
condition.value = "Binary"
assert_equal condition.sanitize, ["\"accounts\".\"name\" LIKE ?", "Binary%"]
+ condition = Searchgasm::Condition::Blank.new(Account, Account.columns_hash["id"])
+ condition.value = true
+ assert_equal condition.sanitize, "\"accounts\".\"id\" is NULL or \"accounts\".\"id\" = ''"
+
+ condition = Searchgasm::Condition::Blank.new(Account, Account.columns_hash["id"])
+ condition.value = false
+ assert_equal condition.sanitize, "\"accounts\".\"id\" is NOT NULL and \"accounts\".\"id\" != ''"
+
+ condition = Searchgasm::Condition::Blank.new(Account, Account.columns_hash["id"])
+ condition.value = "true"
+ assert_equal condition.sanitize, "\"accounts\".\"id\" is NULL or \"accounts\".\"id\" = ''"
+
+ condition = Searchgasm::Condition::Blank.new(Account, Account.columns_hash["id"])
+ condition.value = "false"
+ assert_equal condition.sanitize, "\"accounts\".\"id\" is NOT NULL and \"accounts\".\"id\" != ''"
+
+ condition = Searchgasm::Condition::Blank.new(Account, Account.columns_hash["id"])
+ condition.value = nil
+ assert_equal condition.sanitize, nil
+
+ condition = Searchgasm::Condition::Blank.new(Account, Account.columns_hash["id"])
+ condition.value = ""
+ assert_equal condition.sanitize, nil
+
condition = Searchgasm::Condition::ChildOf.new(User)
condition.value = User.first.id
assert_equal condition.sanitize, ["\"users\".\"parent_id\" = ?", User.first.id]
@@ -62,51 +86,27 @@ def test_sanitize
condition.value = User.find(1)
assert_equal condition.sanitize, ["(\"users\".\"id\" = ?) OR (\"users\".\"id\" = ? OR \"users\".\"id\" = ?)", 1, 2, 3]
- condition = Searchgasm::Condition::IsBlank.new(Account, Account.columns_hash["id"])
- condition.value = true
- assert_equal condition.sanitize, "\"accounts\".\"id\" is NULL or \"accounts\".\"id\" = ''"
-
- condition = Searchgasm::Condition::IsBlank.new(Account, Account.columns_hash["id"])
- condition.value = false
- assert_equal condition.sanitize, "\"accounts\".\"id\" is NOT NULL and \"accounts\".\"id\" != ''"
-
- condition = Searchgasm::Condition::IsBlank.new(Account, Account.columns_hash["id"])
- condition.value = "true"
- assert_equal condition.sanitize, "\"accounts\".\"id\" is NULL or \"accounts\".\"id\" = ''"
-
- condition = Searchgasm::Condition::IsBlank.new(Account, Account.columns_hash["id"])
- condition.value = "false"
- assert_equal condition.sanitize, "\"accounts\".\"id\" is NOT NULL and \"accounts\".\"id\" != ''"
-
- condition = Searchgasm::Condition::IsBlank.new(Account, Account.columns_hash["id"])
- condition.value = nil
- assert_equal condition.sanitize, nil
-
- condition = Searchgasm::Condition::IsBlank.new(Account, Account.columns_hash["id"])
- condition.value = ""
- assert_equal condition.sanitize, nil
-
- condition = Searchgasm::Condition::IsNil.new(Account, Account.columns_hash["id"])
+ condition = Searchgasm::Condition::Nil.new(Account, Account.columns_hash["id"])
condition.value = true
assert_equal condition.sanitize, "\"accounts\".\"id\" is NULL"
- condition = Searchgasm::Condition::IsNil.new(Account, Account.columns_hash["id"])
+ condition = Searchgasm::Condition::Nil.new(Account, Account.columns_hash["id"])
condition.value = false
assert_equal condition.sanitize, "\"accounts\".\"id\" is NOT NULL"
- condition = Searchgasm::Condition::IsNil.new(Account, Account.columns_hash["id"])
+ condition = Searchgasm::Condition::Nil.new(Account, Account.columns_hash["id"])
condition.value = "true"
assert_equal condition.sanitize, "\"accounts\".\"id\" is NULL"
- condition = Searchgasm::Condition::IsNil.new(Account, Account.columns_hash["id"])
+ condition = Searchgasm::Condition::Nil.new(Account, Account.columns_hash["id"])
condition.value = "false"
assert_equal condition.sanitize, "\"accounts\".\"id\" is NOT NULL"
- condition = Searchgasm::Condition::IsNil.new(Account, Account.columns_hash["id"])
+ condition = Searchgasm::Condition::Nil.new(Account, Account.columns_hash["id"])
condition.value = nil
assert_equal condition.sanitize, nil
- condition = Searchgasm::Condition::IsNil.new(Account, Account.columns_hash["id"])
+ condition = Searchgasm::Condition::Nil.new(Account, Account.columns_hash["id"])
condition.value = ""
assert_equal condition.sanitize, nil
View
1  vendor/plugins/searchgasm/test/test_conditions_base.rb
@@ -22,6 +22,7 @@ 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"})
+ assert !Searchgasm::Conditions::Base.needed?(User, {"orders.id" => 2})
end
def test_initialize
View
4 vendor/plugins/searchgasm/test/test_search_base.rb
@@ -180,6 +180,10 @@ def test_searching
assert_equal [Account.find(1)], search.find(:all)
assert_equal Account.find(1), search.first
assert_equal Account.find(1), search.find(:first)
+
+ search = Account.new_search
+ search.select = "id, name"
+ assert_equal Account.all, search.all
end
def test_calculations
View
136 vendor/plugins/searchgasm/test/test_search_ordering.rb
@@ -1,49 +1,34 @@
require File.dirname(__FILE__) + '/test_helper.rb'
class TestSearchOrdering < Test::Unit::TestCase
- def test_order_as
+ def test_order_to_order_by
search = Account.new_search
- assert_equal nil, search.order
- assert_equal "ASC", search.order_as
- assert search.asc?
-
- search.order_as = "DESC"
- assert_equal "DESC", search.order_as
- assert search.desc?
- assert_equal "\"accounts\".\"id\" DESC", search.order
-
- search.order = "id ASC"
- assert_equal "ASC", search.order_as
- assert search.asc?
- assert_equal "id ASC", search.order
-
- search.order = "id DESC"
- assert_equal "DESC", search.order_as
- assert search.desc?
- assert_equal "id DESC", search.order
-
- search.order_by = "name"
- assert_equal "DESC", search.order_as
- assert search.desc?
- assert_equal "\"accounts\".\"name\" DESC", search.order
+ search.order = "name"
+ assert_equal "name", search.order_by
+ search.order = "users.first_name"
+ assert_equal({"users" => "first_name"}, search.order_by)
+ search.order = "\"users\".\"first_name\""
+ assert_equal({"users" => "first_name"}, search.order_by)
+ search.order = "\"users\".\"first_name\", name ASC"
+ assert_equal([{"users" => "first_name"}, "name"], search.order_by)
end
def test_order_by
search = Account.new_search
assert_equal nil, search.order
- assert_equal "id", search.order_by
+ assert_equal nil, search.order_by
search.order_by = "first_name"
assert_equal "first_name", search.order_by
- assert_equal "\"accounts\".\"first_name\" ASC", search.order
+ assert_equal "\"accounts\".\"first_name\"", search.order
search.order_by = "last_name"
assert_equal "last_name", search.order_by
- assert_equal "\"accounts\".\"last_name\" ASC", search.order
+ assert_equal "\"accounts\".\"last_name\"", search.order
search.order_by = ["first_name", "last_name"]
assert_equal ["first_name", "last_name"], search.order_by
- assert_equal "\"accounts\".\"first_name\" ASC, \"accounts\".\"last_name\" ASC", search.order
+ assert_equal "\"accounts\".\"first_name\", \"accounts\".\"last_name\"", search.order
search.order = "created_at DESC"
assert_equal "created_at", search.order_by
@@ -77,4 +62,99 @@ def test_order_by
assert_equal nil, search.order_by
assert_equal "`line_items`.id DESC", search.order
end
+
+ def test_order_as
+ search = Account.new_search
+ assert_equal nil, search.order
+ assert_equal nil, search.order_as
+ assert search.asc?
+ assert !search.desc?
+
+ search.order_as = "DESC"
+ assert_equal nil, search.order_as
+ assert !search.desc?
+ assert_equal nil, search.order
+
+ search.order_by = "name"
+ assert_equal "\"accounts\".\"name\" DESC", search.order
+
+ search.order_as = "ASC"
+ assert_equal "\"accounts\".\"name\" ASC", search.order
+ assert search.asc?
+
+ search.order = "id ASC"
+ assert_equal "ASC", search.order_as
+ assert search.asc?
+ assert_equal "id ASC", search.order
+
+ search.order = "id DESC"
+ assert_equal "DESC", search.order_as
+ assert search.desc?
+ assert_equal "id DESC", search.order
+
+ search.order_by = "name"
+ assert_equal "DESC", search.order_as
+ assert search.desc?
+ assert_equal "\"accounts\".\"name\" DESC", search.order
+
+ assert_raise(ArgumentError) { search.order_as = "awesome" }
+ end
+
+ def test_order_by_auto_joins
+ search = Account.new_search
+ assert_equal nil, search.order_by_auto_joins
+ search.order_by = :name
+ assert_equal nil, search.order_by_auto_joins
+ search.order_by = {:users => :first_name}
+ assert_equal :users, search.order_by_auto_joins
+ search.order_by = [{:users => :first_name}, {:orders => :total}, {:users => {:user_groups => :name}}]
+ assert_equal [:users, :orders, {:users => :user_groups}], search.order_by_auto_joins
+ search.priority_order_by = {:users => :first_name}
+ assert_equal [:users, :orders, {:users => :user_groups}], search.order_by_auto_joins
+ search.priority_order_by = {:users => {:orders => :total}}
+ assert_equal({:users => :orders}, search.priority_order_by_auto_joins)
+ assert_equal [:users, :orders, {:users => :user_groups}, {:users => :orders}], search.auto_joins
+ end
+
+ def test_priority_order_by
+ search = Account.new_search
+ assert_equal nil, search.priority_order
+ assert_equal nil, search.priority_order_by
+ assert_equal nil, search.priority_order_as
+
+ search.priority_order_by = :name
+ assert_equal "\"accounts\".\"name\"", search.priority_order
+ assert_equal "\"accounts\".\"name\"", search.sanitize[:order]
+ assert_equal nil, search.order
+ assert_equal :name, search.priority_order_by
+ assert_equal nil, search.priority_order_as
+
+ search.order_by = :id
+ assert_equal "\"accounts\".\"name\", \"accounts\".\"id\"", search.sanitize[:order]
+ search.order_as = "DESC"
+ assert_equal "\"accounts\".\"name\", \"accounts\".\"id\" DESC", search.sanitize[:order]
+ end
+
+ def test_priority_order_as
+ search = Account.new_search
+ assert_equal nil, search.priority_order_as
+ assert_equal nil, search.order_as
+ search.priority_order_as = "ASC"
+ assert_equal nil, search.priority_order_as
+ assert_equal nil, search.order_as
+ search.priority_order_by = :name
+ assert_equal "ASC", search.priority_order_as
+ assert_equal nil, search.order_as
+ search.priority_order_as = "DESC"
+ assert_equal "DESC", search.priority_order_as
+ assert_equal nil, search.order_as
+ assert_raise(ArgumentError) { search.priority_order_as = "awesome" }
+ search.priority_order = nil
+ assert_equal nil, search.priority_order_as
+ assert_equal nil, search.order_as
+ end
+
+ def test_sanitize
+ # tested in test_priority_order_by
+ end
end
Please sign in to comment.
Something went wrong with that request. Please try again.