searchgasm 1.0.4 → 1.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.rdoc CHANGED
@@ -1,3 +1,9 @@
1
+ == 1.1.0 released 2008-09-18
2
+
3
+ * Added the options :inner_spread and :outer_spread to the page_links helper. Also added various config options for setting page_links defaults.
4
+ * Updated calculation methods to ignore :limit and :offset. AR returns 0 or nil on calculations that provide an offset.
5
+ * Added support to allow for "any" of the conditions, instead of all of them. Joins conditions with "or" instead of "and". See Searchgasm::Conditions::Base or the readme
6
+
1
7
  == 1.0.4 released 2008-09-18
2
8
 
3
9
  * Fixed bugs when performing calculations and searches on has_many through relationships.
data/README.rdoc CHANGED
@@ -189,6 +189,16 @@ Don't need pagination, ordering, or any of the other options? Search with condit
189
189
  Pass a conditions object right into ActiveRecord:
190
190
 
191
191
  User.all(:conditions => conditions)
192
+
193
+ == Match ANY or ALL of the conditions
194
+
195
+ As you saw above, the nice thing about Searchgasm is it's integration with forms. I designed the "any" option so that forms can set this as well, just like a condition.
196
+
197
+ search = User.new_search(:conditions => {:age_gt => 18})
198
+ search.conditions.first_name_contains = "Ben"
199
+ search.conditions.any = true # can set this to "true" or "1" or "yes"
200
+ search.all # will join all conditions with "or" instead of "and"
201
+ # ... all operations above are available
192
202
 
193
203
  == Scoped searching
194
204
 
@@ -9,7 +9,7 @@ module Searchgasm
9
9
  include Searchgasm::Shared::Searching
10
10
  include Searchgasm::Shared::VirtualClasses
11
11
 
12
- attr_accessor :relationship_name, :sql
12
+ attr_accessor :any, :relationship_name, :sql
13
13
 
14
14
  class << self
15
15
  attr_accessor :added_klass_conditions, :added_column_conditions, :added_associations
@@ -66,6 +66,7 @@ module Searchgasm
66
66
 
67
67
  def needed?(model_class, conditions) # :nodoc:
68
68
  if conditions.is_a?(Hash)
69
+ return true if conditions[:any]
69
70
  column_names = model_class.column_names
70
71
  conditions.stringify_keys.keys.each do |condition|
71
72
  return true unless column_names.include?(condition)
@@ -83,10 +84,31 @@ module Searchgasm
83
84
  self.conditions = init_conditions
84
85
  end
85
86
 
87
+ # Determines if we should join the conditions with "AND" or "OR".
88
+ #
89
+ # === Examples
90
+ #
91
+ # search.conditions.any = true # will join all conditions with "or", you can also set this to "true", "1", or "yes"
92
+ # search.conditions.any = false # will join all conditions with "and"
93
+ def any=(value)
94
+ associations.each { |association| association.any = value }
95
+ @any = value
96
+ end
97
+
98
+ def any # :nodoc:
99
+ any?
100
+ end
101
+
102
+ # Convenience method for determining if we should join the conditions with "AND" or "OR".
103
+ def any?
104
+ @any == true || @any == "true" || @any == "1" || @any == "yes"
105
+ end
106
+
86
107
  # A list of includes to use when searching, includes relationships
87
108
  def includes
88
109
  i = []
89
110
  associations.each do |association|
111
+ next if association.conditions.blank?
90
112
  association_includes = association.includes
91
113
  i << (association_includes.blank? ? association.relationship_name.to_sym : {association.relationship_name.to_sym => association_includes})
92
114
  end
@@ -101,10 +123,10 @@ module Searchgasm
101
123
  end
102
124
 
103
125
  # Sanitizes the conditions down into conditions that ActiveRecord::Base.find can understand.
104
- def sanitize(any = false)
105
- conditions = merge_conditions(*objects.collect { |object| object.sanitize })
126
+ def sanitize
127
+ conditions = merge_conditions(*(objects.collect { |object| object.sanitize } << {:any => any}))
106
128
  return sql if conditions.blank?
107
- merged_conditions = merge_conditions(conditions, sql)
129
+ merged_conditions = merge_conditions(conditions, sql, :any => any)
108
130
  merged_conditions
109
131
  end
110
132
 
@@ -123,8 +145,7 @@ module Searchgasm
123
145
  def conditions
124
146
  conditions_hash = {}
125
147
  objects.each do |object|
126
- case object
127
- when self.class
148
+ if object.class < Searchgasm::Conditions::Base
128
149
  relationship_conditions = object.conditions
129
150
  next if relationship_conditions.blank?
130
151
  conditions_hash[object.relationship_name.to_sym] = relationship_conditions
@@ -218,7 +239,7 @@ module Searchgasm
218
239
  end
219
240
 
