searchgasm 1.0.0 → 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -16,10 +16,10 @@ module Searchgasm
16
16
  # * <tt>:choices</tt> -- default: the models column names, the choices to loop through when calling order_by_link
17
17
  def order_by_links(options = {})
18
18
  add_order_by_links_defaults!(options)
19
- link_options = options.dup
19
+ link_options = options.deep_dup
20
20
  link_options.delete(:choices)
21
21
  html = ""
22
- options[:choices].each { |choice| html += order_by_link(choice, link_options.dup) }
22
+ options[:choices].each { |choice| html += order_by_link(choice, link_options.deep_dup) }
23
23
  html
24
24
  end
25
25
 
@@ -37,10 +37,10 @@ module Searchgasm
37
37
  # * <tt>:choices</tt> -- default: ["asc", "desc"], the choices to loop through when calling order_as_link
38
38
  def order_as_links(options = {})
39
39
  add_order_as_links_defaults!(options)
40
- link_options = options.dup
40
+ link_options = options.deep_dup
41
41
  link_options.delete(:choices)
42
42
  html = ""
43
- options[:choices].each { |choice| html += order_as_link(choice, link_options.dup) }
43
+ options[:choices].each { |choice| html += order_as_link(choice, link_options.deep_dup) }
44
44
  html
45
45
  end
46
46
 
@@ -58,10 +58,10 @@ module Searchgasm
58
58
  # * <tt>:choices</tt> -- default: [10, 25, 50, 100, 150, 200, nil], the choices to loop through when calling per_page_link.
59
59
  def per_page_links(options = {})
60
60
  add_per_page_links_defaults!(options)
61
- link_options = options.dup
61
+ link_options = options.deep_dup
62
62
  link_options.delete(:choices)
63
63
  html = ""
64
- options[:choices].each { |choice| html += per_page_link(choice, link_options.dup) }
64
+ options[:choices].each { |choice| html += per_page_link(choice, link_options.deep_dup) }
65
65
  html
66
66
  end
67
67
 
@@ -70,38 +70,53 @@ module Searchgasm
70
70
  # === Examples
71
71
  #
72
72
  # page_links
73
- # page_links(:choices => [25, 50, nil])
73
+ # page_links(:first => "<< First", :last => "Last >>")
74
+ #
75
+ # === Classes and tag
76
+ #
77
+ # If the user is on the current page they will get a <span> tag, not an <a> tag. If they are on the first page the "first" and "prev" options will be a <span> also. The same goes
78
+ # for "next" and "last" if the user is on the last page. Other than that each element will come with a CSS class so you can style it to your liking. Somtimes the easiest way to understand this
79
+ # Is to either look at the example (linked in the README) or try it out and view the HTML source. It's pretty simple, but here are the explanations:
80
+ #
81
+ # * <tt>page</tt> - This is in *every* element, span or a.
82
+ # * <tt>first_page</tt> - This is for the "first page" element only.
83
+ # * <tt>preve_page</tt> - This is for the "prev page" element only.
84
+ # * <tt>current_page</tt> - This is for the current page element
85
+ # * <tt>next_page</tt> - This is for the "next page" element only.
86
+ # * <tt>last_page</tt> - This is for the "last page" element only.
87
+ # * <tt>disabled_page</tt> - Any element that is a span instead of an a tag.
74
88
  #
75
89
  # === Options
76
90
  #
77
91
  # Please look at per_page_link. All options there are applicable here and are passed onto each option.
78
92
  #
79
- # * <tt>:spread</tt> -- default: 3, how many choices available on each side of the current page
80
- # * <tt>:prev</tt> -- default: <, 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
81
- # * <tt>:next</tt> -- default: >, 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
82
- # * <tt>:first</tt> -- default: <<, 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
83
- # * <tt>:last</tt> -- default: >>, 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
93
+ # * <tt>:spread</tt> -- default: 3, set to nil to show all page, this represents how many choices available on each side of the current page
94
+ # * <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
95
+ # * <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
96
+ # * <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
97
+ # * <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
84
98
  def page_links(options = {})
85
99
  add_page_links_defaults!(options)
