will_paginate 2.3.17 → 3.0.pre

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of will_paginate might be problematic. Click here for more details.

Files changed (83) hide show
  1. data/CHANGELOG.rdoc +24 -80
  2. data/LICENSE +1 -1
  3. data/README.rdoc +125 -0
  4. data/Rakefile +26 -22
  5. data/lib/will_paginate.rb +10 -84
  6. data/lib/will_paginate/array.rb +25 -8
  7. data/lib/will_paginate/collection.rb +15 -28
  8. data/lib/will_paginate/core_ext.rb +26 -0
  9. data/lib/will_paginate/deprecation.rb +50 -0
  10. data/lib/will_paginate/finders.rb +9 -0
  11. data/lib/will_paginate/finders/active_record.rb +158 -0
  12. data/lib/will_paginate/finders/active_resource.rb +51 -0
  13. data/lib/will_paginate/finders/base.rb +112 -0
  14. data/lib/will_paginate/finders/data_mapper.rb +30 -0
  15. data/lib/will_paginate/finders/sequel.rb +23 -0
  16. data/lib/will_paginate/railtie.rb +26 -0
  17. data/lib/will_paginate/version.rb +5 -5
  18. data/lib/will_paginate/view_helpers.rb +25 -436
  19. data/lib/will_paginate/view_helpers/action_view.rb +142 -0
  20. data/lib/will_paginate/view_helpers/base.rb +126 -0
  21. data/lib/will_paginate/view_helpers/link_renderer.rb +130 -0
  22. data/lib/will_paginate/view_helpers/link_renderer_base.rb +83 -0
  23. data/lib/will_paginate/view_helpers/merb.rb +13 -0
  24. data/spec/collection_spec.rb +147 -0
  25. data/spec/console +8 -0
  26. data/spec/console_fixtures.rb +8 -0
  27. data/spec/database.yml +22 -0
  28. data/spec/finders/active_record_spec.rb +377 -0
  29. data/spec/finders/active_resource_spec.rb +52 -0
  30. data/spec/finders/activerecord_test_connector.rb +114 -0
  31. data/spec/finders/data_mapper_spec.rb +62 -0
  32. data/spec/finders/data_mapper_test_connector.rb +20 -0
  33. data/spec/finders/sequel_spec.rb +53 -0
  34. data/spec/finders/sequel_test_connector.rb +9 -0
  35. data/spec/finders_spec.rb +76 -0
  36. data/{test → spec}/fixtures/admin.rb +0 -0
  37. data/{test → spec}/fixtures/developer.rb +2 -3
  38. data/{test → spec}/fixtures/developers_projects.yml +0 -0
  39. data/{test → spec}/fixtures/project.rb +2 -6
  40. data/{test → spec}/fixtures/projects.yml +1 -1
  41. data/{test → spec}/fixtures/replies.yml +0 -0
  42. data/{test → spec}/fixtures/reply.rb +1 -1
  43. data/{test → spec}/fixtures/schema.rb +0 -0
  44. data/spec/fixtures/topic.rb +7 -0
  45. data/{test → spec}/fixtures/topics.yml +0 -0
  46. data/{test → spec}/fixtures/user.rb +0 -0
  47. data/{test → spec}/fixtures/users.yml +0 -0
  48. data/spec/rcov.opts +2 -0
  49. data/spec/spec.opts +2 -0
  50. data/spec/spec_helper.rb +74 -0
  51. data/spec/tasks.rake +60 -0
  52. data/spec/view_helpers/action_view_spec.rb +345 -0
  53. data/spec/view_helpers/base_spec.rb +64 -0
  54. data/spec/view_helpers/link_renderer_base_spec.rb +84 -0
  55. data/spec/view_helpers/view_example_group.rb +103 -0
  56. metadata +60 -65
  57. data/README.md +0 -53
  58. data/lib/will_paginate/finder.rb +0 -269
  59. data/lib/will_paginate/i18n.rb +0 -29
  60. data/lib/will_paginate/locale/en.yml +0 -33
  61. data/lib/will_paginate/named_scope.rb +0 -170
  62. data/lib/will_paginate/named_scope_patch.rb +0 -37
  63. data/lib/will_paginate/per_page.rb +0 -27
  64. data/test/ci.rb +0 -60
  65. data/test/collection_test.rb +0 -160
  66. data/test/console +0 -8
  67. data/test/database.yml +0 -16
  68. data/test/finder_test.rb +0 -527
  69. data/test/fixtures/topic.rb +0 -12
  70. data/test/gemfiles/Gemfile.1.2 +0 -13
  71. data/test/gemfiles/Gemfile.1.2.lock +0 -39
  72. data/test/gemfiles/Gemfile.2.0 +0 -16
  73. data/test/gemfiles/Gemfile.2.0.lock +0 -28
  74. data/test/gemfiles/Gemfile.2.1 +0 -16
  75. data/test/gemfiles/Gemfile.2.1.lock +0 -28
  76. data/test/gemfiles/Gemfile.2.2 +0 -16
  77. data/test/gemfiles/Gemfile.2.2.lock +0 -28
  78. data/test/helper.rb +0 -34
  79. data/test/lib/activerecord_test_case.rb +0 -38
  80. data/test/lib/activerecord_test_connector.rb +0 -86
  81. data/test/lib/load_fixtures.rb +0 -12
  82. data/test/lib/view_test_process.rb +0 -186
  83. data/test/view_test.rb +0 -380
