arid_cache 1.2.0 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.rdoc +27 -9
- data/VERSION +1 -1
- data/arid_cache.gemspec +11 -4
- data/lib/arid_cache/cache_proxy/options.rb +73 -0
- data/lib/arid_cache/cache_proxy/result_processor.rb +199 -0
- data/lib/arid_cache/cache_proxy/utilities.rb +35 -0
- data/lib/arid_cache/cache_proxy.rb +63 -304
- data/lib/arid_cache.rb +1 -1
- data/spec/arid_cache/{cache_proxy_result_spec.rb → cache_proxy/cached_result_spec.rb} +2 -2
- data/spec/arid_cache/cache_proxy/options_spec.rb +80 -0
- data/spec/arid_cache/cache_proxy/result_processor_spec.rb +217 -0
- data/spec/arid_cache/cache_proxy_spec.rb +118 -89
- data/test/console +2 -0
- metadata +13 -6
@@ -1,13 +1,15 @@
|
|
1
|
+
require 'arid_cache/cache_proxy/utilities'
|
2
|
+
require 'arid_cache/cache_proxy/options'
|
3
|
+
require 'arid_cache/cache_proxy/result_processor'
|
4
|
+
|
1
5
|
module AridCache
|
2
6
|
class CacheProxy
|
3
|
-
attr_accessor :object, :key, :opts, :blueprint, :cached, :cache_key, :block, :records, :combined_options, :klass
|
4
7
|
|
5
|
-
# AridCache::CacheProxy::
|
8
|
+
# AridCache::CacheProxy::CachedResult
|
6
9
|
#
|
7
|
-
# This struct is stored in the cache and stores information
|
8
|
-
#
|
9
|
-
|
10
|
-
|
10
|
+
# This struct is stored in the cache and stores information about a
|
11
|
+
# collection of ActiveRecords.
|
12
|
+
CachedResult = Struct.new(:ids, :klass, :count) do
|
11
13
|
def has_count?
|
12
14
|
!count.nil?
|
13
15
|
end
|
@@ -25,6 +27,12 @@ module AridCache
|
|
25
27
|
end
|
26
28
|
end
|
27
29
|
|
30
|
+
OPTIONS_FOR_PAGINATE = [:page, :per_page, :total_entries, :finder]
|
31
|
+
OPTIONS_FOR_CACHE_PROXY = [:raw, :clear]
|
32
|
+
OPTIONS_FOR_FIND = [ :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :having, :from, :lock ]
|
33
|
+
OPTIONS_FOR_CACHE = [ :expires_in ]
|
34
|
+
OPTIONS_FOR_CACHE_KEY = [ :auto_expire ]
|
35
|
+
|
28
36
|
#
|
29
37
|
# Managing your caches
|
30
38
|
#
|
@@ -34,26 +42,36 @@ module AridCache
|
|
34
42
|
end
|
35
43
|
|
36
44
|
def self.clear_class_caches(object)
|
37
|
-
key = (
|
45
|
+
key = (Utilities.object_class(object)).name.downcase + '-'
|
38
46
|
Rails.cache.delete_matched(%r[arid-cache-#{key}.*])
|
39
47
|
end
|
40
48
|
|
41
49
|
def self.clear_instance_caches(object)
|
42
|
-
key = AridCache::Inflector.pluralize((
|
50
|
+
key = AridCache::Inflector.pluralize((Utilities.object_class(object)).name).downcase
|
43
51
|
Rails.cache.delete_matched(%r[arid-cache-#{key}.*])
|
44
52
|
end
|
45
53
|
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
54
|
+
# Clear the cached result for this cache only
|
55
|
+
def clear_cached
|
56
|
+
Rails.cache.delete(@cache_key, @options.opts_for_cache)
|
57
|
+
end
|
58
|
+
|
59
|
+
#
|
60
|
+
# Initialize
|
61
|
+
#
|
53
62
|
|
54
|
-
|
55
|
-
|
56
|
-
|
63
|
+
def initialize(receiver, method, opts={}, &block)
|
64
|
+
@receiver = receiver
|
65
|
+
@method = method
|
66
|
+
@block = block
|
67
|
+
@blueprint = AridCache.store.find(@receiver, @method)
|
68
|
+
|
69
|
+
# Combine the options from the blueprint with the options for this call
|
70
|
+
opts = opts.symbolize_keys
|
71
|
+
@options = Options.new(@blueprint.nil? ? opts : @blueprint.opts.merge(opts))
|
72
|
+
@options[:receiver] = receiver
|
73
|
+
@cache_key = @receiver.arid_cache_key(@method, @options.opts_for_cache_key)
|
74
|
+
@cached = Rails.cache.read(@cache_key, @options.opts_for_cache)
|
57
75
|
end
|
58
76
|
|
59
77
|
#
|
@@ -61,308 +79,49 @@ module AridCache
|
|
61
79
|
#
|
62
80
|
|
63
81
|
# Return a count of ids in the cache, or return whatever is in the cache if it is
|
64
|
-
# not a CacheProxy::
|
82
|
+
# not a CacheProxy::CachedResult
|
65
83
|
def fetch_count
|
66
|
-
|
67
|
-
|
68
|
-
elsif cached.is_a?(AridCache::CacheProxy::Result)
|
69
|
-
cached.has_count? ? cached.count : execute_count
|
70
|
-
elsif cached.is_a?(Fixnum)
|
71
|
-
cached
|
72
|
-
elsif cached.respond_to?(:count)
|
73
|
-
cached.count
|
74
|
-
else
|
75
|
-
cached # what else can we do? return it
|
76
|
-
end
|
84
|
+
@options[:count_only] = true
|
85
|
+
result_processor.to_result
|
77
86
|
end
|
78
87
|
|
79
88
|
# Return a list of records using the options provided. If the item in the cache
|
80
|
-
# is not a CacheProxy::
|
89
|
+
# is not a CacheProxy::CachedResult it is returned after applying options. If there is nothing in the cache
|
81
90
|
# the block defining the cache is exectued. If the :raw option is true, returns the
|
82
|
-
# CacheProxy::
|
83
|
-
# are
|
91
|
+
# CacheProxy::CachedResult unmodified, ignoring other options, except where those options
|
92
|
+
# are needed to initialize the cache.
|
84
93
|
def fetch
|
85
|
-
|
86
|
-
|
87
|
-
result = if refresh_cache?
|
88
|
-
execute_find(@raw_result)
|
89
|
-
elsif cached.is_a?(AridCache::CacheProxy::Result)
|
90
|
-
if cached.has_ids? && @raw_result
|
91
|
-
self.cached # return it unmodified
|
92
|
-
elsif cached.has_ids?
|
93
|
-
fetch_from_cache # return a list of active records after applying options
|
94
|
-
else # true if we have only calculated the count thus far
|
95
|
-
execute_find(@raw_result)
|
96
|
-
end
|
97
|
-
else
|
98
|
-
cached # some base type, return it unmodified
|
99
|
-
end
|
100
|
-
end
|
101
|
-
|
102
|
-
# Clear the cached result for this cache only
|
103
|
-
def clear_cached
|
104
|
-
Rails.cache.delete(self.cache_key, opts_for_cache)
|
105
|
-
end
|
106
|
-
|
107
|
-
# Return the cached result for this object's key
|
108
|
-
def cached
|
109
|
-
@cached ||= Rails.cache.read(self.cache_key, opts_for_cache)
|
94
|
+
result_processor.to_result
|
110
95
|
end
|
111
96
|
|
112
|
-
# Return the class of the cached results i.e. if the cached result is a
|
113
|
-
# list of Album records, then klass returns Album. If there is nothing
|
114
|
-
# in the cache, then the class is inferred to be the class of the object
|
115
|
-
# that the cached method is being called on.
|
116
|
-
def klass
|
117
|
-
@klass ||= if self.cached && self.cached.is_a?(AridCache::CacheProxy::Result)
|
118
|
-
self.cached.klass
|
119
|
-
else
|
120
|
-
object_base_class
|
121
|
-
end
|
122
|
-
end
|
123
|
-
|
124
|
-
|
125
97
|
private
|
126
98
|
|
127
|
-
# Return a
|
128
|
-
# the
|
129
|
-
|
130
|
-
|
131
|
-
# a regular list of ActiveRecord results is returned.
|
132
|
-
#
|
133
|
-
# If no :order is specified, the current ordering of the ids is
|
134
|
-
# preserved with some fancy SQL.
|
135
|
-
def fetch_from_cache
|
136
|
-
if paginate?
|
137
|
-
|
138
|
-
# Return a paginated collection
|
139
|
-
if cached.ids.empty?
|
140
|
-
|
141
|
-
# No ids, return an empty WillPaginate result
|
142
|
-
[].paginate(opts_for_paginate)
|
143
|
-
|
144
|
-
elsif combined_options.include?(:order)
|
145
|
-
|
146
|
-
# An order has been specified. We have to go to the database
|
147
|
-
# and paginate there because the contents of the requested
|
148
|
-
# page will be different.
|
149
|
-
klass.paginate(cached.ids, { :total_entries => cached.ids.size }.merge(opts_for_find.merge(opts_for_paginate)))
|
150
|
-
|
151
|
-
else
|
152
|
-
|
153
|
-
# Order is unchanged. We can paginate in memory and only select
|
154
|
-
# those ids that we actually need. This is the most efficient.
|
155
|
-
paged_ids = cached.ids.paginate(opts_for_paginate)
|
156
|
-
paged_ids.replace(klass.find_all_by_id(paged_ids, opts_for_find(paged_ids)))
|
157
|
-
|
158
|
-
end
|
159
|
-
|
160
|
-
elsif cached.ids.empty?
|
161
|
-
|
162
|
-
# We are returning a regular (non-paginated) result.
|
163
|
-
# If we don't have any ids, that's an empty result
|
164
|
-
# in any language.
|
165
|
-
[]
|
166
|
-
|
167
|
-
elsif combined_options.include?(:order)
|
168
|
-
|
169
|
-
# An order has been specified, so use it.
|
170
|
-
klass.find_all_by_id(cached.ids, opts_for_find)
|
171
|
-
|
172
|
-
else
|
173
|
-
|
174
|
-
# No order has been specified, so we have to maintain
|
175
|
-
# the current order. We do this by passing some extra
|
176
|
-
# SQL which orders by the current array ordering.
|
177
|
-
offset, limit = combined_options.delete(:offset) || 0, combined_options.delete(:limit) || cached.count
|
178
|
-
ids = cached.ids[offset, limit]
|
179
|
-
|
180
|
-
klass.find_all_by_id(ids, opts_for_find(ids))
|
181
|
-
end
|
99
|
+
# Return a ResultProcessor instance. Seed the cache if we need to, otherwise
|
100
|
+
# use what is in the cache.
|
101
|
+
def result_processor
|
102
|
+
seed_cache? ? seed_cache : ResultProcessor.new(@cached, @options)
|
182
103
|
end
|
183
104
|
|
184
|
-
|
185
|
-
|
105
|
+
# Return a boolean indicating whether we need to seed the cache. Seed the cache
|
106
|
+
# if :force => true, the cache is empty or records have been requested and there
|
107
|
+
# are none in the cache yet.
|
108
|
+
def seed_cache?
|
109
|
+
@cached.nil? || @options.force? || (@cached.is_a?(CachedResult) && !@options.count_only? && !@cached.has_ids?)
|
186
110
|
end
|
187
111
|
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
# Seed the cache by executing the stored block (or by calling a method on the object).
|
198
|
-
# Then apply any options like pagination or ordering before returning the result, which
|
199
|
-
# is either some base type, or usually, a list of active records.
|
200
|
-
#
|
201
|
-
# Options:
|
202
|
-
# raw - if true, return the CacheProxy::Result after seeding the cache, ignoring
|
203
|
-
# other options. Default is false.
|
204
|
-
def execute_find(raw = false)
|
205
|
-
get_records
|
206
|
-
cached = AridCache::CacheProxy::Result.new
|
207
|
-
|
208
|
-
if !records.is_a?(Enumerable) || (!records.empty? && !records.first.is_a?(::ActiveRecord::Base))
|
209
|
-
cached = records # some base type, cache it as itself
|
210
|
-
else
|
211
|
-
cached.ids = records.collect(&:id)
|
212
|
-
cached.count = records.size
|
213
|
-
if records.respond_to?(:proxy_reflection) # association proxy
|
214
|
-
cached.klass = records.proxy_reflection.klass
|
215
|
-
elsif !records.empty?
|
216
|
-
cached.klass = records.first.class
|
217
|
-
else
|
218
|
-
cached.klass = object_base_class
|
219
|
-
end
|
220
|
-
end
|
221
|
-
Rails.cache.write(cache_key, cached, opts_for_cache)
|
222
|
-
self.cached = cached
|
223
|
-
|
224
|
-
# Return the raw result?
|
225
|
-
return self.cached if raw
|
226
|
-
|
227
|
-
# An order has been specified. We have to go to the database
|
228
|
-
# to order because we can't be sure that the current order is the same as the cache.
|
229
|
-
if cached.is_a?(AridCache::CacheProxy::Result) && combined_options.include?(:order)
|
230
|
-
self.klass = self.cached.klass # TODO used by fetch_from_cache needs refactor
|
231
|
-
fetch_from_cache
|
232
|
-
else
|
233
|
-
process_result_in_memory(records)
|
234
|
-
end
|
112
|
+
# Seed the cache by executing the stored block (or by calling a method on the object)
|
113
|
+
# and storing the result in the cache. Return the processed result ready to return
|
114
|
+
# to the user.
|
115
|
+
def seed_cache
|
116
|
+
block = @block || (@blueprint && @blueprint.proc)
|
117
|
+
block_result = block.nil? ? @receiver.instance_eval(@method) : @receiver.instance_eval(&block)
|
118
|
+
@result = ResultProcessor.new(block_result, @options)
|
119
|
+
write_cache(@result.to_cache)
|
120
|
+
@result
|
235
121
|
end
|
236
122
|
|
237
|
-
|
238
|
-
|
239
|
-
# to load the page rather than just using the records we have already fetched.
|
240
|
-
#
|
241
|
-
# If we are not paginating and the options include :limit (and optionally :offset)
|
242
|
-
# apply the limit and offset to the records before returning them.
|
243
|
-
#
|
244
|
-
# Otherwise we have an issue where all the records are returned the first time
|
245
|
-
# the collection is loaded, but on subsequent calls the options_for_find are
|
246
|
-
# included and you get different results. Note that with options like :order
|
247
|
-
# this cannot be helped. We don't want to modify the query that generates the
|
248
|
-
# collection because the idea is to allow getting different perspectives of the
|
249
|
-
# cached collection without relying on modifying the collection as a whole.
|
250
|
-
#
|
251
|
-
# If you do want a specialized, modified, or subset of the collection it's best
|
252
|
-
# to define it in a block and have a new cache for it:
|
253
|
-
#
|
254
|
-
# model.my_special_collection { the_collection(:order => 'new order', :limit => 10) }
|
255
|
-
def process_result_in_memory(records)
|
256
|
-
if opts.include?(:page)
|
257
|
-
records = records.respond_to?(:to_a) ? records.to_a : records
|
258
|
-
records.respond_to?(:paginate) ? records.paginate(opts_for_paginate) : records
|
259
|
-
elsif opts.include?(:limit)
|
260
|
-
records = records.respond_to?(:to_a) ? records.to_a : records
|
261
|
-
offset = opts[:offset] || 0
|
262
|
-
records[offset, opts[:limit]]
|
263
|
-
else
|
264
|
-
records
|
265
|
-
end
|
266
|
-
end
|
267
|
-
|
268
|
-
def execute_count
|
269
|
-
get_records
|
270
|
-
cached = AridCache::CacheProxy::Result.new
|
271
|
-
|
272
|
-
# Just get the count if we can.
|
273
|
-
#
|
274
|
-
# Because of how AssociationProxy works, if we even look at it, it'll
|
275
|
-
# trigger the query. So don't look.
|
276
|
-
#
|
277
|
-
# Association proxy or named scope. Check for an association first, because
|
278
|
-
# it doesn't trigger the select if it's actually named scope. Calling respond_to?
|
279
|
-
# on an association proxy will hower trigger a select because it loads up the target
|
280
|
-
# and passes the respond_to? on to it.
|
281
|
-
if records.respond_to?(:proxy_reflection) || records.respond_to?(:proxy_options)
|
282
|
-
cached.count = records.count # just get the count
|
283
|
-
cached.klass = object_base_class
|
284
|
-
elsif records.is_a?(Enumerable) && (records.empty? || records.first.is_a?(::ActiveRecord::Base))
|
285
|
-
cached.ids = records.collect(&:id) # get everything now that we have it
|
286
|
-
cached.count = records.size
|
287
|
-
cached.klass = records.empty? ? object_base_class : records.first.class
|
288
|
-
else
|
289
|
-
cached = records # some base type, cache it as itself
|
290
|
-
end
|
291
|
-
|
292
|
-
Rails.cache.write(cache_key, cached, opts_for_cache)
|
293
|
-
self.cached = cached
|
294
|
-
cached.respond_to?(:count) ? cached.count : cached
|
295
|
-
end
|
296
|
-
|
297
|
-
OPTIONS_FOR_PAGINATE = [:page, :per_page, :total_entries, :finder]
|
298
|
-
|
299
|
-
# Filter options for paginate, if *klass* is set, we get the :per_page value from it.
|
300
|
-
def opts_for_paginate
|
301
|
-
paginate_opts = combined_options.reject { |k,v| !OPTIONS_FOR_PAGINATE.include?(k) }
|
302
|
-
paginate_opts[:finder] = :find_all_by_id unless paginate_opts.include?(:finder)
|
303
|
-
paginate_opts[:per_page] = klass.per_page if klass && !paginate_opts.include?(:per_page)
|
304
|
-
paginate_opts
|
305
|
-
end
|
306
|
-
|
307
|
-
OPTIONS_FOR_FIND = [ :conditions, :include, :joins, :limit, :offset, :order, :select, :readonly, :group, :having, :from, :lock ]
|
308
|
-
|
309
|
-
# Preserve the original order of the results if no :order option is specified.
|
310
|
-
#
|
311
|
-
# @arg ids array of ids to order by unless an :order option is specified. If not
|
312
|
-
# specified, cached.ids is used.
|
313
|
-
def opts_for_find(ids=nil)
|
314
|
-
ids ||= cached.ids
|
315
|
-
find_opts = combined_options.reject { |k,v| !OPTIONS_FOR_FIND.include?(k) }
|
316
|
-
find_opts[:order] = preserve_order(ids) unless find_opts.include?(:order)
|
317
|
-
find_opts
|
318
|
-
end
|
319
|
-
|
320
|
-
OPTIONS_FOR_CACHE = [ :expires_in ]
|
321
|
-
|
322
|
-
def opts_for_cache
|
323
|
-
combined_options.reject { |k,v| !OPTIONS_FOR_CACHE.include?(k) }
|
324
|
-
end
|
325
|
-
|
326
|
-
OPTIONS_FOR_CACHE_KEY = [ :auto_expire ]
|
327
|
-
|
328
|
-
def opts_for_cache_key
|
329
|
-
combined_options.reject { |k,v| !OPTIONS_FOR_CACHE_KEY.include?(k) }
|
330
|
-
end
|
331
|
-
|
332
|
-
OPTIONS_FOR_CACHE_PROXY = [:raw, :clear]
|
333
|
-
|
334
|
-
# Returns options that affect the cache proxy result
|
335
|
-
def opts_for_cache_proxy
|
336
|
-
combined_options.reject { |k,v| !OPTIONS_FOR_CACHE_PROXY.include?(k) }
|
337
|
-
end
|
338
|
-
|
339
|
-
def object_base_class #:nodoc:
|
340
|
-
object.is_a?(Class) ? object : object.class
|
341
|
-
end
|
342
|
-
|
343
|
-
# Generate an ORDER BY clause that preserves the ordering of the ids in *ids*.
|
344
|
-
#
|
345
|
-
# The method we use depends on the database adapter because only MySQL
|
346
|
-
# supports the ORDER BY FIELD() function. For other databases we use
|
347
|
-
# a CASE statement.
|
348
|
-
#
|
349
|
-
# TODO: is it quicker to sort in memory?
|
350
|
-
def preserve_order(ids)
|
351
|
-
column = if self.klass.respond_to?(:table_name)
|
352
|
-
::ActiveRecord::Base.connection.quote_table_name(self.klass.table_name) + '.id'
|
353
|
-
else
|
354
|
-
"id"
|
355
|
-
end
|
356
|
-
|
357
|
-
if ids.empty?
|
358
|
-
nil
|
359
|
-
elsif ::ActiveRecord::Base.is_mysql_adapter?
|
360
|
-
"FIELD(#{column},#{ids.join(',')})"
|
361
|
-
else
|
362
|
-
order = ''
|
363
|
-
ids.each_index { |i| order << "WHEN #{column}=#{ids[i]} THEN #{i+1} " }
|
364
|
-
"CASE " + order + " END"
|
365
|
-
end
|
123
|
+
def write_cache(data)
|
124
|
+
Rails.cache.write(@cache_key, data, @options.opts_for_cache)
|
366
125
|
end
|
367
126
|
end
|
368
127
|
end
|
data/lib/arid_cache.rb
CHANGED
@@ -1,9 +1,9 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
|
-
describe AridCache::CacheProxy::
|
3
|
+
describe AridCache::CacheProxy::CachedResult do
|
4
4
|
before :each do
|
5
5
|
class X; end
|
6
|
-
@result = AridCache::CacheProxy::
|
6
|
+
@result = AridCache::CacheProxy::CachedResult.new
|
7
7
|
end
|
8
8
|
|
9
9
|
it "should set the klass from a class" do
|
@@ -0,0 +1,80 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe AridCache::CacheProxy::Options do
|
4
|
+
def new_options(opts={})
|
5
|
+
AridCache::CacheProxy::Options.new(opts)
|
6
|
+
end
|
7
|
+
|
8
|
+
describe "defaults" do
|
9
|
+
before :each do
|
10
|
+
@opt = new_options
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should have default" do
|
14
|
+
@opt.force?.should be_false
|
15
|
+
@opt.paginate?.should be_false
|
16
|
+
@opt.raw?.should be_false
|
17
|
+
@opt.count_only?.should be_false
|
18
|
+
@opt.order_by_proc?.should be_false
|
19
|
+
@opt.order_by_key?.should be_false
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
it "force?" do
|
24
|
+
new_options(:force => true).force?.should be_true
|
25
|
+
end
|
26
|
+
|
27
|
+
it "paginate?" do
|
28
|
+
new_options(:page => 1).paginate?.should be_true
|
29
|
+
new_options(:per_page => 1).paginate?.should be_false
|
30
|
+
new_options(:page => 1, :per_page => 1).paginate?.should be_true
|
31
|
+
end
|
32
|
+
|
33
|
+
it "raw?" do
|
34
|
+
new_options(:raw => true).raw?.should be_true
|
35
|
+
end
|
36
|
+
|
37
|
+
it "count_only?" do
|
38
|
+
new_options(:count_only => true).count_only?.should be_true
|
39
|
+
end
|
40
|
+
|
41
|
+
it "order options" do
|
42
|
+
@opt = new_options(:order => 'key')
|
43
|
+
@opt.order_by_key?.should be_true
|
44
|
+
@opt.order_by_proc?.should be_false
|
45
|
+
@opt = new_options(:order => :symbol)
|
46
|
+
@opt.order_by_key?.should be_true
|
47
|
+
@opt.order_by_proc?.should be_false
|
48
|
+
@opt = new_options(:order => Proc.new {})
|
49
|
+
@opt.order_by_key?.should be_false
|
50
|
+
@opt.order_by_proc?.should be_true
|
51
|
+
end
|
52
|
+
|
53
|
+
describe "options for paginate" do
|
54
|
+
before :each do
|
55
|
+
@result_klass = Class.new do
|
56
|
+
def self.per_page; 23; end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should get per_page from the result_klass" do
|
61
|
+
@opts = new_options(:result_klass => @result_klass).opts_for_paginate
|
62
|
+
@opts[:per_page].should == 23
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should use the provided per_page value" do
|
66
|
+
@opts = new_options(:result_klass => @result_klass, :per_page => 3).opts_for_paginate
|
67
|
+
@opts[:per_page].should == 3
|
68
|
+
end
|
69
|
+
|
70
|
+
it "should set total_entries" do
|
71
|
+
new_options.opts_for_paginate[:total_entries].should be_nil
|
72
|
+
@opts = new_options.opts_for_paginate((1..10).to_a)
|
73
|
+
@opts[:total_entries].should == 10
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should use find_all_by_id as the finder" do
|
77
|
+
new_options.opts_for_paginate[:finder].should == :find_all_by_id
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|