shingara-will_paginate 2.3.3 → 2.3.10

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,19 @@
1
+ == 2.3.6, released 2008-10-26
2
+
3
+ * Rails 2.2 fix: stop using `extract_attribute_names_from_match` inernal AR method, it no longer exists
4
+
5
+ == 2.3.5, released 2008-10-07
6
+
7
+ * update the backported named_scope implementation for Rails versions older than 2.1
8
+ * break out of scope of paginated_each() yielded block when used on named scopes
9
+ * fix paginate(:from)
10
+
11
+ == 2.3.4, released 2008-09-16
12
+
13
+ * Removed gem dependency to Active Support (causes trouble with vendored rails).
14
+ * Rails 2.1: fix a failing test and a deprecation warning.
15
+ * Cope with scoped :select when counting.
16
+
1
17
  == 2.3.3, released 2008-08-29
2
18
 
3
19
  * Ensure that paginate_by_sql doesn't change the original SQL query.
@@ -87,7 +87,8 @@ contributions or just simply awesome ideas:
87
87
  Chris Wanstrath, Dr. Nic Williams, K. Adam Christensen, Mike Garey, Bence
88
88
  Golda, Matt Aimonetti, Charles Brian Quinn, Desi McAdam, James Coglan, Matijs
89
89
  van Zuijlen, Maria, Brendan Ribera, Todd Willey, Bryan Helmkamp, Jan Berkel,
90
- Lourens Naudé, Rick Olson, Russell Norris, Piotr Usewicz, Chris Eppstein.
90
+ Lourens Naudé, Rick Olson, Russell Norris, Piotr Usewicz, Chris Eppstein,
91
+ Denis Barushev, Ben Pickles.
91
92
 
92
93
 
93
94
  == Usable pagination in the UI
data/Rakefile CHANGED
@@ -32,18 +32,9 @@ desc %{Update ".manifest" with the latest list of project filenames. Respect\
32
32
  .gitignore by excluding everything that git ignores. Update `files` and\
33
33
  `test_files` arrays in "*.gemspec" file if it's present.}
34
34
  task :manifest do
35
- list = Dir['**/*'].sort
36
- spec_file = Dir['*.gemspec'].first
37
- list -= [spec_file] if spec_file
35
+ list = `git ls-files --full-name --exclude=*.gemspec --exclude=.*`.chomp.split("\n")
38
36
 
39
- File.read('.gitignore').each_line do |glob|
40
- glob = glob.chomp.sub(/^\//, '')
41
- list -= Dir[glob]
42
- list -= Dir["#{glob}/**/*"] if File.directory?(glob) and !File.symlink?(glob)
43
- puts "excluding #{glob}"
44
- end
45
-
46
- if spec_file
37
+ if spec_file = Dir['*.gemspec'].first
47
38
  spec = File.read spec_file
48
39
  spec.gsub! /^(\s* s.(test_)?files \s* = \s* )( \[ [^\]]* \] | %w\( [^)]* \) )/mx do
49
40
  assignment = $1
@@ -51,9 +42,9 @@ task :manifest do
51
42
  '%s%%w(%s)' % [assignment, bunch.join(' ')]
52
43
  end
53
44
 
54
- File.open(spec_file, 'w') {|f| f << spec }
45
+ File.open(spec_file, 'w') { |f| f << spec }
55
46
  end
56
- File.open('.manifest', 'w') {|f| f << list.join("\n") }
47
+ File.open('.manifest', 'w') { |f| f << list.join("\n") }
57
48
  end
58
49
 
59
50
  task :examples do
@@ -17,7 +17,7 @@ module WillPaginate
17
17
 
18
18
  # hooks WillPaginate::ViewHelpers into ActionView::Base
19
19
  def enable_actionpack
20
- return if ActionView::Base.instance_methods.include? 'will_paginate'
20
+ return if ActionView::Base.instance_methods.include_method? :will_paginate
21
21
  require 'will_paginate/view_helpers'
22
22
  ActionView::Base.send :include, ViewHelpers
23
23
 
@@ -44,6 +44,16 @@ module WillPaginate
44
44
  klass.send :include, Finder::ClassMethods
45
45
  klass.class_eval { alias_method_chain :method_missing, :paginate }
46
46
  end
47
+
48
+ # monkeypatch Rails ticket #2189: "count breaks has_many :through"
49
+ ActiveRecord::Base.class_eval do
50
+ protected
51
+ def self.construct_count_options_from_args(*args)
52
+ result = super
53
+ result[0] = '*' if result[0].is_a?(String) and result[0] =~ /\.\*$/
54
+ result
55
+ end
56
+ end
47
57
  end
48
58
 
49
59
  # Enable named_scope, a feature of Rails 2.1, even if you have older Rails
@@ -68,15 +78,12 @@ module WillPaginate
68
78
 
69
79
  def self.warn(message, callstack = caller)
70
80
  message = 'WillPaginate: ' + message.strip.gsub(/\s+/, ' ')
71
- behavior.call(message, callstack) if behavior && !silenced?
72
- end
73
-
74
- def self.silenced?
75
- ActiveSupport::Deprecation.silenced?
81
+ ActiveSupport::Deprecation.warn(message, callstack)
76
82
  end
77
83
  end
78
84
  end
79
85
 