@@ -1,269 +0,0 @@
1
- require 'will_paginate/core_ext'
2
- require 'will_paginate/per_page'
3
-
4
- module WillPaginate
5
- # A mixin for ActiveRecord::Base. Provides +per_page+ class method
6
- # and hooks things up to provide paginating finders.
7
- #
8
- # Find out more in WillPaginate::Finder::ClassMethods
9
- #
10
- module Finder
11
- def self.included(base)
12
- base.extend PerPage
13
- base.extend ClassMethods
14
- class << base
15
- alias_method_chain :method_missing, :paginate
16
- end
17
- end
18
-
19
- # = Paginating finders for ActiveRecord models
20
- #
21
- # WillPaginate adds +paginate+, +per_page+ and other methods to
22
- # ActiveRecord::Base class methods and associations. It also hooks into
23
- # +method_missing+ to intercept pagination calls to dynamic finders such as
24
- # +paginate_by_user_id+ and translate them to ordinary finders
25
- # (+find_all_by_user_id+ in this case).
26
- #
27
- # In short, paginating finders are equivalent to ActiveRecord finders; the
28
- # only difference is that we start with "paginate" instead of "find" and
29
- # that <tt>:page</tt> is required parameter:
30
- #
31
- # @posts = Post.paginate :all, :page => params[:page], :order => 'created_at DESC'
32
- #
33
- # In paginating finders, "all" is implicit. There is no sense in paginating
34
- # a single record, right? So, you can drop the <tt>:all</tt> argument:
35
- #
36
- # Post.paginate(...) => Post.find :all
37
- # Post.paginate_all_by_something => Post.find_all_by_something
38
- # Post.paginate_by_something => Post.find_all_by_something
39
- #
40
- # == The importance of the <tt>:order</tt> parameter
41
- #
42
- # In ActiveRecord finders, <tt>:order</tt> parameter specifies columns for
43
- # the <tt>ORDER BY</tt> clause in SQL. It is important to have it, since
44
- # pagination only makes sense with ordered sets. Without the <tt>ORDER
45
- # BY</tt> clause, databases aren't required to do consistent ordering when
46
- # performing <tt>SELECT</tt> queries; this is especially true for
47
- # PostgreSQL.
48
- #
49
- # Therefore, make sure you are doing ordering on a column that makes the
50
- # most sense in the current context. Make that obvious to the user, also.
51
- # For perfomance reasons you will also want to add an index to that column.
52
- module ClassMethods
53
- # This is the main paginating finder.
54
- #
55
- # == Special parameters for paginating finders
56
- # * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
57
- # * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 30 if not overridden)
58
- # * <tt>:total_entries</tt> -- use only if you manually count total entries
59
- # * <tt>:count</tt> -- additional options that are passed on to +count+
60
- # * <tt>:finder</tt> -- name of the ActiveRecord finder used (default: "find")
61
- #
62
- # All other options (+conditions+, +order+, ...) are forwarded to +find+
63
- # and +count+ calls.
64
- def paginate(*args)
65
- options = args.pop
66
- page, per_page, total_entries = wp_parse_options(options)
67
- finder = (options[:finder] || 'find').to_s
68
-
69
- if finder == 'find'
70
- # an array of IDs may have been given:
71
- total_entries ||= (Array === args.first and args.first.size)
72
- # :all is implicit
73
- args.unshift(:all) if args.empty?
74
- end
75
-
76
- WillPaginate::Collection.create(page, per_page, total_entries) do |pager|
77
- count_options = options.except :page, :per_page, :total_entries, :finder
78
- find_options = count_options.except(:count).update(:offset => pager.offset, :limit => pager.per_page)
79
-
80
- args << find_options
81
- # @options_from_last_find = nil
82
- pager.replace(send(finder, *args) { |*a| yield(*a) if block_given? })
83
-
84
- # magic counting for user convenience:
85
- pager.total_entries = wp_count(count_options, args, finder) unless pager.total_entries
86
- end
87
- end
88
-
89
- # Iterates through all records by loading one page at a time. This is useful
90
- # for migrations or any other use case where you don't want to load all the
91
- # records in memory at once.
92
- #
93
- # It uses +paginate+ internally; therefore it accepts all of its options.
94
- # You can specify a starting page with <tt>:page</tt> (default is 1). Default
95
- # <tt>:order</tt> is <tt>"id"</tt>, override if necessary.
96
- #
97
- # See {Faking Cursors in ActiveRecord}[http://weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord]
98
- # where Jamis Buck describes this and a more efficient way for MySQL.
99
- def paginated_each(options = {})
100
- order = scope(:find, :order) || 'id'
101
- options = { :order => order, :page => 1 }.merge options
102
- options[:page] = options[:page].to_i
103
- options[:total_entries] = 0 # skip the individual count queries
104
- total = 0
105
-
106
- begin
107
- collection = paginate(options)
108
- with_exclusive_scope(:find => {}) do
109
- # using exclusive scope so that the block is yielded in scope-free context
110
- total += collection.each { |item| yield item }.size
111
- end
112
- options[:page] += 1
113
- end until collection.size < collection.per_page
114
-
115
- total
116
- end
117
-
118
- # Wraps +find_by_sql+ by simply adding LIMIT and OFFSET to your SQL string
119
- # based on the params otherwise used by paginating finds: +page+ and
120
- # +per_page+.
121
- #
122
- # Example:
123
- #
124
- # @developers = Developer.paginate_by_sql ['select * from developers where salary > ?', 80000],
125
- # :page => params[:page], :per_page => 3
126
- #
127
- # A query for counting rows will automatically be generated if you don't
128
- # supply <tt>:total_entries</tt>. If you experience problems with this
129
- # generated SQL, you might want to perform the count manually in your
130
- # application.
131
- #
132
- def paginate_by_sql(sql, options)
133
- WillPaginate::Collection.create(*wp_parse_options(options)) do |pager|
134
- query = sanitize_sql(sql.dup)
135
- original_query = query.dup
136
- # add limit, offset
137
- add_limit! query, :offset => pager.offset, :limit => pager.per_page
138
- # perfom the find
139
- pager.replace find_by_sql(query)
140
-
141
- unless pager.total_entries
142
- count_query = original_query.sub /\bORDER\s+BY\s+[\w`,\s]+$/mi, ''
143
- count_query = "SELECT COUNT(*) FROM (#{count_query})"
144
-
145
- unless self.connection.adapter_name =~ /^(oracle|oci$)/i
146
- count_query << ' AS count_table'
147
- end
148
- # perform the count query
149
- pager.total_entries = count_by_sql(count_query)
150
- end
151
- end
152
- end
153
-
154
- def respond_to?(method, include_priv = false) #:nodoc:
155
- case method.to_sym
156
- when :paginate, :paginate_by_sql
157
- true
158
- else
159
- super || super(method.to_s.sub(/^paginate/, 'find'), include_priv)
160
- end
161
- end
162
-
163
- protected
164
-
165
- def respond_to_missing?(method, include_private = false)
166
- method.to_s.index('paginate') == 0 || super
167
- end
168
-
169
- def method_missing_with_paginate(method, *args) #:nodoc:
170
- # did somebody tried to paginate? if not, let them be
171
- unless method.to_s.index('paginate') == 0
172
- if block_given?
173
- return method_missing_without_paginate(method, *args) { |*a| yield(*a) }
174
- else
175
- return method_missing_without_paginate(method, *args)
176
- end
177
- end
178
-
179
- # paginate finders are really just find_* with limit and offset
180
- finder = method.to_s.sub('paginate', 'find')
181
- finder.sub!('find', 'find_all') if finder.index('find_by_') == 0
182
-
183
- options = args.pop
184
- raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys
185
- options = options.dup
186
- options[:finder] = finder
187
- args << options
188
-
189
- paginate(*args) { |*a| yield(*a) if block_given? }
190
- end
191
-
192
- # Does the not-so-trivial job of finding out the total number of entries
193
- # in the database. It relies on the ActiveRecord +count+ method.
194
- def wp_count(options, args, finder)
195
- excludees = [:count, :order, :limit, :offset, :readonly]
196
- excludees << :from unless ActiveRecord::Calculations::CALCULATIONS_OPTIONS.include?(:from)
197
-
198
- # we may be in a model or an association proxy
199
- klass = (@owner and @reflection) ? @reflection.klass : self
200
-
201
- # Use :select from scope if it isn't already present.
202
- options[:select] = scope(:find, :select) unless options[:select]
203
-
204
- if options[:select] and options[:select] =~ /^\s*DISTINCT\b/i
205
- # Remove quoting and check for table_name.*-like statement.
206
- if options[:select].gsub(/[`"]/, '') =~ /\w+\.\*/
207
- options[:select] = "DISTINCT #{klass.table_name}.#{klass.primary_key}"
208
- end
209
- else
210
- excludees << :select # only exclude the select param if it doesn't begin with DISTINCT
211
- end
212
-
213
- # count expects (almost) the same options as find
214
- count_options = options.except *excludees
215
-
216
- # merge the hash found in :count
217
- # this allows you to specify :select, :order, or anything else just for the count query
218
- count_options.update options[:count] if options[:count]
219
-
220
- # forget about includes if they are irrelevant (Rails 2.1)
221
- if count_options[:include] and
222
- klass.private_methods.include_method?(:references_eager_loaded_tables?) and
223
- !klass.send(:references_eager_loaded_tables?, count_options)
224
- count_options.delete :include
225
- end
226
-
227
- # we may have to scope ...
228
- counter = Proc.new { count(count_options) }
229
-
230
- count = if finder.index('find_') == 0 and klass.respond_to?(scoper = finder.sub('find', 'with'))
231
- # scope_out adds a 'with_finder' method which acts like with_scope, if it's present
232
- # then execute the count with the scoping provided by the with_finder
233
- send(scoper, &counter)
234
- elsif finder =~ /^find_(all_by|by)_([_a-zA-Z]\w*)$/
235
- # extract conditions from calls like "paginate_by_foo_and_bar"
236
- attribute_names = $2.split('_and_')
237
- conditions = construct_attributes_from_arguments(attribute_names, args)
238
- with_scope(:find => { :conditions => conditions }, &counter)
239
- else
240
- counter.call
241
- end
242
-
243
- (!count.is_a?(Integer) && count.respond_to?(:length)) ? count.length : count
244
- end
245
-
246
- def wp_parse_options(options) #:nodoc:
247
- raise ArgumentError, 'parameter hash expected' unless options.respond_to? :symbolize_keys
248
- options = options.symbolize_keys
249
- raise ArgumentError, ':page parameter required' unless options.key? :page
250
-
251
- if options[:count] and options[:total_entries]
252
- raise ArgumentError, ':count and :total_entries are mutually exclusive'
253
- end
254
-
255
- page = options[:page] || 1
256
- per_page = options[:per_page] || self.per_page
257
- total = options[:total_entries]
258
- [page, per_page, total]
259
- end
260
-
261
- private
262
-
263
- # def find_every_with_paginate(options)
264
- # @options_from_last_find = options
265
- # find_every_without_paginate(options)
266
- # end
267
- end
268
- end
269
- end
@@ -1,29 +0,0 @@
1
- module WillPaginate
2
- module I18n
3
- def self.locale_dir
4
- File.expand_path('../locale', __FILE__)
5
- end
6
-
7
- def self.load_path
8
- Dir["#{locale_dir}/*.{rb,yml}"]
9
- end
10
-
11
- def will_paginate_translate(keys, options = {})
12
- if defined? ::I18n
13
- defaults = Array(keys).dup
14
- if block_given?
15
- if defined? ::I18n::Backend::Simple::MATCH
16
- # procs in defaults array were not supported back then
17
- defaults << yield(defaults.first, options)
18
- else
19
- defaults << Proc.new
20
- end
21
- end
22
- ::I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => :will_paginate))
23
- else
24
- key = Array === keys ? keys.first : keys
25
- yield key, options
26
- end
27
- end
28
- end
29
- end
@@ -1,33 +0,0 @@
1
- en:
2
- will_paginate:
3
- previous_label: "&laquo; Previous"
4
- next_label: "Next &raquo;"
5
- page_gap: "&hellip;"
6
-
7
- page_entries_info:
8
- single_page:
9
- zero: "No %{model} found"
10
- one: "Displaying 1 %{model}"
11
- other: "Displaying all %{count} %{model}"
12
- single_page_html:
13
- zero: "No %{model} found"
14
- one: "Displaying <b>1</b> %{model}"
15
- other: "Displaying <b>all&nbsp;%{count}</b> %{model}"
16
-
17
- multi_page: "Displaying %{model} %{from} - %{to} of %{count} in total"
18
- multi_page_html: "Displaying %{model} <b>%{from}&nbsp;-&nbsp;%{to}</b> of <b>%{count}</b> in total"
19
-
20
- # models:
21
- # entry:
22
- # zero: entries
23
- # one: entry
24
- # few: entries
25
- # other: entries
26
-
27
- # line_item:
28
- # page_entries_info:
29
- # single_page:
30
- # zero: "Your shopping cart is empty"
31
- # one: "Displaying one item in your cart"
32
- # other: "Displaying all %{count} items"
33
- # multi_page: "Displaying items %{from} - %{to} of %{count} in total"
@@ -1,170 +0,0 @@
1
- module WillPaginate
2
- # This is a feature backported from Rails 2.1 because of its usefullness not only with will_paginate,
3
- # but in other aspects when managing complex conditions that you want to be reusable.
4
- module NamedScope
5
- # All subclasses of ActiveRecord::Base have two named_scopes:
6
- # * <tt>all</tt>, which is similar to a <tt>find(:all)</tt> query, and
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>
8
- #
9
- # These anonymous scopes tend to be useful when procedurally generating complex queries, where passing
10
- # intermediate values (scopes) around as first-class objects is convenient.
11
- def self.included(base)
12
- base.class_eval do
13
- extend ClassMethods
14
- named_scope :scoped, lambda { |scope| scope }
15
- end
16
- end
17
-
18
- module ClassMethods
19
- def scopes
20
- read_inheritable_attribute(:scopes) || write_inheritable_attribute(:scopes, {})
21
- end
22
-
23
- # Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query,
24
- # such as <tt>:conditions => {:color => :red}, :select => 'shirts.*', :include => :washing_instructions</tt>.
25
- #
26
- # class Shirt < ActiveRecord::Base
27
- # named_scope :red, :conditions => {:color => 'red'}
28
- # named_scope :dry_clean_only, :joins => :washing_instructions, :conditions => ['washing_instructions.dry_clean_only = ?', true]
29
- # end
30
- #
31
- # The above calls to <tt>named_scope</tt> define class methods <tt>Shirt.red</tt> and <tt>Shirt.dry_clean_only</tt>. <tt>Shirt.red</tt>,
32
- # in effect, represents the query <tt>Shirt.find(:all, :conditions => {:color => 'red'})</tt>.
33
- #
34
- # Unlike Shirt.find(...), however, the object returned by <tt>Shirt.red</tt> is not an Array; it resembles the association object
35
- # constructed by a <tt>has_many</tt> declaration. For instance, you can invoke <tt>Shirt.red.find(:first)</tt>, <tt>Shirt.red.count</tt>,
36
- # <tt>Shirt.red.find(:all, :conditions => {:size => 'small'})</tt>. Also, just
37
- # as with the association objects, name scopes acts like an Array, implementing Enumerable; <tt>Shirt.red.each(&block)</tt>,
38
- # <tt>Shirt.red.first</tt>, and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if Shirt.red really were an Array.
39
- #
40
- # These named scopes are composable. For instance, <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are both red and dry clean only.
41
- # Nested finds and calculations also work with these compositions: <tt>Shirt.red.dry_clean_only.count</tt> returns the number of garments
42
- # for which these criteria obtain. Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
43
- #
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
45
- # <tt>has_many</tt> associations. If,
46
- #
47
- # class Person < ActiveRecord::Base
48
- # has_many :shirts
49
- # end
50
- #
51
- # then <tt>elton.shirts.red.dry_clean_only</tt> will return all of Elton's red, dry clean
52
- # only shirts.
53
- #
54
- # Named scopes can also be procedural.
55
- #
56
- # class Shirt < ActiveRecord::Base
57
- # named_scope :colored, lambda { |color|
58
- # { :conditions => { :color => color } }
59
- # }
60
- # end
61
- #
62
- # In this example, <tt>Shirt.colored('puce')</tt> finds all puce shirts.
63
- #
64
- # Named scopes can also have extensions, just as with <tt>has_many</tt> declarations:
65
- #
66
- # class Shirt < ActiveRecord::Base
67
- # named_scope :red, :conditions => {:color => 'red'} do
68
- # def dom_id
69
- # 'red_shirts'
70
- # end
71
- # end
72
- # end
73
- #
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
88
- scopes[name] = lambda do |parent_scope, *args|
89
- Scope.new(parent_scope, case options
90
- when Hash
91
- options
92
- when Proc
93
- options.call(*args)
94
- end) { |*a| yield(*a) if block_given? }
95
- end
96
- (class << self; self end).instance_eval do
97
- define_method name do |*args|
98
- scopes[name].call(self, *args)
99
- end
100
- end
101
- end
102
- end
103
-
104
- class Scope
105
- attr_reader :proxy_scope, :proxy_options
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
-
113
- delegate :scopes, :with_scope, :to => :proxy_scope
114
-
115
- def initialize(proxy_scope, options)
116
- [options[:extend]].flatten.each { |extension| extend extension } if options[:extend]
117
- extend Module.new { |*args| yield(*args) } if block_given?
118
- @proxy_scope, @proxy_options = proxy_scope, options.except(:extend)
119
- end
120
-
121
- def reload
122
- load_found; self
123
- end
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
-
149
- protected
150
- def proxy_found
151
- @found || load_found
152
- end
153
-
154
- private
155
- def method_missing(method, *args)
156
- if scopes.include?(method)
157
- scopes[method].call(self, *args)
158
- else
159
- with_scope :find => proxy_options do
160
- proxy_scope.send(method, *args) { |*a| yield(*a) if block_given? }
161
- end
162
- end
163
- end
164
-
165
- def load_found
166
- @found = find(:all)
167
- end
168
- end
169
- end
170
- end