220
241
  def assert_valid_conditions(conditions)
221
- conditions.stringify_keys.fast_assert_valid_keys(self.class.condition_names + self.class.association_names)
242
+ conditions.stringify_keys.fast_assert_valid_keys(self.class.condition_names + self.class.association_names + ["any"])
222
243
  end
223
244
 
224
245
  def associations
@@ -43,6 +43,78 @@ module Searchgasm
43
43
  @desc_indicator = value
44
44
  end
45
45
 
46
+ def page_links_first # :nodoc:
47
+ @page_links_first
48
+ end
49
+
50
+ # The default for the :first option for the page_links helper.
51
+ #
52
+ # * <tt>Default:</tt> nil
53
+ # * <tt>Accepts:</tt> Anything you want, text, html, etc. nil to disable
54
+ def page_links_first=(value)
55
+ @page_links_first = value
56
+ end
57
+
58
+ def page_links_last # :nodoc:
59
+ @page_links_last
60
+ end
61
+
62
+ # The default for the :last option for the page_links helper.
63
+ #
64
+ # * <tt>Default:</tt> nil
65
+ # * <tt>Accepts:</tt> Anything you want, text, html, etc. nil to disable
66
+ def page_links_last=(value)
67
+ @page_links_last = value
68
+ end
69
+
70
+ def page_links_inner_spread # :nodoc:
71
+ @page_links_inner_spread ||= 3
72
+ end
73
+
74
+ # The default for the :inner_spread option for the page_links helper.
75
+ #
76
+ # * <tt>Default:</tt> 3
77
+ # * <tt>Accepts:</tt> Any integer >= 1, set to nil to show all pages
78
+ def page_links_inner_spread=(value)
79
+ @page_links_inner_spread = value
80
+ end
81
+
82
+ def page_links_outer_spread # :nodoc:
83
+ @page_links_outer_spread ||= 2
84
+ end
85
+
86
+ # The default for the :outer_spread option for the page_links helper.
87
+ #
88
+ # * <tt>Default:</tt> 2
89
+ # * <tt>Accepts:</tt> Any integer >= 1, set to nil to display, 0 to only show the "..." separator
90
+ def page_links_outer_spread=(value)
91
+ @page_links_outer_spread = value
92
+ end
93
+
94
+ def page_links_next # :nodoc:
95
+ @page_links_next ||= "< Next"
96
+ end
97
+
98
+ # The default for the :next option for the page_links helper.
99
+ #
100
+ # * <tt>Default:</tt> "< Next"
101
+ # * <tt>Accepts:</tt> Anything you want, text, html, etc. nil to disable
102
+ def page_links_next=(value)
103
+ @page_links_next = value
104
+ end
105
+
106
+ def page_links_prev # :nodoc:
107
+ @page_links_prev ||= "< Prev"
108
+ end
109
+
110
+ # The default for the :prev option for the page_links helper.
111
+ #
112
+ # * <tt>Default:</tt> "< Prev"
113
+ # * <tt>Accepts:</tt> Anything you want, text, html, etc. nil to disable
114
+ def page_links_prev=(value)
115
+ @page_links_prev = value
116
+ end
117
+
46
118
  def per_page # :nodoc:
47
119
  @per_page
48
120
  end
@@ -1,9 +1,6 @@
1
1
  module Searchgasm
2
2
  module Helpers
3
3
  module ControlTypes
4
- # = Links Control Types
5
- #
6
- # These helpers create a group of links to help navigate through search data.
7
4
  module Links
8
5
  # Creates a group of links that order the data by a column or columns. All that this does is loop through the :choices option and call order_by_link and then glue it all together.
9
6
  #
@@ -14,7 +11,7 @@ module Searchgasm
14
11
  #
15
12
  # === Options
16
13
  #
17
- # Please look at order_by_link. All options there are applicable here and are passed onto each option. Here are the options specific to this method:
14
+ # Please look at order_by_link. All options there are applicable here and are passed onto each option.
18
15
  #
19
16
  # * <tt>:choices</tt> -- default: the models column names, the choices to loop through when calling order_by_link
20
17
  def order_by_links(options = {})
@@ -35,7 +32,7 @@ module Searchgasm
35
32
  #
36
33
  # === Options
37
34
  #
38
- # Please look at order_as_link. All options there are applicable here and are passed onto each option. Here are the options specific to this method:
35
+ # Please look at order_as_link. All options there are applicable here and are passed onto each option.
39
36
  #
40
37
  # * <tt>:choices</tt> -- default: ["asc", "desc"], the choices to loop through when calling order_as_link
41
38
  def order_as_links(options = {})
@@ -56,7 +53,7 @@ module Searchgasm
56
53
  #
57
54
  # === Options
58
55
  #