80
- if defined?(Rails) and defined?(ActiveRecord) and defined?(ActionController)
81
- WillPaginate.enable
86
+ if defined? Rails
87
+ WillPaginate.enable_activerecord if defined? ActiveRecord
88
+ WillPaginate.enable_actionpack if defined? ActionController
82
89
  end
@@ -82,7 +82,7 @@ module WillPaginate
82
82
  #
83
83
  # The Array#paginate API has since then changed, but this still serves as a
84
84
  # fine example of WillPaginate::Collection usage.
85
- def self.create(page, per_page, total = nil, &block)
85
+ def self.create(page, per_page, total = nil)
86
86
  pager = new(page, per_page, total)
87
87
  yield pager
88
88
  pager
@@ -1,7 +1,18 @@
1
1
  require 'set'
2
2
  require 'will_paginate/array'
3
3
 
4
- unless Hash.instance_methods.include? 'except'
4
+ # helper to check for method existance in ruby 1.8- and 1.9-compatible way
5
+ # because `methods`, `instance_methods` and others return strings in 1.8 and symbols in 1.9
6
+ #
7
+ # ['foo', 'bar'].include_method?(:foo) # => true
8
+ class Array
9
+ def include_method?(name)
10
+ name = name.to_sym
11
+ !!(find { |item| item.to_sym == name })
12
+ end
13
+ end
14
+
15
+ unless Hash.instance_methods.include_method? :except
5
16
  Hash.class_eval do
6
17
  # Returns a new hash without the given keys.
7
18
  def except(*keys)
@@ -16,7 +27,7 @@ unless Hash.instance_methods.include? 'except'
16
27
  end
17
28
  end
18
29
 
19
- unless Hash.instance_methods.include? 'slice'
30
+ unless Hash.instance_methods.include_method? :slice
20
31
  Hash.class_eval do
21
32
  # Returns a new hash with only the given keys.
22
33
  def slice(*keys)
@@ -61,7 +61,7 @@ module WillPaginate
61
61
  #
62
62
  # All other options (+conditions+, +order+, ...) are forwarded to +find+
63
63
  # and +count+ calls.
64
- def paginate(*args, &block)
64
+ def paginate(*args)
65
65
  options = args.pop
66
66
  page, per_page, total_entries = wp_parse_options(options)
67
67
  finder = (options[:finder] || 'find').to_s
@@ -79,7 +79,7 @@ module WillPaginate
79
79
 
80
80
  args << find_options
81
81
  # @options_from_last_find = nil
82
- pager.replace send(finder, *args, &block)
82
+ pager.replace(send(finder, *args) { |*a| yield(*a) if block_given? })
83
83
 
84
84
  # magic counting for user convenience:
85
85
  pager.total_entries = wp_count(count_options, args, finder) unless pager.total_entries
@@ -96,7 +96,7 @@ module WillPaginate
96
96
  #
97
97
  # See {Faking Cursors in ActiveRecord}[http://weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord]
98
98
  # where Jamis Buck describes this and a more efficient way for MySQL.
99
- def paginated_each(options = {}, &block)
99
+ def paginated_each(options = {})
100
100
  options = { :order => 'id', :page => 1 }.merge options
101
101
  options[:page] = options[:page].to_i
102
102
  options[:total_entries] = 0 # skip the individual count queries
@@ -104,7 +104,10 @@ module WillPaginate
104
104
 
105
105
  begin
106
106
  collection = paginate(options)
107
- total += collection.each(&block).size
107
+ with_exclusive_scope(:find => {}) do
108
+ # using exclusive scope so that the block is yielded in scope-free context
109
+ total += collection.each { |item| yield item }.size
110
+ end
108
111
  options[:page] += 1
109
112
  end until collection.size < collection.per_page
110
113
 
@@ -138,7 +141,7 @@ module WillPaginate
138
141
  count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s]+$/mi, ''
139
142
  count_query = "SELECT COUNT(*) FROM (#{count_query})"
140
143
 
141
- unless ['oracle', 'oci'].include?(self.connection.adapter_name.downcase)
144
+ unless self.connection.adapter_name =~ /^(oracle|oci$)/i
142
145
  count_query << ' AS count_table'
143
146
  end
144
147
  # perform the count query
@@ -158,10 +161,14 @@ module WillPaginate
158
161
 
159
162
  protected
160
163
 
161
- def method_missing_with_paginate(method, *args, &block) #:nodoc:
164
+ def method_missing_with_paginate(method, *args) #:nodoc:
162
165
  # did somebody tried to paginate? if not, let them be
163
166
  unless method.to_s.index('paginate') == 0
164
- return method_missing_without_paginate(method, *args, &block)
167
+ if block_given?
168
+ return method_missing_without_paginate(method, *args) { |*a| yield(*a) }
169
+ else
170
+ return method_missing_without_paginate(method, *args)
171
+ end
165
172
  end
166
173
 
167
174
  # paginate finders are really just find_* with limit and offset
@@ -174,14 +181,27 @@ module WillPaginate
174
181
  options[:finder] = finder
175
182
  args << options
176
183
 
177
- paginate(*args, &block)
184
+ paginate(*args) { |*a| yield(*a) if block_given? }
178
185
  end
179
186
 
180
187
  # Does the not-so-trivial job of finding out the total number of entries
181
188
  # in the database. It relies on the ActiveRecord +count+ method.
182
189
  def wp_count(options, args, finder)
183
190
  excludees = [:count, :order, :limit, :offset, :readonly]
