C0 code coverage information
Generated on Sat Feb 02 17:44:27 +0100 2008 with rcov 0.8.1.2
Code reported as executed by Ruby looks like this...
and this: this line is also marked as covered.
Lines considered as run by rcov, but not reported by Ruby, look like this,
and this: these lines were inferred by rcov (using simple heuristics).
Finally, here's a line marked as not executed.
1 module Ramaze
2
3 # The BSD License
4 #
5 # Copyright (c) 2004-2007, George K. Moschovitis. (http://www.gmosx.com)
6 # All rights reserved.
7 #
8 # Redistribution and use in source and binary forms, with or without
9 # modification, are permitted provided that the following conditions are
10 # met:
11 #
12 # * Redistributions of source code must retain the above copyright
13 # notice, this list of conditions and the following disclaimer.
14 #
15 # * Redistributions in binary form must reproduce the above copyright
16 # notice, this list of conditions and the following disclaimer in the
17 # documentation and/or other materials provided with the distribution.
18 #
19 # * Neither the name of Nitro nor the names of its contributors may be
20 # used to endorse or promote products derived from this software
21 # without specific prior written permission.
22 #
23 # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
24 # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
25 # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
26 # A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
27 # OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
28 # SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED
29 # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
30 # PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
31 # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
32 # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
33 # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
34 #
35
36 # Displays a collection of entitities in multiple pages.
37 #
38 # === Design
39 #
40 # This pager is carefully designed for scaleability. It stores only the items
41 # for one page. The key parameter is needed, multiple pagers can coexist in a
42 # single page. The pager leverages the SQL LIMIT option to optimize database
43 # interaction.
44 #
45 #
46 # === Example
47 #
48 # class MyController
49 # def index
50 # objs = (0..200).to_a
51 # @entries, @pager = paginate(objs, :limit => 20)
52 # end
53 # end
54 #
55 #
56 # <html>
57 # <head><title>Pager</title></head>
58 # <body>
59 # <?r if pager.navigation? ?>
60 # <div class="pager">#{@pager.navigation}</div>
61 # <?r end ?>
62 # <ul>
63 # <?r @entries.each do |entry| ?>
64 # <li>#{entry}</li>
65 # <?r end ?>
66 # </ul>
67 # </body>
68 # </html>
69 #
70 # === Styling
71 #
72 # The following classes can be used for styling with CSS (provided you put the
73 # pager in a element with class 'pager' like shown above):
74 #
75 # .pager {}
76 # .pager .first {}
77 # .pager .previous {}
78 # .pager .next {}
79 # .pager .last {}
80 # .pager ul {}
81 # .pager li {}
82 # .pager li.active {}
83
84 class Pager
85 include Ramaze::LinkHelper
86
87 # Items per page.
88
89 trait :limit => 10
90
91 # The request key.
92
93 trait :key => '_page'
94
95 # The current page.
96
97 attr_reader :page
98
99 # Items per page.
100
101 attr_reader :limit
102
103 # The total number of pages.
104
105 attr_reader :page_count
106
107 # Total count of items.
108
109 attr_reader :total_count
110
111 # Create a new Pager object.
112 #
113 # request:: Ramaze::Request object providing access to GET parameters
114 # limit:: how many elements go to one page
115 # total_count:: total element count
116 # key:: key used for getting the current page from GET paramaters
117 #
118 # Note: You never have to create this class yourself, use the `paginate()`
119 # convenience method from the PagerHelper.
120
121 def initialize(request, limit, total_count, key = trait[:key])
122 raise 'limit should be > 0' unless limit > 0
123
124 @request, @key = request, key
125 @page = (request.params[key] || 1).to_i
126 @limit = limit
127 set_count(total_count)
128 @start_idx = (@page - 1) * limit
129 end
130
131 # Return the first page index.
132
133 def first_page
134 1
135 end
136
137 # Is the first page displayed?
138
139 def first_page?
140 @page == 1
141 end
142
143 # Return the last page index.
144
145 def last_page
146 return @page_count
147 end
148
149 # Is the last page displayed?
150
151 def last_page?
152 @page == @page_count
153 end
154
155 # Return the index of the previous page.
156
157 def prev_page
158 [@page - 1, 1].max
159 end
160
161 # Return the index of the next page.
162
163 def next_page
164 [@page + 1, @page_count].min
165 end
166
167 # Returns each element for the current page
168
169 def each(&block)
170 @page_items.each(&block)
171 end
172
173 # Iterator
174 # Returns 1-based index.
175
176 def each_with_index(&block)
177 @page_items.each_with_index(&block)
178 end
179
180 # Is the pager empty, ie has one page only?
181
182 def empty?
183 @page_count < 2
184 end
185
186 # Returns true if a navigation is necessary (meaning there is more than one
187 # page)
188
189 def navigation?
190 !empty?
191 end
192
193 # Returns the amount of all elements in all pages.
194
195 def size
196 @total_count
197 end
198
199 # Override this method in your application if needed.
200 #--
201 # TODO: better markup.
202 #++
203
204 def navigation
205 nav = ""
206
207 unless first_page?
208 nav << %{
209 <div class="first"><a href="#{link_first_page}">First</a></div>
210 <div class="previous"><a href="#{link_prev_page}">Previous</a></div>
211 }
212 end
213
214 unless last_page?
215 nav << %{
216 <div class="last"><a href="#{link_last_page}">Last</a></div>
217 <div class="next"><a href="#{link_next_page}">Next</a></div>
218 }
219 end
220
221 nav << %{<ul>}
222
223 for i in nav_range()
224 if i == @page
225 nav << %{<li class="active">#{i}</li>}
226 else
227 nav << %{<li><a href="#{target_uri(i)}">#{i}</a></li>}
228 end
229 end
230
231 nav << %{</ul>}
232
233 return nav
234 end
235
236 # To be used with Og queries.
237
238 def limit
239 if @start_idx > 0
240 { :limit => @limit, :offset => @start_idx }
241 else
242 { :limit => @limit }
243 end
244 end
245
246 # Returns the index of the first element to go into the current page
247
248 def offset
249 @start_idx
250 end
251
252 private
253
254 # Generate the target URI.
255
256 def target_uri(page)
257 params = @request.params.merge(@key => page)
258 Rs(Action.current.method, params)
259 end
260
261 # Generate link for the first page.
262
263 def link_first_page; target_uri(first_page); end
264
265 # Generate link for the last page.
266
267 def link_last_page; target_uri(last_page); end
268
269 # Generate link for the previous page.
270
271 def link_prev_page; target_uri(prev_page); end
272
273 # Generate link for the next page.
274
275 def link_next_page; target_uri(next_page); end
276
277
278 # Returns the range of the current page.
279
280 def page_range
281 s = @idx
282 e = [@idx + @items_limit - 1, all_total_count].min
283
284 return [s, e]
285 end
286
287 # Returns the range of
288 # Override if needed.
289
290 def nav_range
291 # effective range = 10 pages.
292 s = [@page - 5, 1].max
293 e = [@page + 9, @page_count].min
294
295 d = 9 - (e - s)
296 e += d if d < 0
297
298 return (s..e)
299 end
300
301 # generates total and page count of the pager.
302
303 def set_count(total_count)
304 @total_count = total_count
305 @page_count = (@total_count.to_f / @limit).ceil
306 end
307
308 end
309
310 # Pager related helper methods.
311
312 module PagerHelper
313
314 private
315
316 # Helper method that generates a collection of items and the
317 # associated pager object.
318 #
319 # === Example
320 #
321 # items = [ 'item1', 'item2', ... ]
322 # entries, pager = paginate(items, :limit => 10)
323 #
324 # <ul>
325 # <?r for entry in entries ?>
326 # <li>#{entry.to_link}</li>
327 # <?r end ?>
328 # </ul>
329 # #{pager.navigation}
330 #
331 # === Og Example
332 #
333 # entries, pager = paginate(Article, :where => 'title LIKE..', :limit => 10)
334 #
335 # or
336 #
337 # entries, pager = paginate(article.comments, :limit => 10)
338
339 def paginate(items, options = {})
340 limit = options.delete(:limit) || Pager.trait[:limit]
341 pager_key = options.delete(:pager_key) || Pager.trait[:key]
342
343 case items
344 when Array
345 pager = Pager.new(request, limit, items.size, pager_key)
346 items = items.slice(pager.offset, pager.limit[:limit])
347 return items, pager
348 end
349
350 if defined?(Og) && items.is_a?(Og::Collection)
351 pager = Pager.new(request, limit, items.count, pager_key)
352 options.update(pager.limit)
353 items = items.reload(options)
354 return items, pager
355 elsif defined?(Og::EntityMixin) && items.is_a?(Og::EntityMixin) ||
356 defined?(Og::Mixin) && items.is_a?(Og::Mixin) # Og <= 0.41
357 pager = Pager.new(request, limit, items.count(options), pager_key)
358 options.update(pager.limit)
359 items = items.all(options)
360 return items, pager
361 end
362
363 raise "No suitable pagination method for #{items.inspect}"
364 end
365
366 end
367
368 end
Generated using the rcov code coverage analysis tool for Ruby version 0.8.1.2.