100
+ return if options[:last_page] <= 1
86
101
 
87
- current_page = options[:search_obj].page
88
- page_start = 0
89
- page_end = 0
102
+ first_page = 0
103
+ last_page = 0
90
104
  if !options[:spread].blank?
91
- page_start = current_page - options[:spread]
92
- page_start = options[:choices].first unless options[:choices].include?(page_start)
93
- page_end = current_page + options[:spread]
94
- page_end = options[:choices].last unless options[:choices].include?(page_end)
105
+ first_page = options[:current_page] - options[:spread]
106
+ first_page = options[:first_page] if first_page < options[:first_page]
107
+ last_page = options[:current_page] + options[:spread]
108
+ last_page = options[:last_page] if last_page > options[:last_page]
95
109
  else
96
- page_start = options[:choices].first
97
- page_end = options[:choices].last
110
+ first_page = options[:first_page]
111
+ last_page = options[:last_page]
98
112
  end
99
113
 
100
-
101
- link_options = options.dup
102
- [:choices, :spread, :prev, :next, :first, :last].each { |option| link_options.delete(option) }
103
114
  html = ""
104
- (page_start..page_end).each { |choice| html += page_link(choice, link_options.dup) }
115
+ html += span_or_page_link(:first, options.deep_dup, options[:current_page] == options[:first_page]) if options[:first]
116
+ html += span_or_page_link(:prev, options.deep_dup, options[:current_page] == options[:first_page]) if options[:prev]
117
+ (first_page..last_page).each { |page| html += span_or_page_link(page, options.deep_dup, page == options[:current_page]) }
118
+ html += span_or_page_link(:next, options.deep_dup, options[:current_page] == options[:last_page]) if options[:next]
119
+ html += span_or_page_link(:last, options.deep_dup, options[:current_page] == options[:last_page]) if options[:last]
105
120
  html
106
121
  end
107
122
 
@@ -133,14 +148,33 @@ module Searchgasm
133
148
 
134
149
  def add_page_links_defaults!(options)
135
150
  add_searchgasm_helper_defaults!(:page, options)
136
- options[:choices] ||= (1..options[:search_obj].page_count)
137
- options[:spead] ||= 3
138
- options[:prev] ||= "<"
139
- options[:next] ||= ">"
140
- options[:first] ||= "<<"
141
- options[:last] ||= ">>"
151
+ options[:first_page] ||= 1
152
+ options[:last_page] ||= options[:search_obj].page_count
153
+ options[:current_page] ||= options[:search_obj].page
154
+ options[:spread] = 3 unless options.has_key?(:spread)
155
+ options[:prev] = "< Prev" unless options.has_key?(:prev)
156
+ options[:next] = "Next >" unless options.has_key?(:next)
142
157
  options
143
158
  end
159
+
160
+ def span_or_page_link(name, options, span)
161
+ text = ""
162
+ page = 0
163
+ case name
164
+ when Fixnum
165
+ text = name
166
+ page = name
167
+ searchgasm_add_class!(options[:html], "current_page") if span
168
+ else
169
+ text = options[name]
170
+ page = options[:search_obj].send("#{name}_page")
171
+ searchgasm_add_class!(options[:html], "#{name}_page")
172
+ end
173
+
174
+ searchgasm_add_class!(options[:html], "disabled_page") if span
175
+ options[:text] = text
176
+ span ? content_tag(:span, text, options[:html]) : page_link(page, options)
177
+ end
144
178
  end
145
179
  end
146
180
  end
@@ -23,7 +23,7 @@ module Searchgasm
23
23
  # 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.
24
24
  def page_select(options = {})
25
25
  add_page_select_defaults!(options)
26
- searchgasm_state_for(:page, options) + select(options[:params_scope], :page, options[:choices], options[:tag] || {}, options[:html])
26
+ searchgasm_state_for(:page, options) + select(options[:params_scope], :page, (options[:first_page]..options[:last_page]), options[:tag] || {}, options[:html])
27
27
  end
28
28
 
29
29
  private
@@ -4,9 +4,13 @@ module Searchgasm
4
4
  #
5
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
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
+ #
7
11
  # === How it's organized