184
- unless options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
191
+ excludees << :from unless ActiveRecord::Calculations::CALCULATIONS_OPTIONS.include?(:from)
192
+
193
+ # we may be in a model or an association proxy
194
+ klass = (@owner and @reflection) ? @reflection.klass : self
195
+
196
+ # Use :select from scope if it isn't already present.
197
+ options[:select] = scope(:find, :select) unless options[:select]
198
+
199
+ if options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
200
+ # Remove quoting and check for table_name.*-like statement.
201
+ if options[:select].gsub('`', '') =~ /\w+\.\*/
202
+ options[:select] = "DISTINCT #{klass.table_name}.#{klass.primary_key}"
203
+ end
204
+ else
185
205
  excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
186
206
  end
187
207
 
@@ -191,13 +211,10 @@ module WillPaginate
191
211
  # merge the hash found in :count
192
212
  # this allows you to specify :select, :order, or anything else just for the count query
193
213
  count_options.update options[:count] if options[:count]
194
-
195
- # we may be in a model or an association proxy
196
- klass = (@owner and @reflection) ? @reflection.klass : self
197
-
214
+
198
215
  # forget about includes if they are irrelevant (Rails 2.1)
199
216
  if count_options[:include] and
200
- klass.private_methods.include?('references_eager_loaded_tables?') and
217
+ klass.private_methods.include_method?(:references_eager_loaded_tables?) and
201
218
  !klass.send(:references_eager_loaded_tables?, count_options)
202
219
  count_options.delete :include
203
220
  end
@@ -209,9 +226,9 @@ module WillPaginate
209
226
  # scope_out adds a 'with_finder' method which acts like with_scope, if it's present
210
227
  # then execute the count with the scoping provided by the with_finder
211
228
  send(scoper, &counter)
212
- elsif match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(finder)
229
+ elsif finder =~ /^find_(all_by|by)_([_a-zA-Z]\w*)$/
213
230
  # extract conditions from calls like "paginate_by_foo_and_bar"
214
- attribute_names = extract_attribute_names_from_match(match)
231
+ attribute_names = $2.split('_and_')
215
232
  conditions = construct_attributes_from_arguments(attribute_names, args)
216
233
  with_scope(:find => { :conditions => conditions }, &counter)
217
234
  else
@@ -1,27 +1,22 @@
1
- ## stolen from: http://dev.rubyonrails.org/browser/trunk/activerecord/lib/active_record/named_scope.rb?rev=9084
2
-
3
1
  module WillPaginate
4
2
  # This is a feature backported from Rails 2.1 because of its usefullness not only with will_paginate,
5
3
  # but in other aspects when managing complex conditions that you want to be reusable.
6
4
  module NamedScope
7
5
  # All subclasses of ActiveRecord::Base have two named_scopes:
8
6
  # * <tt>all</tt>, which is similar to a <tt>find(:all)</tt> query, and
9
- # * <tt>scoped</tt>, which allows for the creation of anonymous scopes, on the fly:
10
- #
11
- # Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)
7
+ # * <tt>scoped</tt>, which allows for the creation of anonymous scopes, on the fly: <tt>Shirt.scoped(:conditions => {:color => 'red'}).scoped(:include => :washing_instructions)</tt>
12
8
  #
13
9
  # These anonymous scopes tend to be useful when procedurally generating complex queries, where passing
14
10
  # intermediate values (scopes) around as first-class objects is convenient.
15
11
  def self.included(base)
16
12
  base.class_eval do
17
13
  extend ClassMethods
18
- named_scope :all
19
14
  named_scope :scoped, lambda { |scope| scope }
20
15
  end
21
16
  end
22
17
 
23
18
  module ClassMethods
24
- def scopes #:nodoc:
19
+ def scopes
25
20
  read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
26
21
  end
27
22
 
@@ -46,7 +41,7 @@ module WillPaginate
46
41
  # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
47
42
  # for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
48
43
  #
49
- # All scopes are available as class methods on the ActiveRecord descendent upon which the scopes were defined. But they are also available to
44
+ # All scopes are available as class methods on the ActiveRecord::Base descendent upon which the scopes were defined. But they are also available to
50
45
  # <tt>has_many</tt> associations. If,
51
46
  #
52
47
  # class Person < ActiveRecord::Base
@@ -76,14 +71,27 @@ module WillPaginate
76
71
  # end
77
72
  # end
78
73
  #
79
- def named_scope(name, options = {}, &block)
74
+ #
75
+ # For testing complex named scopes, you can examine the scoping options using the
76
+ # <tt>proxy_options</tt> method on the proxy itself.
77
+ #
78
+ # class Shirt < ActiveRecord::Base
79
+ # named_scope :colored, lambda { |color|
80
+ # { :conditions => { :color => color } }
81
+ # }
82
+ # end
83
+ #
84
+ # expected_options = { :conditions => { :colored => 'red' } }
85
+ # assert_equal expected_options, Shirt.colored('red').proxy_options
86
+ def named_scope(name, options = {})
87
+ name = name.to_sym
80
88
  scopes[name] = lambda do |parent_scope, *args|
81
89
  Scope.new(parent_scope, case options
82
90
  when Hash
83
91
  options
84
92
  when Proc
85
93
  options.call(*args)
86
- end, &block)
94
+ end) { |*a| yield(*a) if block_given? }
87
95
  end