59
- # Please look at per_page_link. All options there are applicable here and are passed onto each option. Here are the options specific to this method:
56
+ # Please look at per_page_link. All options there are applicable here and are passed onto each option.
60
57
  #
61
58
  # * <tt>:choices</tt> -- default: [10, 25, 50, 100, 150, 200, nil], the choices to loop through when calling per_page_link.
62
59
  def per_page_links(options = {})
@@ -75,7 +72,7 @@ module Searchgasm
75
72
  # page_links
76
73
  # page_links(:first => "<< First", :last => "Last >>")
77
74
  #
78
- # === Classes and tag
75
+ # === Classes and tags
79
76
  #
80
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
81
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
@@ -83,7 +80,7 @@ module Searchgasm
83
80
  #
84
81
  # * <tt>page</tt> - This is in *every* element, span or a.
85
82
  # * <tt>first_page</tt> - This is for the "first page" element only.
86
- # * <tt>preve_page</tt> - This is for the "prev page" element only.
83
+ # * <tt>prev_page</tt> - This is for the "prev page" element only.
87
84
  # * <tt>current_page</tt> - This is for the current page element
88
85
  # * <tt>next_page</tt> - This is for the "next page" element only.
89
86
  # * <tt>last_page</tt> - This is for the "last page" element only.
@@ -91,33 +88,64 @@ module Searchgasm
91
88
  #
92
89
  # === Options
93
90
  #
94
- # Please look at per_page_link. All options there are applicable here and are passed onto each option. Here are the options specific to this method:
95
- #
96
- # * <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
97
- # * <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
98
- # * <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
91
+ # Please look at per_page_link. All options there are applicable here and are passed onto each option.
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
99
102
  # * <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
100
103
  # * <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
101
104
  def page_links(options = {})
102
105
  add_page_links_defaults!(options)
103
106
  return if options[:last_page] <= 1
104
107
 
105
- first_page = 0
106
- last_page = 0
107
- if !options[:spread].blank?
108
- first_page = options[:current_page] - options[:spread]
109
- first_page = options[:first_page] if first_page < options[:first_page]
110
- last_page = options[:current_page] + options[:spread]
111
- last_page = options[:last_page] if last_page > options[:last_page]
108
+ inner_spread_start = inner_spread_end = lower_gap = lower_outer_spread_start = lower_outer_spread_end = upper_gap = upper_outer_spread_start = upper_outer_spread_end = 0
109
+ if !options[:inner_spread].blank?
110
+ inner_spread_start = options[:current_page] - options[:inner_spread]
111
+ inner_spread_start = options[:first_page] if inner_spread_start < options[:first_page]
112
+ inner_spread_end = options[:current_page] + options[:inner_spread]
113
+ inner_spread_end = options[:last_page] if inner_spread_end > options[:last_page]
114
+
115
+ if !options[:outer_spread].blank?
116
+ lower_gap = inner_spread_start - options[:first_page]
117
+ if lower_gap > 0
118
+ lower_outer_spread_start = options[:first_page]
119
+ lower_outer_spread_end = options[:outer_spread] > lower_gap ? lower_gap : options[:outer_spread]
120
+ end
121
+
122
+ upper_gap = options[:last_page] - inner_spread_end
123
+ if upper_gap > 0
124
+ upper_outer_spread_start = options[:last_page] - (options[:outer_spread] > upper_gap ? upper_gap : options[:outer_spread]) + 1
125
+ upper_outer_spread_end = options[:last_page]
126
+ end
127
+ end
112
128
  else
113
- first_page = options[:first_page]
114
- last_page = options[:last_page]
129
+ inner_spread_start = options[:first_page]
130
+ inner_spread_end = options[:last_page]
115
131
  end
116
132
 
117
133
  html = ""
118
134
  html += span_or_page_link(:first, options.deep_dup, options[:current_page] == options[:first_page]) if options[:first]
119
135
  html += span_or_page_link(:prev, options.deep_dup, options[:current_page] == options[:first_page]) if options[:prev]
120
- (first_page..last_page).each { |page| html += span_or_page_link(page, options.deep_dup, page == options[:current_page]) }
136
+
137
+ if lower_gap > 0
138
+ (lower_outer_spread_start..lower_outer_spread_end).each { |page| html += span_or_page_link(page, options.deep_dup, false) }
139
+ html += content_tag(:span, "&hellip;", options[:html]) if (inner_spread_start - lower_outer_spread_end) > 1
140
+ end
141
+
142
+ (inner_spread_start..inner_spread_end).each { |page| html += span_or_page_link(page, options.deep_dup, page == options[:current_page]) }
143
+
144
+ if upper_gap > 0
145
+ html += content_tag(:span, "&hellip;", options[:html]) if (upper_outer_spread_start - inner_spread_end) > 1
146
+ (upper_outer_spread_start..upper_outer_spread_end).each { |page| html += span_or_page_link(page, options.deep_dup, false) }
147
+ end
148
+
121
149
  html += span_or_page_link(:next, options.deep_dup, options[:current_page] == options[:last_page]) if options[:next]