8
12
  #
9
- # Basically you can do 4 different things in your with with your data:
13
+ # If we break it down, you can do 4 different things with your data in your view:
10
14
  #
11
15
  # 1. Order your data by a single column or an array of columns
12
16
  # 2. Descend or ascend your data
@@ -16,8 +20,9 @@ module Searchgasm
16
20
  # Each one of these actions comes with 3 different types of helpers:
17
21
  #
18
22
  # 1. Link - A single link for a single value. Requires that you pass a value as the first parameter.
19
- # 2. Group of links - A group of single links.
23
+ # 2. Links - A group of single links.
20
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.
21
26
  #
22
27
  # === Examples
23
28
  #
@@ -33,6 +38,17 @@ module Searchgasm
33
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.
34
39
  # => This is just order_by_links as a select form element, nothing fancy
35
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
+ #
36
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
37
53
  # helpers check out their source files. Look at the sub modules under this one (Ex: Searchgasm::Helpers::ControlTypes::Select)
38
54
  module ControlTypes
@@ -19,13 +19,14 @@ module Searchgasm
19
19
  #
20
20
  # If you pass a Searchgasm::Search::Base object it automatically adds the :order_by, :order_as, and :per_page hidden fields. This is done so that when someone
21
21
  # creates a new search, their options are remembered. It keeps the search consisten and is much more user friendly. If you want to override this you can pass the
22
- # following options. Or you can set this up in your configuration, see Searchgasm::Config for more details.
22
+ # following options or you can set this up in your configuration, see Searchgasm::Config for more details.
23
+ #
24
+ # Lastly some light javascript is added to the "onsubmit" action. You will notice the order_by, per_page, and page helpers also add in a single hidden tag in the page. The form
25
+ # finds these elements, gets their values and updates its hidden fields so that the correct values will be submitted during the search. The end result is having the "ordering" and "per page" options remembered.
23
26
  #
24
27
  # === Options
25
28
  #
26
29
  # * <tt>:hidden_fields</tt> --- Array, a list of hidden fields to include. Defaults to [:order_by, :order_as, :per_page]. Pass false, nil, or a blank array to not include any.
27
- # * <tt>:js_lib</tt> --- Accepts :prototype, :jquery, or nil. Javascript is written to keep the :hidden_fields in sync with the other fields on the page. nil will turn javascript off, you will be on your own.
28
- # Keeping these fields in sync allows search to remember their values when they are changed, making search much more user friendly.
29
30
  module Form
30
31
  module Shared # :nodoc:
31
32
  private
@@ -34,9 +35,14 @@ module Searchgasm
34
35
  end
35
36
 
36
37
  def find_searchgasm_object(args)
38
+ search_object = nil
39
+
37
40
  case args.first
38
41
  when String, Symbol
39
- search_object = searchgasm_object?(args[1]) ? args[1] : instance_variable_get("@#{args.first}")
42
+ begin
43
+ search_object = searchgasm_object?(args[1]) ? args[1] : instance_variable_get("@#{args.first}")
44
+ rescue Exception
45
+ end
40
46
  when Array
41
47
  search_object = args.first.last
42
48
  else
@@ -8,7 +8,8 @@ module Searchgasm
8
8
  options[:search_obj] ||= instance_variable_get("@#{options[:params_scope]}")
9
9
  raise(ArgumentError, "@search object could not be inferred, please specify: :search_obj => @search") unless options[:search_obj].is_a?(Searchgasm::Search::Base)
10
10
  options[:html] ||= {}
11
- options[:html][:class] = (options[:html][:class].blank? ? "" : "#{options[:html][:class]} ") + option.to_s
11
+ options[:html][:class] ||= ""
12
+ searchgasm_add_class!(options[:html], option)
12
13
  options
13
14
  end
14
15
 
@@ -17,12 +18,13 @@ module Searchgasm
17
18
  end
18
19
 
19
20
  def searchgasm_url_hash(option, value, options)
20
- params_copy = params.dup
21
+ params_copy = params.deep_dup.with_indifferent_access
21
22
  params_copy.delete(:commit)
22
23
 
23
24
  # Extract search params from params
