Link to first and last page with pagination

Hi,

Coming from PHP I’m a total ruby n00b and and playing around with middleman to generate a blog. To get things working nicely with bootstrap I’ve written my own pagination snippet:

  <div class="pagination pagination-centered">
    <ul>
        <% if prev_page %>
            <li><%= link_to '&laquo;', page_start %></li>
            <li><%= link_to '&lsaquo;', prev_page.url %></li>
        <% else %>
            <li class="disabled"><span>&laquo;</span></li>
            <li class="disabled"><span>&lsaquo;</span></li>
        <% end %>

        <% (page_number - 2 .. page_number + 2).select{|i| i > 0 && i <= num_pages}.each do |i| %>
            
            <% p = nil %>
            <% (i ... page_number).each do p = p ? p.metadata[:locals]['prev_page'] : prev_page; end %>
            <% (page_number ... i).each do p = p ? p.metadata[:locals]['next_page'] : next_page; end %>
            <% if i == page_number %>
                <li class="active"><span><%= i %></span></li>
            <% else %>
                <li><%= link_to "#{i}", p && p.url %></li>
            <% end %>
        <% end %>
        
        <% if next_page %>
            <li><%= link_to '&rsaquo;', next_page.url %></li>
            <li><%= link_to '&raquo;', path() %></li>
        <% else %>
            <li class="disabled"><span>&rsaquo;</span></li>
            <li class="disabled"><span>&raquo;</span></li>
        <% end %>
    </ul> 
</div>

The problem here of course, I have no idea how to link to the first and last page used in pagination (page_start and path() respectively). I know this is probably a stupid question. Could anyone point me in the right direction or link to a tutorial to get up to date with rail basics?

Solved it. Isn’t particularly efficient but it works:

 <div class="pagination pagination-centered">
<ul>
  <% if prev_page %>
    <% p = nil %>
    <% (1 ... page_number).each do p = p ? p.metadata[:locals]['prev_page'] : prev_page; end %>
    <li><%= link_to '&laquo;', p && p.url %></li>
    <li><%= link_to '&lsaquo;', prev_page.url %></li>
  <% else %>
    <li class="disabled"><span>&laquo;</span></li>
    <li class="disabled"><span>&lsaquo;</span></li>
  <% end %>

  <% (page_number - 2 .. page_number + 2).select{|i| i > 0 && i <= num_pages}.each do |i| %>
    <% p = nil %>
    <% (i ... page_number).each do p = p ? p.metadata[:locals]['prev_page'] : prev_page; end %>
    <% (page_number ... i).each do p = p ? p.metadata[:locals]['next_page'] : next_page; end %>
    <% if i == page_number %>
      <li class="active"><span><%= i %></span></li>
    <% else %>
      <li><%= link_to "#{i}", p && p.url %></li>
    <% end %>
  <% end %>
        
  <% if next_page %>
    <% p = nil %>
    <% (page_number ... num_pages).each do p = p ? p.metadata[:locals]['next_page'] : next_page; end %>
    <li><%= link_to '&rsaquo;', next_page.url %></li>
    <li><%= link_to '&raquo;', p && p.url %></li>
  <% else %>
    <li class="disabled"><span>&rsaquo;</span></li>
    <li class="disabled"><span>&raquo;</span></li>
  <% end %>
</ul> 

I think this can be rewritten to be a lot cleaner. I’d be happy to help, but I’m struggling to understand what’s going on here. Are you using Middleman Blog?

I’ve rewritten this with a few helper methods. I think it’s easier to read, and to customise, although there are still some improvements you can make.

In your page:

<div class="pagination pagination-centered">
  <%= pagination_links %>
</div>

Inside the helpers block of your config.rb:

helpers do

  def locals_for(page, key)
    page && page.metadata[:locals][key]
  end

  def pagination_links
    prev_link = pagination_item('Prev', locals_for(current_page, 'prev_page').try(:url))
    next_link = pagination_item('Next', locals_for(current_page, 'next_page').try(:url))

    items = []

    # Add the current page
    page = current_page
    items << pagination_item_for(page)

    # Add the prior pages
    while page = locals_for(page, 'prev_page')
      items.unshift pagination_item_for(page)
    end

    # Add all subsequent pages
    page = current_page

    while page = locals_for(page, 'next_page')
      items << pagination_item_for(page)
    end

    # Combine the items with the prev/next links
    items = [prev_link, items, next_link].flatten

    content_tag(:ul, items.join)
  end

  def pagination_item_for(page)
    link_title = page.metadata[:locals]['page_number']
    pagination_item(link_title, page.url)
  end

  def pagination_item(link_title, link_path, options = {})
    if link_path == current_page.url
      content = content_tag(:span, link_title)
      options[:class] = "active"
    elsif link_path
      content = link_to(link_title, link_path)
    else
      content = content_tag(:span, link_title)
      options[:class] = "disabled"
    end

    content_tag(:li, content, options)
  end

end

Thanks for the tip. Modified your code a little so it does what I want. Show previous and next two pages and show link for first and last page:

helpers do

def locals_for(page, key)
page && page.metadata[:locals][key]
end

def pagination_links

if locals_for(current_page, 'num_pages') == 1
  return nil
end

prev_link = pagination_item('&lsaquo;', locals_for(current_page, 'prev_page').try(:url))
next_link = pagination_item('&rsaquo;', locals_for(current_page, 'next_page').try(:url))

items = []

# Add the current page
page = current_page

# Add the prior pages
i = 0
while page = locals_for(page, 'prev_page')
  if (i < 2)
    items.unshift pagination_item_for(page)
    i += 1
  end
  first_page = page
end
first_link = pagination_item('&laquo;', first_page.try(:url))

# Add all subsequent pages
page = current_page
items.push pagination_item_for(current_page)

i = 0
while page = locals_for(page, 'next_page')
  if (i < 2)
    items.push pagination_item_for(page)
    i += 1
  end
  last_page = page
end
last_link = pagination_item('&raquo;', last_page.try(:url))

# Combine the items with the prev/next links
items = [first_link, prev_link, items, next_link, last_link].flatten

content_tag(:ul, items.join)

end

def pagination_item_for(page)
link_title = page.metadata[:locals][‘page_number’]
pagination_item(link_title, page.url)
end

def pagination_item(link_title, link_path, options = {})
if link_path == current_page.url
content = content_tag(:span, link_title)
options[:class] = “active”
elsif link_path
content = link_to(link_title, link_path)
else
content = content_tag(:span, link_title)
options[:class] = “disabled”
end

content_tag(:li, content, options)

end
end