1
0
Fork 0
mirror of https://github.com/kaminari/kaminari.git synced 2022-11-09 13:44:37 -05:00

Merge branch 'refactor'

This commit is contained in:
Akira Matsuda 2011-02-17 11:28:31 +09:00
commit 60811bf5d7
6 changed files with 372 additions and 153 deletions

View file

@ -4,7 +4,26 @@
num_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
paginator: the paginator that renders the pagination tags inside
-%>
<nav class='pagination'>
<%= content_for :kaminari_paginator_tags %>
</nav>
<%= paginator.render do -%>
<nav class='pagination'>
<%= current_page > 1 ? prev_link_tag : prev_span_tag %>
<% each_page do |page| -%>
<% if page.current? -%>
<%= current_page_tag %>
<% elsif page.left_outer? || page.right_outer? || page.inside_window? -%>
<% if page.first? -%>
<%= first_page_link_tag %>
<% elsif page.last? -%>
<%= last_page_link_tag %>
<% else -%>
<%= page_link_tag %>
<% end -%>
<% elsif !page.was_truncated? -%>
<%= truncated_span_tag %>
<% end -%>
<% end -%>
<%= num_pages > current_page ? next_link_tag : next_span_tag %>
</nav>
<% end -%>

View file

@ -4,5 +4,20 @@
num_pages: total number of pages
per_page: number of items to fetch per page
remote: data-remote
%nav.pagination
= content_for :kaminari_paginator_tags
paginator: the paginator that renders the pagination tags inside
= paginator.render do
%nav.pagination
= current_page > 1 ? prev_link_tag : prev_span_tag
- each_page do |page|
- if page.current?
= current_page_tag
- elsif page.left_outer? || page.right_outer? || page.inside_window?
- if page.first?
= first_page_link_tag
- elsif page.last?
= last_page_link_tag
- else
= page_link_tag
- elsif !page.was_truncated?
= truncated_span_tag
= num_pages > current_page ? next_link_tag : next_span_tag

View file

@ -2,38 +2,14 @@ require File.join(File.dirname(__FILE__), 'tags')
module Kaminari
module Helpers
class PaginationRenderer
# Wraps the template context and helps each tag render itselves
class TemplateWrapper
attr_reader :options, :params
delegate :render, :url_for, :to => :@template
def initialize(template, options) #:nodoc:
@template, @options = template, options
@params = options[:params] ? template.params.merge(options.delete :params) : template.params
@left, @window, @right = (options[:left] || options[:outer_window] || 1), (options[:window] || options[:inner_window] || 4), (options[:right] || options[:outer_window] || 1)
end
def tagify_links #:nodoc:
num_pages, current_page, left, window, right = @options[:num_pages], @options[:current_page], @left, @window, @right
return [] if num_pages <= 1
tags = []
tags << (current_page > 1 ? PrevLink.new(self) : PrevSpan.new(self))
1.upto(num_pages) do |i|
if i == current_page
tags << CurrentPage.new(self, :page => i)
elsif (i <= left + 1) || ((num_pages - i) <= right) || ((i - current_page).abs <= window)
case i
when 1
tags << FirstPageLink.new(self, :page => i)
when num_pages
tags << LastPageLink.new(self, :page => i)
else
tags << PageLink.new(self, :page => i)
end
else
tags << TruncatedSpan.new(self) unless tags.last.is_a? TruncatedSpan
end
end
tags << (num_pages > current_page ? NextLink.new(self) : NextSpan.new(self))
end
def partial_exists?(name) #:nodoc:
@ -41,12 +17,8 @@ module Kaminari
resolver.find_all(*args_for_lookup(name)).present?
end
def to_s #:nodoc:
suppress_logging_render_partial do
clear_content_for :kaminari_paginator_tags
@template.content_for :kaminari_paginator_tags, tagify_links.join.html_safe
Paginator.new(self).to_s
end
def output_buffer #:nodoc:
@template.instance_variable_get('@output_buffer')
end
private
@ -63,19 +35,34 @@ module Kaminari
method.call name, ['kaminari'], true, []
end
end
end
def method_missing(meth, *args, &blk)
@template.send meth, *args, &blk
# The main class that controlls the whole process
class PaginationRenderer
def initialize(template, options) #:nodoc:
@window_options = {}.tap do |h|
h[:window] = options.delete(:window) || options.delete(:inner_window) || 4
outer_window = options.delete(:outer_window)
h[:left] = options.delete(:left) || outer_window || 1
h[:right] = options.delete(:right) || outer_window || 1
end
@template = TemplateWrapper.new(template, options)
end
def to_s #:nodoc:
suppress_logging_render_partial do
Paginator.new(@template, @window_options).to_s
end
end
private
# dirty hack
def suppress_logging_render_partial(&blk)
if subscriber = ActionView::LogSubscriber.log_subscribers.detect {|ls| ls.is_a? ActionView::LogSubscriber}
class << subscriber
alias_method :render_partial_with_logging, :render_partial
# do nothing
def render_partial(event)
end
def render_partial(event); end
end
ret = blk.call
class << subscriber
@ -87,11 +74,6 @@ module Kaminari
blk.call
end
end
# another dirty hack
def clear_content_for(name)
@template.instance_variable_get('@_content_for')[name] = ActiveSupport::SafeBuffer.new
end
end
# = Helpers

