246 lines
6.2 KiB
Ruby
246 lines
6.2 KiB
Ruby
module Kaminari
|
|
module Helpers
|
|
# A tag stands for an HTML tag inside the paginator.
|
|
# Basically, a tag has its own partial template file, so every tag can be
|
|
# rendered into String using its partial template.
|
|
#
|
|
# The template file should be placed in your app/views/kaminari/ directory
|
|
# with underscored class name (besides the "Tag" class. Tag is an abstract
|
|
# class, so _tag parital is not needed).
|
|
# e.g.) PrevLink -> app/views/kaminari/_prev_link.html.erb
|
|
#
|
|
# If the template file does not exist, it falls back to ancestor classes.
|
|
# e.g.) FirstPageLink -> app/views/kaminari/_first_page_link.html.erb
|
|
# -> app/views/kaminari/_page_link.html.erb
|
|
#
|
|
# When no matching template were found in your app, finally the engine's pre
|
|
# installed template will be used.
|
|
# e.g.) Paginator -> $GEM_HOME/kaminari-x.x.x/app/views/kaminari/_paginator.html.erb
|
|
class Tag
|
|
def initialize(template, options = {}) #:nodoc:
|
|
@template, @options = template, template.options.merge(options)
|
|
end
|
|
|
|
def to_s(locals = {}) #:nodoc:
|
|
@template.render :partial => find_template, :locals => @options.merge(locals)
|
|
end
|
|
|
|
private
|
|
def self.ancestor_renderables
|
|
arr = []
|
|
ancestors.each do |klass|
|
|
return arr if klass == Tag
|
|
arr << klass if klass != Renderable
|
|
end
|
|
end
|
|
|
|
# OMG yet another super dirty hack
|
|
# this method finds
|
|
# 1. a template for the given class from app/views
|
|
# 2. a template for its parent class from app/views
|
|
# 3. the default one inside the engine
|
|
def find_template
|
|
self.class.ancestor_renderables.each do |klass|
|
|
return "kaminari/#{klass.template_filename}" if @template.partial_exists? klass.template_filename
|
|
end
|
|
"kaminari/#{self.class.template_filename}"
|
|
end
|
|
|
|
def page_url_for(page)
|
|
@template.url_for @template.params.merge(:page => (page <= 1 ? nil : page))
|
|
end
|
|
end
|
|
|
|
module Renderable #:nodoc:
|
|
def self.included(base) #:nodoc:
|
|
base.extend ClassMethods
|
|
end
|
|
module ClassMethods #:nodoc:
|
|
def template_filename #:nodoc:
|
|
name.demodulize.underscore
|
|
end
|
|
def included(base) #:nodoc:
|
|
base.extend Renderable::ClassMethods
|
|
end
|
|
end
|
|
end
|
|
|
|
# The container tag
|
|
class Paginator < Tag
|
|
include Renderable
|
|
attr_reader :options
|
|
|
|
def initialize(template, window_options) #:nodoc:
|
|
@template, @options = template, window_options.reverse_merge(template.options)
|
|
# so that this instance can actually "render". Black magic?
|
|
@output_buffer = @template.output_buffer
|
|
end
|
|
|
|
def compose_tags(&block) #:nodoc:
|
|
instance_eval &block if @options[:num_pages] > 1
|
|
end
|
|
|
|
def each_page
|
|
1.upto(@options[:num_pages]) do |i|
|
|
@page = i
|
|
yield PageProxy.new(options, i, @last)
|
|
end
|
|
end
|
|
|
|
%w[current_page first_page_link last_page_link page_link].each do |tag|
|
|
eval <<-DEF
|
|
def #{tag}_tag
|
|
@last = #{tag.classify}.new @template, :page => @page
|
|
end
|
|
DEF
|
|
end
|
|
|
|
%w[prev_link prev_span next_link next_span truncated_span].each do |tag|
|
|
eval <<-DEF
|
|
def #{tag}_tag
|
|
@last = #{tag.classify}.new @template
|
|
end
|
|
DEF
|
|
end
|
|
|
|
def to_s(window_options = {}) #:nodoc:
|
|
super window_options.merge :paginator => self
|
|
end
|
|
|
|
class PageProxy
|
|
def initialize(options, page, last)
|
|
@options, @page, @last = options, page, last
|
|
end
|
|
|
|
def current?
|
|
@page == @options[:current_page]
|
|
end
|
|
|
|
def first?
|
|
@page == 1
|
|
end
|
|
|
|
def last?
|
|
@page == @options[:num_pages]
|
|
end
|
|
|
|
def left_outer?
|
|
@page <= @options[:left] + 1
|
|
end
|
|
|
|
def right_outer?
|
|
@options[:num_pages] - @page <= @options[:right]
|
|
end
|
|
|
|
def inside_window?
|
|
(@page - @options[:current_page]).abs <= @options[:window]
|
|
end
|
|
|
|
def was_truncated?
|
|
@last.is_a? TruncatedSpan
|
|
end
|
|
end
|
|
end
|
|
|
|
# A page
|
|
module Page
|
|
include Renderable
|
|
# target page number
|
|
def page
|
|
raise 'Override page with the actual page value to be a Page.'
|
|
end
|
|
def to_s(locals = {}) #:nodoc:
|
|
super locals.merge(:page => page)
|
|
end
|
|
end
|
|
|
|
# Tag that contains a link
|
|
module Link
|
|
include Renderable
|
|
include Page
|
|
def url
|
|
page_url_for page
|
|
end
|
|
def to_s(locals = {}) #:nodoc:
|
|
super locals.merge(:url => url)
|
|
end
|
|
end
|
|
|
|
# Tag that doesn't contain a link
|
|
module NonLink
|
|
include Renderable
|
|
end
|
|
|
|
# The "previous" page of the current page
|
|
module Prev
|
|
include Renderable
|
|
end
|
|
|
|
# "Previous" without link
|
|
class PrevSpan < Tag
|
|
include NonLink
|
|
include Prev
|
|
end
|
|
|
|
# "Previous" with link
|
|
class PrevLink < Tag
|
|
include Link
|
|
include Prev
|
|
def page #:nodoc:
|
|
@options[:current_page] - 1
|
|
end
|
|
end
|
|
|
|
# The "next" page of the current page
|
|
module Next
|
|
include Renderable
|
|
end
|
|
|
|
# "Next" without link
|
|
class NextSpan < Tag
|
|
include NonLink
|
|
include Next
|
|
end
|
|
|
|
# "Next" with link
|
|
class NextLink < Tag
|
|
include Link
|
|
include Next
|
|
def page #:nodoc:
|
|
@options[:current_page] + 1
|
|
end
|
|
end
|
|
|
|
# Link showing page number
|
|
class PageLink < Tag
|
|
include Page
|
|
include Link
|
|
def page #:nodoc:
|
|
@options[:page]
|
|
end
|
|
end
|
|
|
|
# Non-link tag showing the current page number
|
|
class CurrentPage < Tag
|
|
include Page
|
|
include NonLink
|
|
def page #:nodoc:
|
|
@options[:page]
|
|
end
|
|
end
|
|
|
|
# Link with page number that appears at the leftmost
|
|
class FirstPageLink < PageLink
|
|
end
|
|
|
|
# Link with page number that appears at the rightmost
|
|
class LastPageLink < PageLink
|
|
end
|
|
|
|
# Non-link tag that stands for skipped pages...
|
|
class TruncatedSpan < Tag
|
|
include NonLink
|
|
end
|
|
end
|
|
end
|