dweinand-will_paginate 2.3.4 → 2.3.7

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,3 +1,13 @@
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
+
1
11
  == 2.3.4, released 2008-09-16
2
12
 
3
13
  * Removed gem dependency to Active Support (causes trouble with vendored rails).
@@ -80,11 +80,7 @@ module WillPaginate
80
80
 
81
81
  def self.warn(message, callstack = caller)
82
82
  message = 'WillPaginate: ' + message.strip.gsub(/\s+/, ' ')
83
- behavior.call(message, callstack) if behavior && !silenced?
84
- end
85
-
86
- def self.silenced?
87
- ActiveSupport::Deprecation.silenced?
83
+ ActiveSupport::Deprecation.warn(message, callstack)
88
84
  end
89
85
  end
90
86
  end
@@ -83,7 +83,7 @@ module WillPaginate
83
83
  #
84
84
  # The Array#paginate API has since then changed, but this still serves as a
85
85
  # fine example of WillPaginate::Collection usage.
86
- def self.create(page, per_page, total = nil, &block)
86
+ def self.create(page, per_page, total = nil)
87
87
  pager = new(page, per_page, total)
88
88
  yield pager
89
89
  pager
@@ -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
 
@@ -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,13 +181,14 @@ 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]
191
+ excludees << :from unless ActiveRecord::Calculations::CALCULATIONS_OPTIONS.include?(:from)
184
192
 
185
193
  # we may be in a model or an association proxy
186
194
  klass = (@owner and @reflection) ? @reflection.klass : self
@@ -218,9 +226,9 @@ module WillPaginate
218
226
  # scope_out adds a 'with_finder' method which acts like with_scope, if it's present
219
227
  # then execute the count with the scoping provided by the with_finder
220
228
  send(scoper, &counter)
221
- elsif match = /^find_(all_by|by)_([_a-zA-Z]\w*)$/.match(finder)
229
+ elsif finder =~ /^find_(all_by|by)_([_a-zA-Z]\w*)$/
222
230
  # extract conditions from calls like "paginate_by_foo_and_bar"
223
- attribute_names = extract_attribute_names_from_match(match)
231
+ attribute_names = $2.split('_and_')
224
232
  conditions = construct_attributes_from_arguments(attribute_names, args)
225
233
  with_scope(:find => { :conditions => conditions }, &counter)
226
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 = 5
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
@@ -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
 
@@ -427,6 +433,12 @@ class FinderTest < ActiveRecordTestCase
427
433
  assert_equal 14, Developer.paginated_each(:page => '2') { }
428
434
  end
429
435
 
436
+ def test_paginated_each_with_named_scope
437
+ assert_equal 2, Developer.poor.paginated_each(:per_page => 1) {
438
+ assert_equal 11, Developer.count
439
+ }
440
+ end
441
+
430
442
  # detect ActiveRecord 2.1
431
443
  if ActiveRecord::Base.private_methods.include?('references_eager_loaded_tables?')
432
444
  def test_removes_irrelevant_includes_in_count
@@ -444,5 +456,21 @@ class FinderTest < ActiveRecordTestCase
444
456
  :include => :projects, :conditions => 'projects.id > 2'
445
457
  end
446
458
  end
459
+
460
+ def test_paginate_from
461
+ result = Developer.paginate(:from => 'users', :page => 1, :per_page => 1)
462
+ assert_equal 1, result.size
463
+ end
464
+
465
+ def test_hmt_with_include
466
+ # ticket #220
467
+ reply = projects(:active_record).replies.find(:first, :order => 'replies.id')
468
+ assert_equal replies(:decisive), reply
469
+
470
+ # ticket #223
471
+ Project.find(1, :include => :replies)
472
+
473
+ # I cannot reproduce any of the failures from those reports :(
474
+ end
447
475
  end
448
476
  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
@@ -41,6 +41,8 @@ class ActiveRecordTestConnector
41
41
  ActiveRecord::Base.logger = Logger.new(STDOUT) if $0 == 'irb'
42
42
  puts "using #{configuration['adapter']} adapter" unless ENV['DB'].blank?
43
43
 
44
+ gem 'sqlite3-ruby' if 'sqlite3' == db
45
+
44
46
  ActiveRecord::Base.establish_connection(configuration)
45
47
  ActiveRecord::Base.configurations = { db => configuration }
46
48
  prepare ActiveRecord::Base.connection
@@ -42,15 +42,21 @@ class WillPaginate::ViewTestCase < Test::Unit::TestCase
42
42
 
43
43
  locals = { :collection => collection, :options => options }
44
44
 
45
- if defined? ActionView::InlineTemplate
46
- # Rails 2.1
47
- args = [ ActionView::InlineTemplate.new(@view, @template, locals) ]
45
+ unless @view.respond_to? :render_template
46
+ # Rails 2.2
47
+ @html_result = ActionView::InlineTemplate.new(@template).render(@view, locals)
48
48
  else
49
- # older Rails versions
50
- args = [nil, @template, nil, locals]
49
+ if defined? ActionView::InlineTemplate
50
+ # Rails 2.1
51
+ args = [ ActionView::InlineTemplate.new(@view, @template, locals) ]
52
+ else
53
+ # older Rails versions
54
+ args = [nil, @template, nil, locals]
55
+ end
56
+
57
+ @html_result = @view.render_template(*args)
51
58
  end
52
59
 
53
- @html_result = @view.render_template(*args)
54
60
  @html_document = HTML::Document.new(@html_result, true, false)
55
61
 
56
62
  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.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
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dweinand-will_paginate
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.3.4
4
+ version: 2.3.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - "Mislav Marohni\xC4\x87"
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2008-09-16 00:00:00 -07:00
13
+ date: 2009-02-10 00:00:00 -08:00
14
14
  default_executable:
15
15
  dependencies: []
16
16