24
25
  search_params = options[:params_scope].blank? ? params_copy : params_copy[options[:params_scope]]
25
26
  search_params ||= {}
27
+ search_params = search_params.with_indifferent_access
26
28
 
27
29
  # Never want to keep page
28
30
  search_params.delete(:page)
@@ -33,6 +35,13 @@ module Searchgasm
33
35
  search_params
34
36
  end
35
37
 
38
+ def searchgasm_add_class!(html_options, new_class)
39
+ new_class = new_class.to_s
40
+ classes = html_options[:class].split(" ")
41
+ classes << new_class unless classes.include?(new_class)
42
+ html_options[:class] = classes.join(" ")
43
+ end
44
+
36
45
  def searchgasm_order_by_value(order_by)
37
46
  case order_by
38
47
  when String
@@ -52,6 +61,22 @@ module Searchgasm
52
61
  end
53
62
  html
54
63
  end
64
+
65
+ # Need to deep dup a hash otherwise the "child" hashes get modified as its passed around
66
+ def searchgasm_deep_dup(hash)
67
+ new_hash = {}
68
+
69
+ hash.each do |k, v|
70
+ case v
71
+ when Hash
72
+ hash[k] = searchgasm_deep_dup(v)
73
+ else
74
+ hew_hash[k] = v
75
+ end
76
+ end
77
+
78
+ new_hash
79
+ end
55
80
  end
56
81
  end
57
82
  end
@@ -1,3 +1,9 @@
1
1
  module Searchgasm
2
- module Helpers #:nodoc:
2
+ # = Searchgasm Helpers
3
+ #
4
+ # Provides helpers for rails applications that make using Searchgasm extremely easy. Please see Searchgasm::Helpers::ControlTypes and Searchgasm::Helpers::Form for more information on how to use these helpers.
5
+ #
6
+ # Also, I am always looking to improve these. I feel confident that I made these flexible enough for just about any need, but there is always that one crazy instance. If these helpers restrict you
7
+ # in any way please contact me and let me know. You can do some on my website: www.binarylogic.com. I will more than likely add in your functionality that week.
8
+ module Helpers
3
9
  module UtilitiesHelper # :nodoc:
@@ -16,20 +16,43 @@ module Searchgasm #:nodoc:
16
16
  # Use these methods just like you would in ActiveRecord
17
17
  SEARCH_METHODS = [:all, :average, :calculate, :count, :find, :first, :maximum, :minimum, :sum]
18
18
 
19
- attr_accessor :klass, *::ActiveRecord::Base.valid_find_options
19
+ attr_accessor *::ActiveRecord::Base.valid_find_options
20
20
 
21
- # Used in the ActiveRecord methods to determine if Searchgasm should get involved or not.
22
- # This keeps Searchgasm out of the way unless it is needed.
23
- def self.needed?(klass, options)
24
- SPECIAL_FIND_OPTIONS.each do |option|
25
- return true if options.symbolize_keys.keys.include?(option)
21
+ class << self
22
+ # Used in the ActiveRecord methods to determine if Searchgasm should get involved or not.
23
+ # This keeps Searchgasm out of the way unless it is needed.
24
+ def needed?(model_class, options)
25
+ SPECIAL_FIND_OPTIONS.each do |option|
26
+ return true if options.symbolize_keys.keys.include?(option)
27
+ end
28
+
29
+ Searchgasm::Conditions::Base.needed?(model_class, options[:conditions])
30
+ end
31
+
32
+ # Creates virtual classes for the class passed to it. This is a neccesity for keeping dynamically created method
33
+ # names specific to models. It provides caching and helps a lot with performance.
34
+ def create_virtual_class(model_class)
35
+ class_search_name = "::#{model_class.name}Search"
36
+
37
+ begin
38
+ class_search_name.constantize
39
+ rescue NameError
40
+ eval <<-end_eval
41
+ class #{class_search_name} < ::Searchgasm::Search::Base; end;
42
+ end_eval
43
+
44
+ class_search_name.constantize
45
+ end
26
46
  end
27
47
 
