searchgasm 1.1.1 → 1.1.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (35) hide show
  1. data/CHANGELOG.rdoc +8 -0
  2. data/Manifest +2 -4
  3. data/README.rdoc +3 -4
  4. data/lib/searchgasm/active_record/base.rb +23 -20
  5. data/lib/searchgasm/condition/base.rb +18 -7
  6. data/lib/searchgasm/condition/does_not_equal.rb +2 -4
  7. data/lib/searchgasm/condition/equals.rb +2 -4
  8. data/lib/searchgasm/condition/is_blank.rb +23 -0
  9. data/lib/searchgasm/condition/is_nil.rb +23 -0
  10. data/lib/searchgasm/conditions/base.rb +9 -9
  11. data/lib/searchgasm/config.rb +14 -14
  12. data/lib/searchgasm/helpers/control_types/link.rb +55 -0
  13. data/lib/searchgasm/helpers/control_types/links.rb +4 -9
  14. data/lib/searchgasm/helpers/control_types/select.rb +7 -4
  15. data/lib/searchgasm/helpers/form.rb +2 -2
  16. data/lib/searchgasm/helpers/utilities.rb +3 -2
  17. data/lib/searchgasm/search/base.rb +20 -2
  18. data/lib/searchgasm/search/conditions.rb +4 -14
  19. data/lib/searchgasm/search/ordering.rb +30 -29
  20. data/lib/searchgasm/search/pagination.rb +16 -10
  21. data/lib/searchgasm/shared/searching.rb +21 -19
  22. data/lib/searchgasm/shared/utilities.rb +18 -0
  23. data/lib/searchgasm/version.rb +1 -1
  24. data/lib/searchgasm.rb +3 -1
  25. data/searchgasm.gemspec +7 -11
  26. data/test/test_active_record_associations.rb +2 -2
  27. data/test/test_condition_base.rb +2 -2
  28. data/test/test_condition_types.rb +48 -0
  29. data/test/test_conditions_base.rb +5 -5
  30. data/test/test_search_base.rb +8 -8
  31. metadata +6 -10
  32. data/lib/searchgasm/active_record.rb +0 -8
  33. data/lib/searchgasm/helpers/control_types.rb +0 -57
  34. data/lib/searchgasm/helpers.rb +0 -9
  35. data/lib/searchgasm/search.rb +0 -7
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,11 @@
1
+ == 1.1.2 released 2008-09-22
2
+
3
+ * Fixed bug with select control types not using :search_obj to determine its select values.
4
+ * Added is_nil and is_blank condition types.
5
+ * "memoized" various attributes for performance enhancements
6
+ * Removed the :order option from calculation options when :order is useless and just slows down query.
7
+ * Switched from using :include to :joins, big performance increase
8
+
1
9
  == 1.1.1 released 2008-09-19
2
10
 
3
11
  * Fixed typo in "next page" button.
data/Manifest CHANGED
@@ -3,7 +3,6 @@ examples/README.rdoc
3
3
  init.rb
4
4
  lib/searchgasm/active_record/associations.rb
5
5
  lib/searchgasm/active_record/base.rb
6
- lib/searchgasm/active_record.rb
7
6
  lib/searchgasm/condition/base.rb
8
7
  lib/searchgasm/condition/begins_with.rb
9
8
  lib/searchgasm/condition/child_of.rb
@@ -15,6 +14,8 @@ lib/searchgasm/condition/equals.rb
15
14
  lib/searchgasm/condition/greater_than.rb
16
15
  lib/searchgasm/condition/greater_than_or_equal_to.rb
17
16
  lib/searchgasm/condition/inclusive_descendant_of.rb
17
+ lib/searchgasm/condition/is_blank.rb
18
+ lib/searchgasm/condition/is_nil.rb
18
19
  lib/searchgasm/condition/keywords.rb
19
20
  lib/searchgasm/condition/less_than.rb
20
21
  lib/searchgasm/condition/less_than_or_equal_to.rb
@@ -30,16 +31,13 @@ lib/searchgasm/helpers/control_types/remote_link.rb
30
31
  lib/searchgasm/helpers/control_types/remote_links.rb
31
32
  lib/searchgasm/helpers/control_types/remote_select.rb