122
150
  html += span_or_page_link(:last, options.deep_dup, options[:current_page] == options[:last_page]) if options[:last]
123
151
  html
@@ -154,9 +182,12 @@ module Searchgasm
154
182
  options[:first_page] ||= 1
155
183
  options[:last_page] ||= options[:search_obj].page_count
156
184
  options[:current_page] ||= options[:search_obj].page
157
- options[:spread] = 3 unless options.has_key?(:spread)
158
- options[:prev] = "< Prev" unless options.has_key?(:prev)
159
- options[:next] = "Next >" unless options.has_key?(:next)
185
+ options[:inner_spread] = Config.page_links_inner_spread unless options.has_key?(:inner_spread)
186
+ options[:outer_spread] = Config.page_links_outer_spread unless options.has_key?(:outer_spread)
187
+ options[:prev] = Config.page_links_prev unless options.has_key?(:prev)
188
+ options[:next] = Config.page_links_next unless options.has_key?(:next)
189
+ options[:first] = Config.page_links_first unless options.has_key?(:first)
190
+ options[:last] = Config.page_links_last unless options.has_key?(:last)
160
191
  options
161
192
  end
162
193
 
@@ -30,6 +30,8 @@ module Searchgasm
30
30
  klass.send(:with_scope, :find => options) do
31
31
  find_options = (self.class < Searchgasm::Conditions::Base ? {:conditions => sanitize} : sanitize)
32
32
  find_options.delete(:select)
33
+ find_options.delete(:limit)
34
+ find_options.delete(:offset)
33
35
  args << find_options
34
36
  klass.#{method}(*args)
35
37
  end
@@ -66,8 +66,8 @@ module Searchgasm
66
66
  end
67
67
 
68
68
  MAJOR = 1
69
- MINOR = 0
70
- TINY = 4
69
+ MINOR = 1
70
+ TINY = 0
71
71
 
72
72
  # The current version as a Version instance
73
73
  CURRENT = new(MAJOR, MINOR, TINY)
data/searchgasm.gemspec CHANGED
@@ -1,11 +1,11 @@
1
1
 
2
- # Gem::Specification for Searchgasm-1.0.4
2
+ # Gem::Specification for Searchgasm-1.1.0
3
3
  # Originally generated by Echoe
4
4
 
5
5
  --- !ruby/object:Gem::Specification
6
6
  name: searchgasm
7
7
  version: !ruby/object:Gem::Version
8
- version: 1.0.4
8
+ version: 1.1.0
9
9
  platform: ruby
10
10
  authors:
11
11
  - Ben Johnson of Binary Logic
@@ -84,5 +84,11 @@ class TestActiveRecordBase < Test::Unit::TestCase
84
84
  def test_count
85
85
  assert_equal 3, Account.count
86
86
  assert_equal 3, Account.count(:limit => 1)
87
+ assert_equal 0, Account.count(:limit => 1, :offset => 1) # not sure why AR doesn't ignore offset like it does for limit
88
+ search = Account.new_search
89
+ assert_equal 3, search.count
90
+ search.per_page = 10
91
+ search.page = 10
92
+ assert_equal 3, search.count
87
93
  end
88
94
  end
@@ -173,4 +173,16 @@ class TestConditionsBase < Test::Unit::TestCase
173
173
  assert_equal 1, conditions.minimum('id')
174
174
  assert_equal 4, conditions.sum('id')
175
175
  end
176
+
177
+ def test_any
178
+ conditions = Account.new_conditions
179
+ conditions.name_contains = "Binary"
180
+ assert_equal Account.find(1, 3), conditions.all
181
+ conditions.id = 1
182
+ assert_equal [Account.find(1)], conditions.all
183
+ conditions.any = true
184
+ assert_equal Account.find(1, 3), conditions.all
185
+ conditions.any = false
186
+ assert_equal [Account.find(1)], conditions.all
187
+ end
176
188
  end
@@ -67,6 +67,17 @@ class TestSearchPagination < Test::Unit::TestCase
67
67
  end
68
68
 
69
69
  def test_page_count
70
+ search = Account.new_search
71
+ assert_equal 1, search.page_count
72
+ search.per_page = 1
73
+ assert_equal 3, search.page_count
74
+ search.per_page = 100
75
+ assert_equal 1, search.page_count
70
76
 
77
+ Searchgasm::Config.per_page = 1
78
+ search = Account.new_search
79
+ assert_equal 3, search.page_count
80
+ search.conditions.users.first_name_contains
81
+ assert_equal 3, search.page_count
71
82
  end
72
83
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: searchgasm
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.4
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ben Johnson of Binary Logic