28
- Searchgasm::Conditions::Base.needed?(klass, options[:conditions])
48
+ # The class / model we are searching
49
+ def klass
50
+ # Can't cache this because thin and mongrel don't play nice with caching constants
51
+ name.split("::").last.gsub(/Search$/, "").constantize
52
+ end
29
53
  end
30
54
 
31
- def initialize(klass, init_options = {})
32
- self.klass = klass
55
+ def initialize(init_options = {})
33
56
  self.options = init_options
34
57
  end
35
58
 
@@ -44,11 +67,17 @@ module Searchgasm #:nodoc:
44
67
  end_eval
45
68
  end
46
69
 
70
+ # Makes using searchgasm in the console less annoying and keeps the output meaningful and useful
47
71
  def inspect
48
72
  options_as_nice_string = ::ActiveRecord::Base.valid_find_options.collect { |name| "#{name}: #{send(name)}" }.join(", ")
49
73
  "#<#{klass} #{options_as_nice_string}>"
50
74
  end
51
75
 
76
+ # Convenience method for self.class.klass
77
+ def klass
78
+ self.class.klass
79
+ end
80
+
52
81
  def limit=(value)
53
82
  @limit = value.blank? || value == 0 ? nil : value.to_i
54
83
  end
@@ -1,5 +1,9 @@
1
1
  module Searchgasm
2
2
  module Search
3
+ # = Searchgasm Conditions
4
+ #
5
+ # Implements all of the conditions functionality into a searchgasm search. All of this functonality is extracted out into its own class Searchgasm::Conditions::Base. This is a separate module to help keep the code
6
+ # clean and organized.
3
7
  module Conditions
4
8
  def self.included(klass)
5
9
  klass.class_eval do
@@ -10,12 +14,24 @@ module Searchgasm
10
14
  end
11
15
  end
12
16
 
13
- def initialize_with_conditions(klass, init_options = {})
14
- self.conditions = Searchgasm::Conditions::Base.new(klass)
15
- initialize_without_conditions(klass, init_options)
17
+ def initialize_with_conditions(init_options = {})
18
+ self.conditions = Searchgasm::Conditions::Base.create_virtual_class(klass).new
19
+ initialize_without_conditions(init_options)
16
20
  end
17
21
 
18
22
  # Sets conditions on the search. Accepts a hash or a Searchgasm::Conditions::Base object.
23
+ #
24
+ # === Examples
25
+ #
26
+ # search.conditions = {:first_name_like => "Ben"}
27
+ # search.conditions = User.new_conditions
28
+ #
29
+ # or to set a scope
30
+ #
31
+ # search.conditions = "user_group_id = 6"
32
+ #
33
+ # now you can create the rest of your search and your "scope" will get merged into your final SQL.
34
+ # What this does is determine if the value a hash or a conditions object, if not it sets it up as a scope.
19
35
  def conditions_with_conditions=(values)
20
36
  case values
21
37
  when Searchgasm::Conditions::Base
@@ -25,22 +41,26 @@ module Searchgasm
25
41
  end
26
42
  end
27
43
 
44
+ # Tells searchgasm was relationships to include during the search. This is based on what conditions you set.
45
+ #
46
+ # <b>Be careful!</b>
47
+ # ActiveRecord associations can be an SQL train wreck. Make sure you think about what you are searching and that you aren't joining a table with a million records.
28
48
  def include_with_conditions
29
49
  includes = [include_without_conditions, conditions.includes].flatten.compact.uniq
30
50
  includes.blank? ? nil : (includes.size == 1 ? includes.first : includes)
31
51
  end
32
52
 
33
- def sanitize_with_conditions(for_method = nil)
53
+ def sanitize_with_conditions(for_method = nil) # :nodoc:
34
54
  find_options = sanitize_without_conditions(for_method)
35
55
  find_options[:conditions] = find_options[:conditions].sanitize if find_options[:conditions]
36
56
  find_options
37
57
  end
38
58
 
39
- def scope
59
+ def scope # :nodoc:
40
60
  conditions.scope
41
61
  end
42
62
 
43
- def scope=(value)
63
+ def scope=(value) # :nodoc:
44
64
  conditions.scope = value
45
65
  end
46
66
  end