32
33
  lib/searchgasm/helpers/control_types/select.rb
33
- lib/searchgasm/helpers/control_types.rb
34
34
  lib/searchgasm/helpers/form.rb
35
35
  lib/searchgasm/helpers/utilities.rb
36
- lib/searchgasm/helpers.rb
37
36
  lib/searchgasm/search/base.rb
38
37
  lib/searchgasm/search/conditions.rb
39
38
  lib/searchgasm/search/ordering.rb
40
39
  lib/searchgasm/search/pagination.rb
41
40
  lib/searchgasm/search/protection.rb
42
- lib/searchgasm/search.rb
43
41
  lib/searchgasm/shared/searching.rb
44
42
  lib/searchgasm/shared/utilities.rb
45
43
  lib/searchgasm/shared/virtual_classes.rb
data/README.rdoc CHANGED
@@ -9,6 +9,7 @@ Searchgasm is orgasmic. Maybe not orgasmic, but you will get aroused. So go grab
9
9
  * <b>Documentation:</b> http://searchgasm.rubyforge.org
10
10
  * <b>Easy pagination, ordering, and searching tutorial:</b> http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchgasm
11
11
  * <b>Live example of the tutorial above (with source):</b> http://searchgasm_example.binarylogic.com
12
+ * <b>Bugs / feature suggestions:</b> http://binarylogic.lighthouseapp.com/projects/16601-searchgasm
12
13
 
13
14
  == Install and use
14
15
 
@@ -288,6 +289,8 @@ Some of these conditions come with aliases, so you have your choice how to call
288
289
 
289
290
  :equals => :is
290
291
  :does_not_equal => :is_not, :not
292
+ :is_nil => :nil, :is_null, :null
293
+ :is_blank => :blank
291
294
  :begins_with => :starts_with, :sw, :bw, :start
292
295
  :contains => :like, :has
293
296
  :ends_with => :ew, :ends, :end
@@ -353,10 +356,6 @@ ActiveRecord should never know about Searchgasm
353
356
 
354
357
  What that rule means is that any options you pass when searching get "sanitized" down into options ActiveRecord can understand. Searchgasm serves as a transparent filter between you and ActiveRecord. It doesn't dig into the ActiveRecord internals, it only uses what is publicly available. It jumps in and helps out <em>only</em> when needed, otherwise it sits back and stays completely out of the way. Between that and the extensive tests, this is a solid and fast plugin.
355
358
 
356
- == Reporting problems / bugs
357
-
358
- http://binarylogic.lighthouseapp.com/projects/16601-searchgasm
359
-
360
359
  == Credits
361
360
 
