# * George Moschovitis # (c) 2004-2005 Navel, all rights reserved. # $Id: pager.rb 266 2005-02-28 14:50:48Z gmosx $ require 'nitro/uri' module N; module UI # Displays a collection of entitities in multiple pages. # # === Design # # The new version is carefully designed for scaleability. It stores # only the items for one page. The name parameter is needed, multiple # pagers can coexist in a single page. Unlike older pagers this # pager leverages the SQL LIMIT option to optimize database interaction. # # === Example # # @pager = UI::Pager.new('entries', @request, 5) # @entries = BlogEntry.all("ORDER BY oid #{@pager.sql_limit}") # @pager.set(BlogEntry.count) # # default navigation (customize with css): #
#{@pager.navigation}
# # custom navigation: # # # # # # # #
PreviousNext
#-- # INVESTIGATE: # mysql> SELECT SQL_CALC_FOUND_ROWS * FROM tbl_name # -> WHERE id > 100 LIMIT 10; # mysql> SELECT FOUND_ROWS(); #++ class Pager < Array attr_accessor :name, :idx, :page, :page_count # Total count of items attr_accessor :total_count # Page items attr_accessor :page_items # Read needed variables from the request. attr_accessor :request def initialize(name, request, items_per_page = 10, items = nil) raise "items_per_page should be > 0" unless items_per_page > 0 @request, @name = request, name @page = request.query.fetch("__pg#{@name}", 1).to_i @items_per_page = items_per_page @start_idx = (@page - 1) * items_per_page if items set(items.size()) # gmosx, FIXME: not exactly what i want! items.slice!(@start_idx, @items_per_page) end end def set(total_count) @total_count = total_count @page_count = (@total_count.to_f() / @items_per_page).ceil() end def first_page return 1 end def last_page return @page_count end def previous_page return [@page - 1, 1].max() end def next_page return [@page + 1, @page_count].min() end # Iterator def each(&block) @page_items.each(&block) end # Iterator # Returns 1-based index. def each_with_index idx = @start_idx for item in @page_items yield(idx + 1, item) idx += 1 end end def empty? return @items.empty? end def size return @items.size() end # Returns the range of the current page. def page_range s = @idx e = [@idx + @items_per_page - 1, all_total_count].min return [s, e] end # Override if needed. def nav_range # effective range = 10 pages. s = [@page - 5, 1].max() e = [@page + 9, @page_count].min() d = 9 - (e - s) e += d if d < 0 return (s..e) end # Override this method in your application # if needed. # TODO: better markup. def navigation nav = "" unless @page == first_page() nav << %{
First
} end unless @page == last_page() nav << %{
Last
} end nav << %{} return nav end # Create an appropriate SQL limit clause. # Returns postgres/mysql compatible limit. def sql_limit if @start_idx > 0 return "LIMIT #{@items_per_page} OFFSET #{@start_idx}" else # gmosx: perhaps this is optimized ? naaaaaah... return "LIMIT #{@items_per_page}" end end # Returns the current offset. The offset is zero-based. def offset (@page-1) * @items_per_page end # Generate the target URI. # def target_uri(page) params = {"__pg#{@name}" => page} return N::UriUtils.update_query_string(@request.uri.to_s, params) end end end; end