88
96
  (class << self; self end).instance_eval do
89
97
  define_method name do |*args|
@@ -93,14 +101,20 @@ module WillPaginate
93
101
  end
94
102
  end
95
103
 
96
- class Scope #:nodoc:
104
+ class Scope
97
105
  attr_reader :proxy_scope, :proxy_options
98
- [].methods.each { |m| delegate m, :to => :proxy_found unless m =~ /(^__|^nil\?|^send|class|extend|find|count|sum|average|maximum|minimum|paginate)/ }
106
+
107
+ [].methods.each do |m|
108
+ unless m =~ /(^__|^nil\?|^send|^object_id$|class|extend|^find$|count|sum|average|maximum|minimum|paginate|first|last|empty\?|respond_to\?)/
109
+ delegate m, :to => :proxy_found
110
+ end
111
+ end
112
+
99
113
  delegate :scopes, :with_scope, :to => :proxy_scope
100
114
 
101
- def initialize(proxy_scope, options, &block)
115
+ def initialize(proxy_scope, options)
102
116
  [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
103
- extend Module.new(&block) if block_given?
117
+ extend Module.new { |*args| yield(*args) } if block_given?
104
118
  @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
105
119
  end
106
120
 
@@ -108,18 +122,42 @@ module WillPaginate
108
122
  load_found; self
109
123
  end
110
124
 
125
+ def first(*args)
126
+ if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
127
+ proxy_found.first(*args)
128
+ else
129
+ find(:first, *args)
130
+ end
131
+ end
132
+
133
+ def last(*args)
134
+ if args.first.kind_of?(Integer) || (@found && !args.first.kind_of?(Hash))
135
+ proxy_found.last(*args)
136
+ else
137
+ find(:last, *args)
138
+ end
139
+ end
140
+
141
+ def empty?
142
+ @found ? @found.empty? : count.zero?
143
+ end
144
+
145
+ def respond_to?(method, include_private = false)
146
+ super || @proxy_scope.respond_to?(method, include_private)
147
+ end
148
+
111
149
  protected
112
150
  def proxy_found
113
151
  @found || load_found
114
152
  end
115
153
 
116
154
  private
117
- def method_missing(method, *args, &block)
155
+ def method_missing(method, *args)
118
156
  if scopes.include?(method)
119
157
  scopes[method].call(self, *args)
120
158
  else
121
159
  with_scope :find => proxy_options do
122
- proxy_scope.send(method, *args, &block)
160
+ proxy_scope.send(method, *args) { |*a| yield(*a) if block_given? }
123
161
  end
124
162
  end
125
163
  end
@@ -1,9 +1,7 @@
1
- ## based on http://dev.rubyonrails.org/changeset/9084
2
-
3
1
  ActiveRecord::Associations::AssociationProxy.class_eval do
4
2
  protected
5
- def with_scope(*args, &block)
6
- @reflection.klass.send :with_scope, *args, &block
3
+ def with_scope(*args)
4
+ @reflection.klass.send(:with_scope, *args) { |*a| yield(*a) if block_given? }
7
5
  end
8
6
  end
9
7
 
@@ -12,11 +10,11 @@ end
12
10
  klass.class_eval do
13
11
  protected
14
12
  alias :method_missing_without_scopes :method_missing_without_paginate
15
- def method_missing_without_paginate(method, *args, &block)
13
+ def method_missing_without_paginate(method, *args)
16
14
  if @reflection.klass.scopes.include?(method)
17
- @reflection.klass.scopes[method].call(self, *args, &block)
15
+ @reflection.klass.scopes[method].call(self, *args) { |*a| yield(*a) if block_given? }
18
16
  else
19
- method_missing_without_scopes(method, *args, &block)
17
+ method_missing_without_scopes(method, *args) { |*a| yield(*a) if block_given? }
20
18
  end
21
19
  end
22
20
  end
@@ -25,14 +23,14 @@ end
25
23
  # Rails 1.2.6
26
24
  ActiveRecord::Associations::HasAndBelongsToManyAssociation.class_eval do
27
25
  protected
28
- def method_missing(method, *args, &block)
26
+ def method_missing(method, *args)
29
27
  if @target.respond_to?(method) || (!@reflection.klass.respond_to?(method) && Class.respond_to?(method))
30
28
  super
31
29
  elsif @reflection.klass.scopes.include?(method)
32
30
  @reflection.klass.scopes[method].call(self, *args)
33
31
  else
34
32
  @reflection.klass.with_scope(:find => { :conditions => @finder_sql, :joins => @join_sql, :readonly => false }) do
35
- @reflection.klass.send(method, *args, &block)
33
+ @reflection.klass.send(method, *args) { |*a| yield(*a) if block_given? }
36
34
  end
37
35
  end
38
36
  end
@@ -2,7 +2,7 @@ module WillPaginate
2
2
  module VERSION
3
3
  MAJOR = 2
4
4
  MINOR = 3
5
- TINY = 3
5
+ TINY = 10
6
6
 
7
7
  STRING = [MAJOR, MINOR, TINY].join('.')
8
8
  end
@@ -99,7 +99,7 @@ module WillPaginate
99
99
 
100
100
  options = options.symbolize_keys.reverse_merge WillPaginate::ViewHelpers.pagination_options
101
101
  if options[:prev_label]
102
- WillPaginate::Deprecation::warn(":prev_label view parameter is now :previous_label; the old name has been deprecated.")
102
+ WillPaginate::Deprecation::warn(":prev_label view parameter is now :previous_label; the old name has been deprecated", caller)
103
103
  options[:previous_label] = options.delete(:prev_label)
104
104
  end
105
105
 
@@ -141,8 +141,15 @@ module WillPaginate
141
141
  # blocks of pagination links sharing the same ID (which is invalid HTML).
142
142
  def paginated_section(*args, &block)
143
143
  pagination = will_paginate(*args).to_s
144
- content = pagination + capture(&block) + pagination
145
- concat content, block.binding
144
+
145
+ unless ActionView::Base.respond_to? :erb_variable
146
+ concat pagination
147
+ yield
148
+ concat pagination
149
+ else
150
+ content = pagination + capture(&block) + pagination
151
+ concat(content, block.binding)
152
+ end
146
153
  end
147
154
 
148
155
  # Renders a helpful message with numbers of displayed vs. total entries.
@@ -178,12 +185,11 @@ module WillPaginate
178
185
 
179
186
  def self.total_pages_for_collection(collection) #:nodoc:
180
187
  if collection.respond_to?('page_count') and !collection.respond_to?('total_pages')
181
- WillPaginate::Deprecation.warn <<-MSG
188
+ WillPaginate::Deprecation.warn %{
182
189
  You are using a paginated collection of class #{collection.class.name}
183
190
  which conforms to the old API of WillPaginate::Collection by using
184
191
  `page_count`, while the current method name is `total_pages`. Please
185
- upgrade yours or 3rd-party code that provides the paginated collection.
186
- MSG
192
+ upgrade yours or 3rd-party code that provides the paginated collection}, caller
187
193
  class << collection
188
194
  def total_pages; page_count; end
189
195
  end
@@ -315,8 +321,7 @@ module WillPaginate
315
321
  stringified_merge @url_params, @options[:params] if @options[:params]
316
322
 
317
323
  if complex = param_name.index(/[^\w-]/)
318
- page_param = (defined?(CGIMethods) ? CGIMethods : ActionController::AbstractRequest).
319
- parse_query_parameters("#{param_name}=#{page}")
324
+ page_param = parse_query_parameters("#{param_name}=#{page}")
320
325
 
321
326
  stringified_merge @url_params, page_param
322
327
  else
@@ -327,21 +332,21 @@ module WillPaginate
327
332
  return url if page_one
328
333
 
329
334
  if complex
330
- @url_string = url.sub(%r!((?:\?|&amp;)#{CGI.escape param_name}=)#{page}!, '\1@')
335
+ @url_string = url.sub(%r!((?:\?|&amp;)#{CGI.escape param_name}=)#{page}!, "\\1\0")
331
336
  return url
332
337
  else
333
338
  @url_string = url
334
339
  @url_params[param_name] = 3
335
340
  @template.url_for(@url_params).split(//).each_with_index do |char, i|
336
341
  if char == '3' and url[i, 1] == '2'
337
- @url_string[i] = '@'
342
+ @url_string[i] = "\0"
338
343
  break
339
344
  end
340
345
  end
341
346
  end
342
347
  end
343
348
  # finally!
344
- @url_string.sub '@', page.to_s
349
+ @url_string.sub "\0", page.to_s
345
350
  end
346
351
 
347
352
  private
@@ -379,5 +384,21 @@ module WillPaginate
379
384
  end
380
385
  end
381
386
  end
387
+
388
+ def parse_query_parameters(params)
389
+ if defined? Rack::Utils
390
+ # For Rails > 2.3
391
+ Rack::Utils.parse_nested_query(params)
392
+ elsif defined?(ActionController::AbstractRequest)
393
+ ActionController::AbstractRequest.parse_query_parameters(params)
394
+ elsif defined?(ActionController::UrlEncodedPairParser)
395
+ # For Rails > 2.2
396
+ ActionController::UrlEncodedPairParser.parse_query_parameters(params)
397
+ elsif defined?(CGIMethods)
398
+ CGIMethods.parse_query_parameters(params)
399
+ else
400
+ raise "unsupported ActionPack version"
401
+ end
402
+ end
382
403
  end
383
404
  end
@@ -2,6 +2,9 @@ require 'helper'
2
2
  require 'will_paginate/array'
3
3
 
4
4
  class ArrayPaginationTest < Test::Unit::TestCase
5
+
6
+ def setup ; end
7
+
5
8
  def test_simple
6
9
  collection = ('a'..'e').to_a
7
10
 
@@ -17,6 +17,6 @@ mysql:
17
17
  postgres:
18
18
  adapter: postgresql
19
19
  username: mislav
20
- password: mislav
20
+ password:
21
21
  database: will_paginate_unittest
22
22
  min_messages: warning
@@ -261,6 +261,12 @@ class FinderTest < ActiveRecordTestCase
261
261
  assert_equal 1, entries.total_entries, 'only one topic should be found'
262
262
  end
263
263
  end
264
+
265
+ def test_named_scope_with_include
266
+ project = projects(:active_record)
267
+ entries = project.topics.with_replies_starting_with('AR ').paginate(:page => 1, :per_page => 1)
268
+ assert_equal 1, entries.size
269
+ end
264
270
 
265
271
  ## misc ##
266
272
 
@@ -278,11 +284,11 @@ class FinderTest < ActiveRecordTestCase
278
284
  # this functionality is temporarily removed
279
285
  def xtest_pagination_defines_method
280
286
  pager = "paginate_by_created_at"
281
- assert !User.methods.include?(pager), "User methods should not include `#{pager}` method"
287
+ assert !User.methods.include_method?(pager), "User methods should not include `#{pager}` method"
282
288
  # paginate!
283
289
  assert 0, User.send(pager, nil, :page => 1).total_entries
284
290
  # the paging finder should now be defined
285
- assert User.methods.include?(pager), "`#{pager}` method should be defined on User"
291
+ assert User.methods.include_method?(pager), "`#{pager}` method should be defined on User"
286
292
  end
287
293
 
288
294
  # Is this Rails 2.0? Find out by testing find_all which was removed in [6998]
@@ -340,6 +346,12 @@ class FinderTest < ActiveRecordTestCase
340
346
  Developer.paginate :select => 'DISTINCT salary', :page => 2
341
347
  end
342
348
 
349
+ def test_count_with_scoped_select_when_distinct
350
+ Developer.stubs(:find).returns([])
351
+ Developer.expects(:count).with(:select => 'DISTINCT users.id').returns(0)
352
+ Developer.distinct.paginate :page => 2
353
+ end
354
+
343
355
  def test_should_use_scoped_finders_if_present
344
356
  # scope-out compatibility
345
357
  Topic.expects(:find_best).returns(Array.new(5))
@@ -349,18 +361,15 @@ class FinderTest < ActiveRecordTestCase
349
361
  end
350
362
 
351
363
  def test_paginate_by_sql
352
- assert_respond_to Developer, :paginate_by_sql
353
- Developer.expects(:find_by_sql).with(regexp_matches(/sql LIMIT 3(,| OFFSET) 3/)).returns([])
354
- Developer.expects(:count_by_sql).with('SELECT COUNT(*) FROM (sql) AS count_table').returns(0)
355
-
356
- entries = Developer.paginate_by_sql 'sql', :page => 2, :per_page => 3
364
+ sql = "SELECT * FROM users WHERE type = 'Developer' ORDER BY id"
365
+ entries = Developer.paginate_by_sql(sql, :page => 2, :per_page => 3)
366
+ assert_equal 11, entries.total_entries
367
+ assert_equal [users(:dev_4), users(:dev_5), users(:dev_6)], entries
357
368
  end
358
369
 
359
370
  def test_paginate_by_sql_respects_total_entries_setting
360
- Developer.expects(:find_by_sql).returns([])
361
- Developer.expects(:count_by_sql).never
362
-
363
- entries = Developer.paginate_by_sql 'sql', :page => 1, :total_entries => 999
371
+ sql = "SELECT * FROM users"
372
+ entries = Developer.paginate_by_sql(sql, :page => 1, :total_entries => 999)
364
373
  assert_equal 999, entries.total_entries
365
374
  end
366
375
 
@@ -390,12 +399,11 @@ class FinderTest < ActiveRecordTestCase
390
399
 
391
400
  def test_paginating_finder_doesnt_mangle_options
392
401
  Developer.expects(:find).returns([])
393
- options = { :page => 1 }
394
- options.expects(:delete).never
402
+ options = { :page => 1, :per_page => 2, :foo => 'bar' }
395
403
  options_before = options.dup
396
404
 
397
405
  Developer.paginate(options)
398
- assert_equal options, options_before
406
+ assert_equal options_before, options
399
407
  end
400
408
 
401
409
  def test_paginate_by_sql_doesnt_change_original_query
@@ -422,8 +430,14 @@ class FinderTest < ActiveRecordTestCase
422
430
  assert_equal 14, Developer.paginated_each(:page => '2') { }
423
431
  end
424
432
 
433
+ def test_paginated_each_with_named_scope
434
+ assert_equal 2, Developer.poor.paginated_each(:per_page => 1) {
435
+ assert_equal 11, Developer.count
436
+ }
437
+ end
438
+
425
439
  # detect ActiveRecord 2.1
426
- if ActiveRecord::Base.private_methods.include?('references_eager_loaded_tables?')
440
+ if ActiveRecord::Base.private_methods.include_method?(:references_eager_loaded_tables?)
427
441
  def test_removes_irrelevant_includes_in_count
428
442
  Developer.expects(:find).returns([1])
429
443
  Developer.expects(:count).with({}).returns(0)
@@ -439,5 +453,21 @@ class FinderTest < ActiveRecordTestCase
439
453
  :include => :projects, :conditions => 'projects.id > 2'
440
454
  end
441
455
  end
456
+
457
+ def test_paginate_from
458
+ result = Developer.paginate(:from => 'users', :page => 1, :per_page => 1)
459
+ assert_equal 1, result.size
460
+ end
461
+
462
+ def test_hmt_with_include
463
+ # ticket #220
464
+ reply = projects(:active_record).replies.find(:first, :order => 'replies.id')
465
+ assert_equal replies(:decisive), reply
466
+
467
+ # ticket #223
468
+ Project.find(1, :include => :replies)
469
+
470
+ # I cannot reproduce any of the failures from those reports :(
471
+ end
442
472
  end
443
473
  end
@@ -7,6 +7,7 @@ class Developer < User
7
7
  end
8
8
  end
9
9
 
10
+ named_scope :distinct, :select => 'DISTINCT `users`.*'
10
11
  named_scope :poor, :conditions => ['salary <= ?', 80000], :order => 'salary'
11
12
 
12
13
  def self.per_page() 10 end
@@ -3,4 +3,8 @@ class Topic < ActiveRecord::Base
3
3
  belongs_to :project
4
4
 
5
5
  named_scope :mentions_activerecord, :conditions => ['topics.title LIKE ?', '%ActiveRecord%']
6
+
7
+ named_scope :with_replies_starting_with, lambda { |text|
8
+ { :conditions => "replies.content LIKE '#{text}%' ", :include => :replies }
9
+ }
6
10
  end
@@ -29,7 +29,10 @@ end
29
29
 
30
30
  # Wrap tests that use Mocha and skip if unavailable.
31
31
  def uses_mocha(test_name)
32
- require 'mocha' unless Object.const_defined?(:Mocha)
32
+ unless Object.const_defined?(:Mocha)
33
+ gem 'mocha', '>= 0.9.5'
34
+ require 'mocha'
35
+ end
33
36
  rescue LoadError => load_error
34
37
  $stderr.puts "Skipping #{test_name} tests. `gem install mocha` and try again."
35
38
  else
@@ -1,6 +1,13 @@
1
1
  require 'lib/activerecord_test_connector'
2
2
 
3
3
  class ActiveRecordTestCase < Test::Unit::TestCase
4
+ if defined?(ActiveSupport::Testing::SetupAndTeardown)
5
+ include ActiveSupport::Testing::SetupAndTeardown
6
+ end
7
+
8
+ if defined?(ActiveRecord::TestFixtures)
9
+ include ActiveRecord::TestFixtures
10
+ end
4
11
  # Set our fixture path
5
12
  if ActiveRecordTestConnector.able_to_connect
6
13
  self.fixture_path = File.join(File.dirname(__FILE__), '..', 'fixtures')
@@ -16,16 +16,20 @@ class ActiveRecordTestConnector
16
16
  unless self.connected || !self.able_to_connect
17
17
  setup_connection
18
18
  load_schema
19
- Dependencies.load_paths.unshift FIXTURES_PATH
19
+ add_load_path FIXTURES_PATH
20
20
  self.connected = true
21
21
  end
22
22
  rescue Exception => e # errors from ActiveRecord setup
23
- $stderr.puts "\nSkipping ActiveRecord tests: #{e}"
24
- $stderr.puts "Install SQLite3 to run the full test suite for will_paginate.\n\n"
23
+ $stderr.puts "\nSkipping ActiveRecord tests: #{e}\n\n"
25
24
  self.able_to_connect = false
26
25
  end
27
26
 
28
27
  private
28
+
29
+ def self.add_load_path(path)
30
+ dep = defined?(ActiveSupport::Dependencies) ? ActiveSupport::Dependencies : ::Dependencies
31
+ dep.load_paths.unshift path
32
+ end
29
33
 
30
34
  def self.setup_connection
31
35
  db = ENV['DB'].blank?? 'sqlite3' : ENV['DB']
@@ -37,6 +41,8 @@ class ActiveRecordTestConnector
37
41
  ActiveRecord::Base.logger = Logger.new(STDOUT) if $0 == 'irb'
38
42
  puts "using #{configuration['adapter']} adapter" unless ENV['DB'].blank?
39
43
 
44
+ gem 'sqlite3-ruby' if 'sqlite3' == db
45
+
40
46
  ActiveRecord::Base.establish_connection(configuration)
41
47
  ActiveRecord::Base.configurations = { db => configuration }
42
48
  prepare ActiveRecord::Base.connection
@@ -1,3 +1,4 @@
1
+ require 'will_paginate/core_ext'
1
2
  require 'action_controller'
2
3
  require 'action_controller/test_process'
3
4
 
@@ -17,6 +18,13 @@ end
17
18
  ActionController::Base.perform_caching = false
18
19
 
19
20
  class WillPaginate::ViewTestCase < Test::Unit::TestCase
21
+ if defined?(ActionController::TestCase::Assertions)
22
+ include ActionController::TestCase::Assertions
23
+ end
24
+ if defined?(ActiveSupport::Testing::Deprecation)
25
+ include ActiveSupport::Testing::Deprecation
26
+ end
27
+
20
28
  def setup
21
29
  super
22
30
  @controller = DummyController.new
@@ -42,15 +50,21 @@ class WillPaginate::ViewTestCase < Test::Unit::TestCase
42
50
 
43
51
  locals = { :collection => collection, :options => options }
44
52
 
45
- if defined? ActionView::InlineTemplate
46
- # Rails 2.1
47
- args = [ ActionView::InlineTemplate.new(@view, @template, locals) ]
53
+ unless @view.respond_to? :render_template
54
+ # Rails 2.2
55
+ @html_result = ActionView::InlineTemplate.new(@template).render(@view, locals)
48
56
  else
49
- # older Rails versions
50
- args = [nil, @template, nil, locals]
57
+ if defined? ActionView::InlineTemplate
58
+ # Rails 2.1
59
+ args = [ ActionView::InlineTemplate.new(@view, @template, locals) ]
60
+ else
61
+ # older Rails versions
62
+ args = [nil, @template, nil, locals]
63
+ end
64
+
65
+ @html_result = @view.render_template(*args)
51
66
  end
52
67
 
53
- @html_result = @view.render_template(*args)
54
68
  @html_document = HTML::Document.new(@html_result, true, false)
55
69
 
56
70
  if block_given?
@@ -35,7 +35,7 @@ task :test_full => %w(test test_mysql test_postgres)
35
35
  desc %{Test everything with Rails 2.1.x, 2.0.x & 1.2.x gems}
36
36
  task :test_all do
37
37
  all = Rake::Task['test_full']
38
- versions = %w(2.1.0 2.0.2 1.2.6)
38
+ versions = %w(2.3.2 2.2.2 2.1.0 2.0.4 1.2.6)
39
39
  versions.each do |version|
40
40
  ENV['RAILS_VERSION'] = "~> #{version}"
41
41
  all.invoke
@@ -192,13 +192,15 @@ class ViewTest < WillPaginate::ViewTestCase
192
192
  @html_result
193
193
  end
194
194
 
195
- def test_page_entries_info_with_longer_class_name
196
- @template = '<%= page_entries_info collection %>'
197
- collection = ('a'..'z').to_a.paginate
198
- collection.first.stubs(:class).returns(mock('class', :name => 'ProjectType'))
195
+ uses_mocha 'class name' do
196
+ def test_page_entries_info_with_longer_class_name
197
+ @template = '<%= page_entries_info collection %>'
198
+ collection = ('a'..'z').to_a.paginate
199
+ collection.first.stubs(:class).returns(mock('class', :name => 'ProjectType'))
199
200
 
200
- paginate collection
201
- assert @html_result.index('project types'), "expected <#{@html_result.inspect}> to mention 'project types'"
201
+ paginate collection
202
+ assert @html_result.index('project types'), "expected <#{@html_result.inspect}> to mention 'project types'"
203
+ end
202
204
  end
203
205
 
204
206
  def test_page_entries_info_with_single_page_collection
@@ -275,6 +277,14 @@ class ViewTest < WillPaginate::ViewTestCase
275
277
  end
276
278
  end
277
279
  end
280
+
281
+ def test_will_paginate_with_atmark_url
282
+ @request.symbolized_path_parameters[:action] = "@tag"
283
+ renderer = WillPaginate::LinkRenderer.new
284
+
285
+ paginate({ :page => 1 }, :renderer=>renderer)
286
+ assert_links_match %r[/foo/@tag\?page=\d]
287
+ end
278
288
 
279
289
  def test_complex_custom_page_param
280
290
  @request.params :developers => { :page => 2 }
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shingara-will_paginate
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.3
4
+ version: 2.3.10
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Mislav Marohni\xC4\x87"
@@ -10,18 +10,10 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2008-08-29 00:00:00 -07:00
13
+ date: 2009-05-21 00:00:00 -07:00
14
14
  default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
17
- name: activesupport
18
- version_requirement:
19
- version_requirements: !ruby/object:Gem::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 1.4.4
24
- version:
15
+ dependencies: []
16
+
25
17
  description: The will_paginate library provides a simple, yet powerful and extensible API for ActiveRecord pagination and rendering of pagination links in ActionView templates.
26
18
  email: mislav.marohnic@gmail.com
27
19
  executables: []
@@ -37,15 +29,12 @@ files:
37
29
  - LICENSE
38
30
  - README.rdoc
39
31
  - Rakefile
40
- - examples
41
32
  - examples/apple-circle.gif
42
33
  - examples/index.haml
43
34
  - examples/index.html
44
35
  - examples/pagination.css
45
36
  - examples/pagination.sass
46
37
  - init.rb
47
- - lib
48
- - lib/will_paginate
49
38
  - lib/will_paginate.rb
50
39
  - lib/will_paginate/array.rb
51
40
  - lib/will_paginate/collection.rb
@@ -55,13 +44,11 @@ files:
55
44
  - lib/will_paginate/named_scope_patch.rb
56
45
  - lib/will_paginate/version.rb
57
46
  - lib/will_paginate/view_helpers.rb
58
- - test
59
47
  - test/boot.rb
60
48
  - test/collection_test.rb
61
49
  - test/console
62
50
  - test/database.yml
63
51
  - test/finder_test.rb
64
- - test/fixtures
65
52
  - test/fixtures/admin.rb
66
53
  - test/fixtures/developer.rb
67
54
  - test/fixtures/developers_projects.yml
@@ -75,7 +62,6 @@ files:
75
62
  - test/fixtures/user.rb
76
63
  - test/fixtures/users.yml
77
64
  - test/helper.rb
78
- - test/lib
79
65
  - test/lib/activerecord_test_case.rb
80
66
  - test/lib/activerecord_test_connector.rb
81
67
  - test/lib/load_fixtures.rb
@@ -117,7 +103,6 @@ test_files:
117
103
  - test/console
118
104
  - test/database.yml
119
105
  - test/finder_test.rb
120
- - test/fixtures
121
106
  - test/fixtures/admin.rb
122
107
  - test/fixtures/developer.rb
123
108
  - test/fixtures/developers_projects.yml
@@ -131,7 +116,6 @@ test_files:
131
116
  - test/fixtures/user.rb
132
117
  - test/fixtures/users.yml
133
118
  - test/helper.rb
134
- - test/lib
135
119
  - test/lib/activerecord_test_case.rb
136
120
  - test/lib/activerecord_test_connector.rb
137
121
  - test/lib/load_fixtures.rb