View file

@ -17,12 +17,12 @@ module Kaminari
# installed template will be used.
# e.g.) Paginator -> $GEM_HOME/kaminari-x.x.x/app/views/kaminari/_paginator.html.erb
class Tag
def initialize(renderer, options = {}) #:nodoc:
@renderer, @options = renderer, renderer.options.merge(options)
def initialize(template, options = {}) #:nodoc:
@template, @options = template, template.options.merge(options)
end
def to_s(locals = {}) #:nodoc:
@renderer.render :partial => find_template, :locals => @options.merge(locals)
@template.render :partial => find_template, :locals => @options.merge(locals)
end
private
@ -41,13 +41,13 @@ module Kaminari
# 3. the default one inside the engine
def find_template
self.class.ancestor_renderables.each do |klass|
return "kaminari/#{klass.template_filename}" if @renderer.partial_exists? klass.template_filename
return "kaminari/#{klass.template_filename}" if @template.partial_exists? klass.template_filename
end
"kaminari/#{self.class.template_filename}"
end
def page_url_for(page)
@renderer.url_for @renderer.params.merge(:page => (page <= 1 ? nil : page))
@template.url_for @template.params.merge(:page => (page <= 1 ? nil : page))
end
end
@ -65,6 +65,99 @@ module Kaminari
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
# render given block as a view template
def render(&block)
instance_eval &block if @options[:num_pages] > 1
nil
end
# enumerate each page providing PageProxy object as the block parameter
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
# Wraps a "page number" and provides some utility methods
class PageProxy
def initialize(options, page, last) #:nodoc:
@options, @page, @last = options, page, last
end
# the page number
def number
@page
end
# current page or not
def current?
@page == @options[:current_page]
end
# the first page or not
def first?
@page == 1
end
# the last page or not
def last?
@page == @options[:num_pages]
end
# within the left outer window or not
def left_outer?
@page <= @options[:left] + 1
end
# within the right outer window or not
def right_outer?
@options[:num_pages] - @page <= @options[:right]
end
# inside the inner window or not
def inside_window?
(@page - @options[:current_page]).abs <= @options[:window]
end
# The last rendered tag was "truncated" or not
def was_truncated?
@last.is_a? TruncatedSpan
end
end
end
# A page
module Page
include Renderable
@ -164,10 +257,5 @@ module Kaminari
class TruncatedSpan < Tag
include NonLink
end
# The container tag
class Paginator < Tag
include Renderable
end
end
end

View file