@@ -3,7 +3,18 @@ module Searchgasm
3
3
  # = Search Ordering
4
4
  #
5
5
  # The purpose of this module is to provide easy ordering for your searches. All that these options do is
6
- # build :order for you. This plays a huge part in ordering your data on the interface. See Searchgasm::Helpers::Search for more information.
6
+ # build :order for you. This plays a huge part in ordering your data on the interface. See the options and examples below. The readme also touches on ordering. It's pretty simple thought:
7
+ #
8
+ # === Examples
9
+ #
10
+ # search.order_by = :id
11
+ # search.order_by = [:id, :first_name]
12
+ # search.order_by = {:user_group => :name}
13
+ # search.order_by = [:id, {:user_group => :name}]
14
+ # search.order_by = {:user_group => {:account => :name}} # you can traverse through all of your relationships
15
+ #
16
+ # search.order_as = "DESC"
17
+ # search.order_as = "ASC"
7
18
  module Ordering
8
19
  def self.included(klass)
9
20
  klass.class_eval do
@@ -12,12 +23,12 @@ module Searchgasm
12
23
  end
13
24
  end
14
25
 
15
- def include_with_ordering
26
+ def include_with_ordering # :nodoc:
16
27
  includes = [include_without_ordering, order_by_includes].flatten.compact.uniq
17
28
  includes.blank? ? nil : (includes.size == 1 ? includes.first : includes)
18
29
  end
19
30
 
20
- def order_with_ordering=(value)
31
+ def order_with_ordering=(value) # :nodoc
21
32
  @order_by = nil
22
33
  self.order_without_ordering = value
23
34
  end
@@ -1,5 +1,8 @@
1
1
  module Searchgasm
2
2
  module Search
3
+ # = Searchgasm Pagination
4
+ #
5
+ # Adds in pagination functionality to searchgasm
3
6
  module Pagination
4
7
  def self.included(klass)
5
8
  klass.class_eval do
@@ -10,23 +13,26 @@ module Searchgasm
10
13
  end
11
14
  end
12
15
 
13
- def limit_with_pagination=(value)
16
+ def limit_with_pagination=(value) # :nodoc:
14
17
  r_value = self.limit_without_pagination = value
15
18
  self.page = @page unless @page.nil? # retry page now that the limit has changed
16
19
  r_value
17
20
  end
18
21
 
19
- def offset_with_pagination=(value)
22
+ def offset_with_pagination=(value) #:nodoc
20
23
  r_value = self.offset_without_pagination = value
21
24
  @page = nil
22
25
  r_value
23
26
  end
24
27
 
28
+ # The current page that the search is on
25
29
  def page
26
30
  return 1 if offset.blank? || limit.blank?
27
31
  (offset.to_f / limit).floor + 1
28
32
  end
33
+ alias_method :current_page, :page
29
34
 
35
+ # Lets you change the page for the next search
30
36
  def page=(value)
31
37
  # Have to use @offset, since self.offset= resets @page
32
38
  if value.nil?
@@ -46,6 +52,7 @@ module Searchgasm
46
52
  value
47
53
  end
48
54
 
55
+ # The total number of pages in your next search
49
56
  def page_count
50
57
  return 1 if per_page.blank? || per_page <= 0
51
58
  # Letting AR caching kick in with the count query
@@ -53,15 +60,53 @@ module Searchgasm
53
60
  end
54
61
  alias_method :page_total, :page_count
55
62
 
56
- def next_page!
57
- raise("You are on the last page") if page == page_count
58
- self.page += 1
63
+ # Always returns 1, this is a convenience method
64
+ def first_page
65
+ 1
66
+ end
67
+
68
+ # Changes the page to 1 and then runs the "all" search. What's different about this method is that it does not raise an exception if you are on the first page. Unlike prev_page! and next_page!
69
+ # I don't think an exception raised is warranted, because you are expecting the same results each time it is ran.
70
+ def first_page!
71
+ self.page = first_page
59
72
  all
60
73
  end
61
74
 