362
361
  Author: {Ben Johnson}[http://github.com/binarylogic] of {Binary Logic}[http://www.binarylogic.com]
@@ -1,4 +1,8 @@
1
1
  module Searchgasm
2
+ # == Searchgasm ActiveRecord
3
+ #
4
+ # Hooks into ActiveRecord to add all of the searchgasm functionality into your models. Only uses what is publically available, doesn't dig into internals, and
5
+ # searchgasm only gets involved when needed.
2
6
  module ActiveRecord
3
7
  # = Searchgasm ActiveRecord Base
4
8
  # Adds in base level functionality to ActiveRecord
@@ -6,7 +10,7 @@ module Searchgasm
6
10
  # This is an alias method chain. It hook into ActiveRecord's "calculate" method and checks to see if Searchgasm should get involved.
7
11
  def calculate_with_searchgasm(*args)
8
12
  options = args.extract_options!
9
- options = filter_options_with_searchgasm(options)
13
+ options = filter_options_with_searchgasm(options, false)
10
14
  args << options
11
15
  calculate_without_searchgasm(*args)
12
16
  end
@@ -124,7 +128,7 @@ module Searchgasm
124
128
  end
125
129
 
126
130
  private
127
- def filter_options_with_searchgasm(options = {})
131
+ def filter_options_with_searchgasm(options = {}, searching = true)
128
132
  return options unless Searchgasm::Search::Base.needed?(self, options)
129
133
  search = Searchgasm::Search::Base.create_virtual_class(self).new # call explicitly to avoid merging the scopes into the searcher
130
134
  search.acting_as_filter = true
@@ -138,32 +142,31 @@ module Searchgasm
138
142
  end
139
143
  end
140
144
  search.options = options
141
- search.sanitize
145
+ search.sanitize(searching)
142
146
  end
143
147
 
144
148
  def searchgasm_conditions(options = {})
145
- searcher = nil
146
- conditions = nil
147
- conditions = options.delete(:conditions) if options[:conditions].is_a?(Hash)
148
-
149
- with_scope(:find => {:conditions => options[:conditions]}) do
150
- searcher = Searchgasm::Conditions::Base.create_virtual_class(self).new(scope(:find)[:conditions])
151
- searcher.conditions = conditions unless conditions.nil?
152
- end
153
-
149
+ searcher = Searchgasm::Conditions::Base.create_virtual_class(self).new
150
+ conditions = scope(:find) && scope(:find)[:conditions]
151
+ searcher.scope = {:conditions => conditions} if conditions
152
+ searcher.conditions = options
154
153
  searcher
155
154
  end
156
155
 
157
156
  def searchgasm_searcher(options = {})
158
- searcher = nil
159
- conditions = nil
160
- conditions = options.delete(:conditions) if options[:conditions].is_a?(Hash)
161
-
162
- with_scope(:find => options) do
163
- searcher = Searchgasm::Search::Base.create_virtual_class(self).new(scope(:find))
164
- searcher.conditions = conditions unless conditions.nil?
157
+ scope = {}
158
+ current_scope = scope(:find) && scope(:find).deep_dup
159
+ if current_scope
160
+ [:conditions, :include, :joins].each do |option|
161
+ value = current_scope.delete(option)
162
+ next if value.blank?
163
+ scope[option] = value
164
+ end
165
165
  end
166
-
166
+ searcher = Searchgasm::Search::Base.create_virtual_class(self).new
167
+ searcher.scope = scope
168
+ searcher.options = current_scope
169
+ searcher.options = options
167
170
  searcher
168
171
  end
169
172
  end
@@ -9,6 +9,9 @@ module Searchgasm
9
9
 
10
10
  attr_accessor :column, :klass
11
11
  attr_reader :value
12
+ class_inheritable_accessor :ignore_blanks, :type_cast_value
13
+ self.ignore_blanks = true
14
+ self.type_cast_value = true
12
15
 
13
16
  class << self
14
17
  # Name of the condition inferred from the class name
@@ -27,6 +30,14 @@ module Searchgasm
27
30
  []
28
31
  end
29
32
 
33
+ def ignore_blanks? # :nodoc:
34
+ ignore_blanks == true
35
+ end
36
+
37
+ def type_cast_value? # :nodoc:
38
+ type_cast_value == true
39
+ end
40
+
30
41
  # 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
31
42
  # specific columns. Any condition that applies to columns should probably return nil here.
32
43
  def name_for_klass(klass)
@@ -64,11 +75,6 @@ module Searchgasm
64
75
  @explicitly_set_value == true
65
76
  end
66
77
 
67
- # In most cases a blank value should be ignored, except for conditions like equals. A blank value is meaningful there, but a blank value for they keyswords condition is not.
68
- def ignore_blanks?
69
- true
70
- end
71
-
72
78
  # A convenience method for the name of the method for that specific column or klass
73
79
  def name
74
80
  column ? self.class.name_for_column(column) : self.class.name_for_klass(klass)
@@ -110,15 +116,20 @@ module Searchgasm
110
116
  to_conditions(v)
111
117
  end
112
118
  end
119
+
120
+ # Should the value be automatically type casted
121
+ def type_cast_value?
122
+ true
123
+ end
113
124
 
114
125
  # The value for the condition
115
126
  def value
116
- @value.is_a?(String) ? column.type_cast(@value) : @value
127
+ self.class.type_cast_value? && @value.is_a?(String) ? column.type_cast(@value) : @value
117
128
  end
118
129
 
119
130
  # Sets the value for the condition, will ignore place if ignore_blanks?
120
131
  def value=(v)
121
- return if ignore_blanks? && v.blank?
132
+ return if self.class.ignore_blanks? && v.blank?
122
133
  self.explicitly_set_value = true
123
134
  @value = v
124
135
  end
@@ -1,16 +1,14 @@
1
1
  module Searchgasm
2
2
  module Condition
3
3
  class DoesNotEqual < Base
4
+ self.ignore_blanks = false
5
+
4
6
  class << self
5
7
  def aliases_for_column(column)
6
8
  ["#{column.name}_is_not", "#{column.name}_not"]
7
9
  end
8
10
  end
9
11
 
10
- def ignore_blanks?
11
- false
12
- end
13
-
14
12
  def to_conditions(value)
15
13
  # Delegate to equals and then change
16
14
  condition = Equals.new(klass, column)
@@ -1,16 +1,14 @@
1
1
  module Searchgasm
2
2
  module Condition
3
3
  class Equals < Base
4
+ self.ignore_blanks = false
5
+
4
6
  class << self
5
7
  def aliases_for_column(column)
6
8
  ["#{column.name}", "#{column.name}_is"]
7
9
  end
8
10
  end
9
11
 
10
- def ignore_blanks?
11
- false
12
- end
13
-
14
12
  def to_conditions(value)
15
13
  # Let ActiveRecord handle this
16
14
  klass.send(:sanitize_sql_hash_for_conditions, {column.name => value})
@@ -0,0 +1,23 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class IsBlank < Base
4
+ self.ignore_blanks = false
5
+ self.type_cast_value = false
6
+
7
+ class << self
8
+ def aliases_for_column(column)
9
+ ["#{column.name}_blank"]
10
+ end
11
+ end
12
+
13
+ def to_conditions(value)
14
+ # Some databases handle null values differently, let AR handle this
15
+ if value == true || value == "true" || value == 1 || value == "1"
16
+ "#{quoted_table_name}.#{quoted_column_name} is NULL or #{quoted_table_name}.#{quoted_column_name} = ''"
17
+ elsif value == false || value == "false" || value == 0 || value == "0"
18
+ "#{quoted_table_name}.#{quoted_column_name} is NOT NULL and #{quoted_table_name}.#{quoted_column_name} != ''"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ module Searchgasm
2
+ module Condition
3
+ class IsNil < Base
4
+ self.ignore_blanks = false
5
+ self.type_cast_value = false
6
+
7
+ class << self
8
+ def aliases_for_column(column)
9
+ ["#{column.name}_nil", "#{column.name}_is_null", "#{column.name}_null"]
10
+ end
11
+ end
12
+
13
+ def to_conditions(value)
14
+ # Some databases handle null values differently, let AR handle this
15
+ if value == true || value == "true" || value == 1 || value == "1"
16
+ "#{quoted_table_name}.#{quoted_column_name} is NULL"
17
+ elsif value == false || value == "false" || value == 0 || value == "0"
18
+ "#{quoted_table_name}.#{quoted_column_name} is NOT NULL"
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -5,9 +5,9 @@ module Searchgasm
5
5
  # Represents a collection of conditions and performs various tasks on that collection. For information on each condition see Searchgasm::Condition.
6
6
  # Each condition has its own file and class and the source for each condition is pretty self explanatory.
7
7
  class Base
8
- include Searchgasm::Shared::Utilities
9
- include Searchgasm::Shared::Searching
10
- include Searchgasm::Shared::VirtualClasses
8
+ include Shared::Utilities
9
+ include Shared::Searching
10
+ include Shared::VirtualClasses
11
11
 
12
12
  attr_accessor :any, :relationship_name, :sql
13
13
 
@@ -104,15 +104,15 @@ module Searchgasm
104
104
  @any == true || @any == "true" || @any == "1" || @any == "yes"
105
105
  end
106
106
 
107
- # A list of includes to use when searching, includes relationships
108
- def includes
109
- i = []
107
+ # A list of joins to use when searching, includes relationships
108
+ def joins
109
+ j = []
110
110
  associations.each do |association|
111
111
  next if association.conditions.blank?
112
- association_includes = association.includes
113
- i << (association_includes.blank? ? association.relationship_name.to_sym : {association.relationship_name.to_sym => association_includes})
112
+ association_joins = association.joins
113
+ j << (association_joins.blank? ? association.relationship_name.to_sym : {association.relationship_name.to_sym => association_joins})
114
114
  end
115
- i.blank? ? nil : (i.size == 1 ? i.first : i)
115
+ j.blank? ? nil : (j.size == 1 ? j.first : j)
116
116
  end
117
117
 
118
118
  def inspect
@@ -43,6 +43,18 @@ module Searchgasm
43
43
  @desc_indicator = value
44
44
  end
45
45
 
46
+ def hidden_fields # :nodoc:
47
+ @hidden_fields ||= (Search::Base::SPECIAL_FIND_OPTIONS - [:page])
48
+ end
49
+
50
+ # Which hidden fields to automatically include when creating a form with a Searchgasm object. See Searchgasm::Helpers::Form for more info.
51
+ #
52
+ # * <tt>Default:</tt> [:order_by, :order_as, :per_page]
53
+ # * <tt>Accepts:</tt> Array, nil, false
54
+ def hidden_fields=(value)
55
+ @hidden_fields = value
56
+ end
57
+
46
58
  def page_links_first # :nodoc:
47
59
  @page_links_first
48
60
  end
@@ -80,7 +92,7 @@ module Searchgasm
80
92
  end
81
93
 
82
94
  def page_links_outer_spread # :nodoc:
83
- @page_links_outer_spread ||= 2
95
+ @page_links_outer_spread ||= 1
84
96
  end
85
97
 
86
98
  # The default for the :outer_spread option for the page_links helper.
@@ -116,7 +128,7 @@ module Searchgasm
116
128
  end
117
129
 
118
130
  def per_page # :nodoc:
119
- @per_page ||= per_page_choices[2]
131
+ @per_page ||= per_page_choices[1]
120
132
  end
121
133
 
122
134
  # The default for per page. This is only applicaple for protected searches. Meaning you start the search with new_search or new_conditions.
@@ -141,18 +153,6 @@ module Searchgasm
141
153
  def per_page_choices=(value)
142
154
  @per_page_choices = value
143
155
  end
144
-
145
- def hidden_fields # :nodoc:
146
- @hidden_fields ||= (Search::Base::SPECIAL_FIND_OPTIONS - [:page])
147
- end
148
-
149
- # Which hidden fields to automatically include when creating a form with a Searchgasm object. See Searchgasm::Helpers::Form for more info.
150
- #
151
- # * <tt>Default:</tt> [:order_by, :order_as, :per_page]
152
- # * <tt>Accepts:</tt> Array, nil, false
153
- def hidden_fields=(value)
154
- @hidden_fields = value
155
- end
156
156
  end
157
157
  end
158
158
  end
@@ -1,5 +1,56 @@
1
1
  module Searchgasm
2
2
  module Helpers
3
+ # = Control Type Helpers
4
+ #
5
+ # The purpose of these helpers is to make ordering and paginating data, in your view, a breeze. Everyone has their own flavor of displaying data, so I made these helpers extra flexible, just for you.
6
+ #
7
+ # === Tutorial
8
+ #
9
+ # Check out my tutorial on how to implement searchgasm into a rails app: http://www.binarylogic.com/2008/9/7/tutorial-pagination-ordering-and-searching-with-searchgasm
10
+ #
11
+ # === How it's organized
12
+ #
13
+ # If we break it down, you can do 4 different things with your data in your view:
14
+ #
15
+ # 1. Order your data by a single column or an array of columns
16
+ # 2. Descend or ascend your data
17
+ # 3. Change how many items are on each page
18
+ # 4. Paginate through your data
19
+ #
20
+ # Each one of these actions comes with 3 different types of helpers:
21
+ #
22
+ # 1. Link - A single link for a single value. Requires that you pass a value as the first parameter.
23
+ # 2. Links - A group of single links.
24
+ # 3. Select - A select with choices that perform an action once selected. Basically the same thing as a group of links, but just as a select form element
25
+ # 4. Remote - lets you prefix any of these helpers with "remote_" and it will use the built in rails ajax helpers. I highly recommend unobstrusive javascript though, using jQuery.
26
+ #
27
+ # === Examples
28
+ #
29
+ # Sometimes the best way to explain something is with some examples. Let's pretend we are performing these actions on a User model. Check it out:
30
+ #
31
+ # order_by_link(:name)
32
+ # => produces a single link that when clicked will order by the name column, and each time its clicked alternated between "ASC" and "DESC"
33
+ #
34
+ # order_by_links
35
+ # => produces a group of links for all of the columns in your users table, each link is basically order_by_link(column.name)
36
+ #
37
+ # order_by_select
38
+ # => produces a select form element with all of the user's columns as choices, when the value is change (onchange) it will act as if they clicked a link.
39
+ # => This is just order_by_links as a select form element, nothing fancy
40
+ #
41
+ # What about paginating? I got you covered:
42
+ #
43
+ # page_link(2)
44
+ # => creates a link to page 2
45
+ #
46
+ # page_links
47
+ # => creates a group of links for pages, similar to a flickr style of pagination
48
+ #
49
+ # page_select
50
+ # => creates a drop down instead of a group of links. The user can select the page in the drop down and it will be as if they clicked a link for that page.
51
+ #
52
+ # You can apply the _link, _links, or _select to any of the following: order_by, order_as, per_page, page. You have your choice on how you want to set up the interface. For more information and options on these individual
53
+ # helpers check out their source files. Look at the sub modules under this one (Ex: Searchgasm::Helpers::ControlTypes::Select)
3
54
  module ControlTypes
4
55
  # = Link Control Types
5
56
  #
@@ -29,6 +80,7 @@ module Searchgasm
29
80
  # === Advanced Options
30
81
  # * <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.
31
82
  # * <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.
83
+ # * <tt>:url_params</tt> -- default: nil, Additional params to add to the url, must be a hash
32
84
  def order_by_link(order_by, options = {})
33
85
  order_by = deep_stringify(order_by)
34
86
  add_order_by_link_defaults!(order_by, options)
@@ -58,6 +110,7 @@ module Searchgasm
58
110
  # === Advanced Options
59
111
  # * <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.
60
112
  # * <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.
113
+ # * <tt>:url_params</tt> -- default: nil, Additional params to add to the url, must be a hash
61
114
  def order_as_link(order_as, options = {})
62
115
  add_order_as_link_defaults!(order_as, options)
63
116
  html = searchgasm_state_for(:order_as, options)
@@ -88,6 +141,7 @@ module Searchgasm
88
141
  # === Advanced Options
89
142
  # * <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.
90
143
  # * <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.
144
+ # * <tt>:url_params</tt> -- default: nil, Additional params to add to the url, must be a hash
91
145
  def per_page_link(per_page, options = {})
92
146
  add_per_page_link_defaults!(per_page, options)
93
147
  html = searchgasm_state_for(:per_page, options)
@@ -116,6 +170,7 @@ module Searchgasm
116
170
  # === Advanced Options
117
171
  # * <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.
118
172
  # * <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.
173
+ # * <tt>:url_params</tt> -- default: nil, Additional params to add to the url, must be a hash
119
174
  def page_link(page, options = {})
120
175
  add_page_link_defaults!(page, options)
121
176
  html = searchgasm_state_for(:page, options)
@@ -90,15 +90,10 @@ module Searchgasm
90
90
  #
91
91
  # Please look at per_page_link. All options there are applicable here and are passed onto each option.
92
92
  #
93
- # * <tt>:inner_spread</tt> -- default: 3, set to nil to show all pages, this represents how many choices available on each side of the current page
94
- # * <tt>:outer_spread</tt> -- default: 1, set to nil to disable, this represents how many choices are in the "outer" spread. If set to 0, the separator will be present with no page links. This option changes the links from
95
- # * "< Prev 2 3 4 [5] 6 7 8 Next >" with 10 total pages to:
96
- # * "< Prev ... 3 4 [5] 6 7 ... Next >" for :outer_spread = 0 and :inner_spread = 3
97
- # * "< Prev 1 ... 3 4 [5] 6 7 ... 10 Next >" for :outer_spread = 1 and :inner_spread = 3
98
- # * "< Prev 1 2 ... 3 4 [5] 6 7 ... 9 10 Next >" for :outer_spread = 2 and :inner_spread = 3 (outer_spread = number of absolute pages on each side)
99
- # * Outer spread pages will not be visible unless the current_page is more than :inner_spread away from the first or last page.
100
- # * <tt>:prev</tt> -- default: < Prev, set to nil to omit. This is an extra link on the left side of the page links that will go to the previous page
101
- # * <tt>:next</tt> -- default: Next >, set to nil to omit. This is an extra link on the right side of the page links that will go to the next page
93
+ # * <tt>:inner_spread</tt> -- default: 3, set to nil to show all pages, set 0 to show no page links. This represents how many choices available on each side of the current page
94
+ # * <tt>:outer_spread</tt> -- default: 1, set to nil to disable, set to 0 show no outer spread but the separator will still be present. This represents how many choices are in the "outer" spread.
95
+ # * <tt>:prev</tt> -- default: "< Prev", set to nil to omit. This is an extra link on the left side of the page links that will go to the previous page
96
+ # * <tt>:next</tt> -- default: "Next >", set to nil to omit. This is an extra link on the right side of the page links that will go to the next page
102
97
  # * <tt>:first</tt> -- default: nil, set to nil to omit. This is an extra link on thefar left side of the page links that will go to the first page
103
98
  # * <tt>:last</tt> -- default: nil, set to nil to omit. This is an extra link on the far right side of the page links that will go to the last page
104
99
  def page_links(options = {})
@@ -8,25 +8,25 @@ module Searchgasm
8
8
  # 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.
9
9
  def order_by_select(options = {})
10
10
  add_order_by_select_defaults!(options)
11
- searchgasm_state_for(:order_by, options) + select(options[:params_scope], :order_by, options[:choices], options[:tag] || {}, options[:html] || {})
11
+ searchgasm_state_for(:order_by, options) + select(options[:params_scope], :order_by, options[:choices], options[:tag], options[:html] || {})
12
12
  end
13
13
 
14
14
  # 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.
15
15
  def order_as_select(options = {})
16
16
  add_order_by_select_defaults!(options)
17
- searchgasm_state_for(:order_as, options) + select(options[:params_scope], :order_as, options[:choices], options[:tag] || {}, options[:html])
17
+ searchgasm_state_for(:order_as, options) + select(options[:params_scope], :order_as, options[:choices], options[:tag], options[:html])
18
18
  end
19
19
 
20
20
  # 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.
21
21
  def per_page_select(options = {})
22
22
  add_per_page_select_defaults!(options)
23
- searchgasm_state_for(:per_page, options) + select(options[:params_scope], :per_page, options[:choices], options[:tag] || {}, options[:html])
23
+ searchgasm_state_for(:per_page, options) + select(options[:params_scope], :per_page, options[:choices], options[:tag], options[:html])
24
24
  end
25
25
 
26
26
  # 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.
27
27
  def page_select(options = {})
28
28
  add_page_select_defaults!(options)
29
- searchgasm_state_for(:page, options) + select(options[:params_scope], :page, (options[:first_page]..options[:last_page]), options[:tag] || {}, options[:html])
29
+ searchgasm_state_for(:page, options) + select(options[:params_scope], :page, (options[:first_page]..options[:last_page]), options[:tag], options[:html])
30
30
  end
31
31
 
32
32
  private
@@ -56,6 +56,9 @@ module Searchgasm
56
56
  end
57
57
 
58
58
  def add_searchgasm_select_defaults!(option, options)
59
+ options[:tag] ||= {}
60
+ options[:tag][:object] = options[:search_obj]
61
+
59
62
  url = searchgasm_url_hash(option, nil, options)
60
63
  url.delete(option)
61
64
  url = searchgasm_url(url, options)
@@ -92,9 +92,9 @@ module Searchgasm
92
92
  options[:html][:onsubmit] += ";"
93
93
 
94
94
  javascript = "if(typeof(Prototype) != 'undefined') {"
95
- search_options[:hidden_fields].each { |field| javascript += "$('#{name}_#{field}_hidden').value = $('#{name}_#{field}').value;" }
95
+ search_options[:hidden_fields].each { |field| javascript += "field = $('#{name}_#{field}'); if(field) { $('#{name}_#{field}_hidden').value = field.value; }" }
96
96
  javascript += "} else if(jQuery) {"
97
- search_options[:hidden_fields].each { |field| javascript += "$('##{name}_#{field}_hidden').val($('##{name}_#{field}').val());" }
97
+ search_options[:hidden_fields].each { |field| javascript += "field = $('##{name}_#{field}'); if(field) { $('##{name}_#{field}_hidden').val(field.val()); }" }
98
98
  javascript += "}"
99
99
 
100
100
  options[:html][:onsubmit] += javascript
@@ -6,7 +6,7 @@ module Searchgasm
6
6
  def add_searchgasm_helper_defaults!(option, options)
7
7
  options[:params_scope] = :search unless options.has_key?(:params_scope)
8
8
  options[:search_obj] ||= instance_variable_get("@#{options[:params_scope]}")
9
- raise(ArgumentError, "@search object could not be inferred, please specify: :search_obj => @search") unless options[:search_obj].is_a?(Searchgasm::Search::Base)
9
+ raise(ArgumentError, "@search object could not be inferred, please specify: :search_obj => @search or :params_scope => :search_obj_name") unless options[:search_obj].is_a?(Searchgasm::Search::Base)
10
10
  options[:html] ||= {}
11
11
  options[:html][:class] ||= ""
12
12
  searchgasm_add_class!(options[:html], option)
@@ -14,7 +14,8 @@ module Searchgasm
14
14
  end
15
15
 
16
16
  def searchgasm_url(url_hash, options)
17
- options[:params_scope].blank? ? url_hash : {options[:params_scope] => url_hash}
17
+ url = options[:params_scope].blank? ? url_hash : {options[:params_scope] => url_hash}
18
+ !options[:url_params].is_a?(Hash) ? url : url.merge(options[:url_params])
18
19
  end
19
20
 
20
21
  def searchgasm_url_hash(option, value, options)
@@ -13,7 +13,7 @@ module Searchgasm #:nodoc:
13
13
  AR_FIND_OPTIONS = ::ActiveRecord::Base.valid_find_options
14
14
 
15
15
  # Options ActiveRecord allows when performing calculations
16
- AR_CALCULATIONS_OPTIONS = ::ActiveRecord::Base.valid_calculations_options
16
+ AR_CALCULATIONS_OPTIONS = (::ActiveRecord::Base.valid_calculations_options - [:select, :limit, :offset, :order, :group])
17
17
 
18
18
  AR_OPTIONS = (AR_FIND_OPTIONS + AR_CALCULATIONS_OPTIONS).uniq
19
19
 
@@ -64,6 +64,11 @@ module Searchgasm #:nodoc:
64
64
  "#<#{klass}Search #{current_find_options.inspect}>"
65
65
  end
66
66
 
67
+ # need to remote any duplicate joins that are specified in includes
68
+ #def joins
69
+ # # If includes are specified, remove the joins
70
+ #end
71
+
67
72
  def limit=(value)
68
73
  @set_limit = true
69
74
  @limit = value.blank? || value == 0 ? nil : value.to_i
@@ -77,7 +82,7 @@ module Searchgasm #:nodoc:
77
82
  def offset=(value)
78
83
  @offset = value.blank? ? nil : value.to_i
79
84
  end
80
-
85
+
81
86
  def options=(values)
82
87
  return unless values.is_a?(Hash)
83
88
  values.symbolize_keys.fast_assert_valid_keys(OPTIONS)
@@ -93,11 +98,24 @@ module Searchgasm #:nodoc:
93
98
  # Sanitizes everything down into options ActiveRecord::Base.find can understand
94
99
  def sanitize(searching = true)
95
100
  find_options = {}
101
+
96
102
  (searching ? AR_FIND_OPTIONS : AR_CALCULATIONS_OPTIONS).each do |find_option|
97
103
  value = send(find_option)
98
104
  next if value.blank?
99
105
  find_options[find_option] = value
100
106
  end
107
+
108
+ unless find_options[:joins].blank?
109
+ # The following is to return uniq records since we are using joins instead of includes
110
+ if searching
111
+ find_options[:group] ||= "#{quote_table_name(klass.table_name)}.#{quote_column_name(klass.primary_key)}"
112
+ else
113
+ # If we are calculating use includes because they use joins that grab uniq records. When calculating, includes don't have the
114
+ # performance hit that they have when searching. Plus it's cleaner.
115
+ find_options[:include] = merge_joins(find_options[:include], find_options.delete(:joins))
116
+ end
117
+ end
118
+
101
119
  find_options
102
120
  end
103
121