@ -12,111 +12,115 @@ describe 'Kaminari::Helpers::PaginationRenderer' do
end
describe '#params' do
subject { PaginationRenderer.new(template, :params => {:controller => 'foo', :action => 'bar'}) }
before do
@renderer = PaginationRenderer.new(template, :params => {:controller => 'foo', :action => 'bar'})
end
subject { @renderer.instance_variable_get '@template' }
its(:params) { should == {:controller => 'foo', :action => 'bar'} }
end
describe '#tagify_links' do
def tags_with(options)
PaginationRenderer.new(template, options).tagify_links
end
#TODO test somehow...
# describe '#tagify_links' do
# def tags_with(options)
# PaginationRenderer.new(template, options).tagify_links
# end
context '1 page in total' do
subject { tags_with :num_pages => 1, :current_page => 1 }
it { should have(0).tags }
end
# context '1 page in total' do
# subject { tags_with :num_pages => 1, :current_page => 1 }
# it { should have(0).tags }
# end
context '10 pages in total' do
context 'first page' do
subject { tags_with :num_pages => 10, :current_page => 1 }
it { should_not contain_tag PrevLink }
it { should contain_tag PrevSpan }
it { should contain_tag CurrentPage }
it { should_not contain_tag FirstPageLink }
it { should contain_tag LastPageLink }
it { should contain_tag PageLink }
it { should contain_tag NextLink }
it { should_not contain_tag NextSpan }
it { should contain_tag TruncatedSpan }
end
# context '10 pages in total' do
# context 'first page' do
# subject { tags_with :num_pages => 10, :current_page => 1 }
# it { should_not contain_tag PrevLink }
# it { should contain_tag PrevSpan }
# it { should contain_tag CurrentPage }
# it { should_not contain_tag FirstPageLink }
# it { should contain_tag LastPageLink }
# it { should contain_tag PageLink }
# it { should contain_tag NextLink }
# it { should_not contain_tag NextSpan }
# it { should contain_tag TruncatedSpan }
# end
context 'second page' do
subject { tags_with :num_pages => 10, :current_page => 2 }
it { should contain_tag PrevLink }
it { should_not contain_tag PrevSpan }
it { should contain_tag CurrentPage }
it { should contain_tag FirstPageLink }
it { should contain_tag LastPageLink }
it { should contain_tag PageLink }
it { should contain_tag NextLink }
it { should_not contain_tag NextSpan }
it { should contain_tag TruncatedSpan }
end
# context 'second page' do
# subject { tags_with :num_pages => 10, :current_page => 2 }
# it { should contain_tag PrevLink }
# it { should_not contain_tag PrevSpan }
# it { should contain_tag CurrentPage }
# it { should contain_tag FirstPageLink }
# it { should contain_tag LastPageLink }
# it { should contain_tag PageLink }
# it { should contain_tag NextLink }
# it { should_not contain_tag NextSpan }
# it { should contain_tag TruncatedSpan }
# end
context 'third page' do
subject { tags_with :num_pages => 10, :current_page => 3 }
it { should contain_tag PrevLink }
it { should_not contain_tag PrevSpan }
it { should contain_tag CurrentPage }
it { should contain_tag FirstPageLink }
it { should contain_tag LastPageLink }
it { should contain_tag PageLink }
it { should contain_tag NextLink }
it { should_not contain_tag NextSpan }
it { should contain_tag TruncatedSpan }
end
# context 'third page' do
# subject { tags_with :num_pages => 10, :current_page => 3 }
# it { should contain_tag PrevLink }
# it { should_not contain_tag PrevSpan }
# it { should contain_tag CurrentPage }
# it { should contain_tag FirstPageLink }
# it { should contain_tag LastPageLink }
# it { should contain_tag PageLink }
# it { should contain_tag NextLink }
# it { should_not contain_tag NextSpan }
# it { should contain_tag TruncatedSpan }
# end
context 'fourth page(no truncation)' do
subject { tags_with :num_pages => 10, :current_page => 4 }
it { should contain_tag PrevLink }
it { should_not contain_tag PrevSpan }
it { should contain_tag CurrentPage }
it { should contain_tag FirstPageLink }
it { should contain_tag LastPageLink }
it { should contain_tag PageLink }
it { should contain_tag NextLink }
it { should_not contain_tag NextSpan }
it { should_not contain_tag TruncatedSpan }
end
# context 'fourth page(no truncation)' do
# subject { tags_with :num_pages => 10, :current_page => 4 }
# it { should contain_tag PrevLink }
# it { should_not contain_tag PrevSpan }
# it { should contain_tag CurrentPage }
# it { should contain_tag FirstPageLink }
# it { should contain_tag LastPageLink }
# it { should contain_tag PageLink }
# it { should contain_tag NextLink }
# it { should_not contain_tag NextSpan }
# it { should_not contain_tag TruncatedSpan }
# end
context 'seventh page(no truncation)' do
subject { tags_with :num_pages => 10, :current_page => 7 }
it { should contain_tag PrevLink }
it { should_not contain_tag PrevSpan }
it { should contain_tag CurrentPage }
it { should contain_tag FirstPageLink }
it { should contain_tag LastPageLink }
it { should contain_tag PageLink }
it { should contain_tag NextLink }
it { should_not contain_tag NextSpan }
it { should_not contain_tag TruncatedSpan }
end
# context 'seventh page(no truncation)' do
# subject { tags_with :num_pages => 10, :current_page => 7 }
# it { should contain_tag PrevLink }
# it { should_not contain_tag PrevSpan }
# it { should contain_tag CurrentPage }
# it { should contain_tag FirstPageLink }
# it { should contain_tag LastPageLink }
# it { should contain_tag PageLink }
# it { should contain_tag NextLink }
# it { should_not contain_tag NextSpan }
# it { should_not contain_tag TruncatedSpan }
# end
context 'eighth page' do
subject { tags_with :num_pages => 10, :current_page => 8 }
it { should contain_tag PrevLink }
it { should_not contain_tag PrevSpan }
it { should contain_tag CurrentPage }
it { should contain_tag FirstPageLink }
it { should contain_tag LastPageLink }
it { should contain_tag PageLink }
it { should contain_tag NextLink }
it { should_not contain_tag NextSpan }
it { should contain_tag TruncatedSpan }
end
# context 'eighth page' do
# subject { tags_with :num_pages => 10, :current_page => 8 }
# it { should contain_tag PrevLink }
# it { should_not contain_tag PrevSpan }
# it { should contain_tag CurrentPage }
# it { should contain_tag FirstPageLink }
# it { should contain_tag LastPageLink }
# it { should contain_tag PageLink }
# it { should contain_tag NextLink }
# it { should_not contain_tag NextSpan }
# it { should contain_tag TruncatedSpan }
# end
context 'last page' do
subject { tags_with :num_pages => 10, :current_page => 10 }
it { should contain_tag PrevLink }
it { should_not contain_tag PrevSpan }
it { should contain_tag CurrentPage }
it { should contain_tag FirstPageLink }
it { should_not contain_tag LastPageLink }
it { should contain_tag PageLink }
it { should_not contain_tag NextLink }
it { should contain_tag NextSpan }
it { should contain_tag TruncatedSpan }
end
end
end
# context 'last page' do
# subject { tags_with :num_pages => 10, :current_page => 10 }
# it { should contain_tag PrevLink }
# it { should_not contain_tag PrevSpan }
# it { should contain_tag CurrentPage }
# it { should contain_tag FirstPageLink }
# it { should_not contain_tag LastPageLink }
# it { should contain_tag PageLink }
# it { should_not contain_tag NextLink }
# it { should contain_tag NextSpan }
# it { should contain_tag TruncatedSpan }
# end
# end
# end
end