75
+ # Changes the page to the page - 1
76
+ def prev_page
77
+ self.page - 1
78
+ end
79
+
80
+ # Changes the page to page - 1 and runs the "all" search. Be careful with this method because if you are on the first page an exception is raised telling you that you are on the first page.
81
+ # I thought about just running the first page search again, but that seems confusing and unexpected.
62
82
  def prev_page!
63
- raise("You are on the first page") if page == 1
64
- self.page -= 1
83
+ raise("You are on the first page") if page == first_page
84
+ self.page = prev_page
85
+ all
86
+ end
87
+
88
+ # Change the page to page + 1
89
+ def next_page
90
+ self.page + 1
91
+ end
92
+
93
+ # Changes the page to page + 1 and calls the "all" method. Be careful with this method because if you are on the last page an exception is raised telling you that you are on the last page.
94
+ # I thought about just running the lat page search again, but that seems confusing and unexpected.
95
+ def next_page!
96
+ raise("You are on the last page") if page == last_page
97
+ self.page = next_page
98
+ all
99
+ end
100
+
101
+ # Always returns the page_count, this is a convenience method
102
+ def last_page
103
+ page_count
104
+ end
105
+
106
+ # Changes the page to the last page and runs the "all" search. What's different about this method is that it does not raise an exception if you are on the last page. Unlike prev_page! and next_page!
107
+ # I don't think an exception raised is warranted, because you are expecting the same results each time it is ran.
108
+ def last_page!
109
+ self.page = last_page
65
110
  all
66
111
  end
67
112
  end
@@ -1,5 +1,25 @@
1
1
  module Searchgasm
2
2
  module Search
3
+ # = Searchgasm Protection
4
+ #
5
+ # This adds protection during mass asignments *only*. This allows you to pass a params object when doing mass assignments and not have to worry about Billy 13 year old adding in SQL injections.
6
+ # There is a section in the readme that covers protection but to reiterate:
7
+ #
8
+ # === Protected
9
+ #
10
+ # User.new_search(params[:search])
11
+ # User.new_conditions(params[:search])
12
+ #
13
+ # search.options = params[:search]
14
+ # conditions.conditions = params[:conditions]
15
+ #
16
+ # === NOT Protected
17
+ #
18
+ # User.new_search!(params[:search])
19
+ # User.new_conditions!(params[:search])
20
+ # User.find(:all, params[:search])
21
+ # User.first(params[:search])
22
+ # User.all(params[:search])
3
23
  module Protection
4
24
  # Options that are allowed when protecting against SQL injections (still checked though)
5
25
  SAFE_OPTIONS = Base::SPECIAL_FIND_OPTIONS + [:conditions, :limit, :offset]
@@ -16,28 +36,30 @@ module Searchgasm
16
36
  end
17
37
  end
18
38
 
19
- def limit_with_protection
39
+ def limit_with_protection # :nodoc:
20
40
  return Config.per_page if protected? && !@set_limit
21
41
  limit_without_protection
22
42
  end
23
43
 
24
- def limit_with_protection=(value)
44
+ def limit_with_protection=(value) # :nodoc:
25
45
  @set_limit = true
26
46
  self.limit_without_protection = value
27
47
  end
28
48
 
29
- def options_with_protection=(values)
49
+ def options_with_protection=(values) # :nodoc:
30
50
  return unless values.is_a?(Hash)
31
51
  self.protect = values.delete(:protect) if values.has_key?(:protect) # make sure we do this first
32
52
  frisk!(values) if protect?
33
53
  self.options_without_protection = values
34
54
  end
35
55
 
56
+ # Accepts a boolean. Will protect mass assignemnts if set to true, and unprotect mass assignments if set to false
36
57
  def protect=(value)
37
58
  conditions.protect = value
38
59
  @protect = value
39
60
  end
40
61
 
62
+ # Convenience methof for determing if the search is protected or not.
41
63
  def protect?
42
64
  protect == true
43
65
  end
@@ -67,7 +67,7 @@ module Searchgasm
67
67
 
68
68
  MAJOR = 1
69
69
  MINOR = 0
70
- TINY = 0
70
+ TINY = 1
71
71
 
72
72
  # The current version as a Version instance
73
73
  CURRENT = new(MAJOR, MINOR, TINY)