View file

@ -44,4 +44,115 @@ describe 'Kaminari::Helpers' do
its(:ancestor_renderables) { should == [NextSpan, Next, NonLink] }
end
end
describe 'Paginator' do
describe 'Paginator::PageProxy' do
describe '#current?' do
context 'current_page == page' do
subject { Paginator::PageProxy.new({:current_page => 26}, 26, nil) }
its(:current?) { should be_true }
end
context 'current_page != page' do
subject { Paginator::PageProxy.new({:current_page => 13}, 26, nil) }
its(:current?) { should_not be_true }
end
end
describe '#first?' do
context 'page == 1' do
subject { Paginator::PageProxy.new({:current_page => 26}, 1, nil) }
its(:first?) { should be_true }
end
context 'page != 1' do
subject { Paginator::PageProxy.new({:current_page => 13}, 2, nil) }
its(:first?) { should_not be_true }
end
end
describe '#last?' do
context 'current_page == page' do
subject { Paginator::PageProxy.new({:num_pages => 39}, 39, nil) }
its(:last?) { should be_true }
end
context 'current_page != page' do
subject { Paginator::PageProxy.new({:num_pages => 39}, 38, nil) }
its(:last?) { should_not be_true }
end
end
describe '#left_outer?' do
context 'current_page == left' do
subject { Paginator::PageProxy.new({:left => 3}, 3, nil) }
its(:left_outer?) { should be_true }
end
context 'current_page == left + 1' do
subject { Paginator::PageProxy.new({:left => 3}, 4, nil) }
its(:left_outer?) { should be_true }
end
context 'current_page == left + 2' do
subject { Paginator::PageProxy.new({:left => 3}, 5, nil) }
its(:left_outer?) { should_not be_true }
end
end
describe '#right_outer?' do
context 'num_pages - page > right' do
subject { Paginator::PageProxy.new({:num_pages => 10, :right => 3}, 6, nil) }
its(:right_outer?) { should_not be_true }
end
context 'num_pages - page == right' do
subject { Paginator::PageProxy.new({:num_pages => 10, :right => 3}, 7, nil) }
its(:right_outer?) { should be_true }
end
context 'num_pages - page < right' do
subject { Paginator::PageProxy.new({:num_pages => 10, :right => 3}, 8, nil) }
its(:right_outer?) { should be_true }
end
end
describe '#inside_window?' do
context 'page > current_page' do
context 'page - current_page > window' do
subject { Paginator::PageProxy.new({:current_page => 4, :window => 5}, 10, nil) }
its(:inside_window?) { should_not be_true }
end
context 'page - current_page == window' do
subject { Paginator::PageProxy.new({:current_page => 4, :window => 6}, 10, nil) }
its(:inside_window?) { should be_true }
end
context 'page - current_page < window' do
subject { Paginator::PageProxy.new({:current_page => 4, :window => 7}, 10, nil) }
its(:inside_window?) { should be_true }
end
end
context 'current_page > page' do
context 'current_page - page > window' do
subject { Paginator::PageProxy.new({:current_page => 15, :window => 4}, 10, nil) }
its(:inside_window?) { should_not be_true }
end
context 'current_page - page == window' do
subject { Paginator::PageProxy.new({:current_page => 15, :window => 5}, 10, nil) }
its(:inside_window?) { should be_true }
end
context 'current_page - page < window' do
subject { Paginator::PageProxy.new({:current_page => 15, :window => 6}, 10, nil) }
its(:inside_window?) { should be_true }
end
end
end
describe '#was_truncated?' do
before do
stub(@template = Object.new).options { {} }
end
context 'last.is_a? TruncatedSpan' do
subject { Paginator::PageProxy.new({}, 10, TruncatedSpan.new(@template)) }
its(:was_truncated?) { should be_true }
end
context 'last.is not a TruncatedSpan' do
subject { Paginator::PageProxy.new({}, 10, PageLink.new(@template)) }
its(:was_truncated?) { should_not be_true }
end
end
end
end
end