mjfreshyfresh-leaf 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,18 @@
1
+ Copyright (c) 2010 Peter Hellberg
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,53 @@
1
+ = Leaf
2
+
3
+ A _really_ simple pagination library, heavily based on the agnostic branch of
4
+ {will_paginate}[http://github.com/mislav/will_paginate/tree/agnostic].
5
+
6
+ = Description
7
+
8
+ Leaf supports pagination for collections responding to +total_pages+,
9
+ +per_page+, +previous_page+ and +total_entries+ in Sinatra views out of the box.
10
+
11
+ It currently supports two renderers: +Leaf::ViewHelpers::LinkRenderer+
12
+ and +Leaf::ViewHelpers::ListRenderer+
13
+
14
+ == Installation
15
+
16
+ gem install leaf
17
+
18
+ == Example usage
19
+
20
+ require 'rubygems'
21
+ require 'sinatra'
22
+ require 'leaf'
23
+
24
+ include Leaf::ViewHelpers::Base
25
+
26
+ # Needed to paginate any array
27
+ # you’ll probably use something else.
28
+ require 'leaf/array'
29
+
30
+ get '/' do
31
+ page = (params[:page]) ? params[:page] : 1
32
+ array = ('a'..'z').to_a
33
+
34
+ haml :index, :locals => {
35
+ :collection => array.paginate({
36
+ :page => page,
37
+ :per_page => 5
38
+ })
39
+ }
40
+ end
41
+
42
+ __END__
43
+ @@ index
44
+ = leaf(collection, :renderer => Leaf::ViewHelpers::ListRenderer)
45
+ %ul
46
+ - collection.each do |letter|
47
+ %li= letter
48
+
49
+ == Authors and credits
50
+
51
+ Leaf is based on {will_paginate}[http://github.com/mislav/will_paginate/] which
52
+ was originally written by PJ Hyett, who later handed over development to Mislav
53
+ Marohnić. (The library was completely rewritten since then.)
@@ -0,0 +1,26 @@
1
+ require 'rubygems'
2
+ gem 'rdoc' # Required on Mac OS X to get a “working” rdoc version
3
+ require 'rake/rdoctask'
4
+
5
+ load 'spec/tasks.rake'
6
+
7
+ desc 'Default: run specs.'
8
+ task :default => :spec
9
+
10
+ desc 'Generate RDoc documentation for the leaf gem.'
11
+ Rake::RDocTask.new(:rdoc) do |rdoc|
12
+ rdoc.rdoc_files.include('README.rdoc', 'MIT-LICENSE').
13
+ include('lib/**/*.rb').
14
+ exclude('lib/leaf/finders/sequel.rb').
15
+ exclude('lib/leaf/view_helpers/sinatra.rb').
16
+ exclude('lib/leaf/core_ext.rb').
17
+ exclude('lib/leaf/version.rb')
18
+
19
+ rdoc.main = "README.rdoc" # page to start on
20
+ rdoc.title = "leaf documentation"
21
+
22
+ rdoc.rdoc_dir = 'doc' # rdoc output folder
23
+ rdoc.options << '--inline-source' << '--charset=UTF-8' << '--format=darkfish'
24
+ rdoc.options << '--main=README.rdoc'
25
+ rdoc.options << '--webcvs=http://github.com/c7/leaf/tree/master/'
26
+ end
@@ -0,0 +1,15 @@
1
+ # = Leafing through the pages!
2
+ #
3
+ # First read about Leaf::Finders::Base, then see
4
+ # Leaf::ViewHelpers. The magical array you're handling in-between is
5
+ # Leaf::Collection.
6
+ #
7
+ # Happy paginating!
8
+ module Leaf
9
+ require 'leaf/version'
10
+
11
+ # Load the helpers for Sinatra
12
+ if defined?(Sinatra)
13
+ require 'leaf/view_helpers/sinatra'
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ require 'leaf/collection'
2
+
3
+ class Array
4
+ # Paginates a static array (extracting a subset of it). The result is a
5
+ # Leaf::Collection instance, which is an array with a few more
6
+ # properties about its paginated state.
7
+ #
8
+ # Parameters:
9
+ # * <tt>:page</tt> - current page, defaults to 1
10
+ # * <tt>:per_page</tt> - limit of items per page, defaults to 30
11
+ # * <tt>:total_entries</tt> - total number of items in the array, defaults to
12
+ # <tt>array.length</tt> (obviously)
13
+ #
14
+ # Example:
15
+ # arr = ['a', 'b', 'c', 'd', 'e']
16
+ # paged = arr.paginate(:per_page => 2) #-> ['a', 'b']
17
+ # paged.total_entries #-> 5
18
+ # arr.paginate(:page => 2, :per_page => 2) #-> ['c', 'd']
19
+ # arr.paginate(:page => 3, :per_page => 2) #-> ['e']
20
+ #
21
+ # This method was originally {suggested by Desi
22
+ # McAdam}[http://www.desimcadam.com/archives/8]
23
+ def paginate(options = {})
24
+ raise ArgumentError, "parameter hash expected (got #{options.inspect})" unless Hash === options
25
+ Leaf::Collection.create options[:page] || 1,
26
+ options[:per_page] || 30,
27
+ options[:total_entries] || self.length do |pager|
28
+ pager.replace self[pager.offset, pager.per_page].to_a
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,100 @@
1
+ module Leaf
2
+ # = Invalid page number error
3
+ # This is an ArgumentError raised in case a page was requested that is either
4
+ # zero or negative number. You should decide how do deal with such errors in
5
+ # the controller.
6
+ class InvalidPage < ArgumentError
7
+ def initialize(page, page_num)
8
+ super "#{page.inspect} given as value, which translates to '#{page_num}' as page number"
9
+ end
10
+ end
11
+
12
+ # = The key to pagination
13
+ # Arrays returned from paginating finds are, in fact, instances of this little
14
+ # class. You may think of Leaf::Collection as an ordinary array with
15
+ # some extra properties. Those properties are used by view helpers to generate
16
+ # correct page links.
17
+ #
18
+ # gem 'leaf'
19
+ # require 'leaf/collection'
20
+ #
21
+ # # now use Leaf::Collection directly or subclass it
22
+ class Collection < Array
23
+ attr_reader :current_page, :per_page, :total_entries, :total_pages
24
+
25
+ # Arguments to the constructor are the current page number, per-page limit
26
+ # and the total number of entries. The last argument is optional because it
27
+ # is best to do lazy counting; in other words, count *conditionally* after
28
+ # populating the collection using the +replace+ method.
29
+ def initialize(page, per_page, total = nil)
30
+ @current_page = page.to_i
31
+ raise InvalidPage.new(page, @current_page) if @current_page < 1
32
+ @per_page = per_page.to_i
33
+ raise ArgumentError, "`per_page` setting cannot be less than 1 (#{@per_page} given)" if @per_page < 1
34
+
35
+ self.total_entries = total if total
36
+ end
37
+
38
+ # Just like +new+, but yields the object after instantiation and returns it
39
+ # afterwards. This is very useful for manual pagination:
40
+ def self.create(page, per_page, total = nil, &block)
41
+ pager = new(page, per_page, total)
42
+ yield pager
43
+ pager
44
+ end
45
+
46
+ # Helper method that is true when someone tries to fetch a page with a
47
+ # larger number than the last page. Can be used in combination with flashes
48
+ # and redirecting.
49
+ def out_of_bounds?
50
+ current_page > total_pages
51
+ end
52
+
53
+ # Current offset of the paginated collection. If we're on the first page,
54
+ # it is always 0. If we're on the 2nd page and there are 30 entries per page,
55
+ # the offset is 30. This property is useful if you want to render ordinals
56
+ # besides your records: simply start with offset + 1.
57
+ def offset
58
+ (current_page - 1) * per_page
59
+ end
60
+
61
+ # current_page - 1 or nil if there is no previous page
62
+ def previous_page
63
+ current_page > 1 ? (current_page - 1) : nil
64
+ end
65
+
66
+ # current_page + 1 or nil if there is no next page
67
+ def next_page
68
+ current_page < total_pages ? (current_page + 1) : nil
69
+ end
70
+
71
+ def total_entries=(number)
72
+ @total_entries = number.to_i
73
+ @total_pages = (@total_entries / per_page.to_f).ceil
74
+ end
75
+
76
+ # This is a magic wrapper for the original Array#replace method. It serves
77
+ # for populating the paginated collection after initialization.
78
+ #
79
+ # Why magic? Because it tries to guess the total number of entries judging
80
+ # by the size of given array. If it is shorter than +per_page+ limit, then we
81
+ # know we're on the last page. This trick is very useful for avoiding
82
+ # unnecessary hits to the database to do the counting after we fetched the
83
+ # data for the current page.
84
+ #
85
+ # However, after using +replace+ you should always test the value of
86
+ # +total_entries+ and set it to a proper value if it's +nil+. See the example
87
+ # in +create+.
88
+ def replace(array)
89
+ result = super
90
+
91
+ # The collection is shorter then page limit? Rejoice, because
92
+ # then we know that we are on the last page!
93
+ if total_entries.nil? and length < per_page and (current_page == 1 or length > 0)
94
+ self.total_entries = offset + length
95
+ end
96
+
97
+ result
98
+ end
99
+ end
100
+ end
@@ -0,0 +1,69 @@
1
+ require 'set'
2
+ require 'leaf/array'
3
+
4
+ # helper to check for method existance in ruby 1.8- and 1.9-compatible way
5
+ # because `methods`, `instance_methods` and others return strings in 1.8 and symbols in 1.9
6
+ #
7
+ # ['foo', 'bar'].include_method?(:foo) # => true
8
+ class Array
9
+ def include_method?(name)
10
+ name = name.to_sym
11
+ !!(find { |item| item.to_sym == name })
12
+ end
13
+ end
14
+
15
+ ## everything below copied from ActiveSupport so we don't depend on it ##
16
+
17
+ unless Hash.instance_methods.include_method? :except
18
+ Hash.class_eval do
19
+ # Returns a new hash without the given keys.
20
+ def except(*keys)
21
+ rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
22
+ reject { |key,| rejected.include?(key) }
23
+ end
24
+
25
+ # Replaces the hash without only the given keys.
26
+ def except!(*keys)
27
+ replace(except(*keys))
28
+ end
29
+ end
30
+ end
31
+
32
+ unless Hash.instance_methods.include_method? :slice
33
+ Hash.class_eval do
34
+ # Returns a new hash with only the given keys.
35
+ def slice(*keys)
36
+ allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
37
+ reject { |key,| !allowed.include?(key) }
38
+ end
39
+
40
+ # Replaces the hash with only the given keys.
41
+ def slice!(*keys)
42
+ replace(slice(*keys))
43
+ end
44
+ end
45
+ end
46
+
47
+ unless String.instance_methods.include_method? :constantize
48
+ String.class_eval do
49
+ def constantize
50
+ unless /\A(?:::)?([A-Z]\w*(?:::[A-Z]\w*)*)\z/ =~ self
51
+ raise NameError, "#{self.inspect} is not a valid constant name!"
52
+ end
53
+
54
+ Object.module_eval("::#{$1}", __FILE__, __LINE__)
55
+ end
56
+ end
57
+ end
58
+
59
+ unless String.instance_methods.include_method? :underscore
60
+ String.class_eval do
61
+ def underscore
62
+ self.to_s.gsub(/::/, '/').
63
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
64
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
65
+ tr("-", "_").
66
+ downcase
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,9 @@
1
+ require 'leaf/core_ext'
2
+
3
+ module Leaf
4
+ # Database logic for different ORMs
5
+ #
6
+ # See Leaf::Finders::Base
7
+ module Finders
8
+ end
9
+ end
@@ -0,0 +1,82 @@
1
+ require 'leaf/core_ext'
2
+
3
+ module Leaf
4
+ module Finders
5
+ # = Database-agnostic finder module
6
+ #
7
+ # Out of the box, leaf supports hooking in the Sequel ORM
8
+ module Base
9
+ def per_page
10
+ @per_page ||= 30
11
+ end
12
+
13
+ def per_page=(limit)
14
+ @per_page = limit.to_i
15
+ end
16
+
17
+ # This is the main paginating finder.
18
+ #
19
+ # == Special parameters for paginating finders
20
+ # * <tt>:page</tt> -- REQUIRED, but defaults to 1 if false or nil
21
+ # * <tt>:per_page</tt> -- defaults to <tt>CurrentModel.per_page</tt> (which is 30 if not overridden)
22
+ # * <tt>:total_entries</tt> -- use only if you manually count total entries
23
+ # * <tt>:count</tt> -- additional options that are passed on to +count+
24
+ # * <tt>:finder</tt> -- name of the finder method to use (default: "find")
25
+ #
26
+ # All other options (+conditions+, +order+, ...) are forwarded to +find+
27
+ # and +count+ calls.
28
+ def paginate(*args, &block)
29
+ options = args.pop
30
+ page, per_page, total_entries = leaf_parse_options(options)
31
+
32
+ Leaf::Collection.create(page, per_page, total_entries) do |pager|
33
+ query_options = options.except :page, :per_page, :total_entries
34
+ wp_query(query_options, pager, args, &block)
35
+ end
36
+ end
37
+
38
+ # Iterates through all records by loading one page at a time. This is useful
39
+ # for migrations or any other use case where you don't want to load all the
40
+ # records in memory at once.
41
+ #
42
+ # It uses +paginate+ internally; therefore it accepts all of its options.
43
+ # You can specify a starting page with <tt>:page</tt> (default is 1). Default
44
+ # <tt>:order</tt> is <tt>"id"</tt>, override if necessary.
45
+ #
46
+ # {Jamis Buck describes this}[http://weblog.jamisbuck.org/2007/4/6/faking-cursors-in-activerecord]
47
+ # and also uses a more efficient way for MySQL.
48
+ def paginated_each(options = {}, &block)
49
+ options = { :order => 'id', :page => 1 }.merge options
50
+ options[:page] = options[:page].to_i
51
+ options[:total_entries] = 0 # skip the individual count queries
52
+ total = 0
53
+
54
+ begin
55
+ collection = paginate(options)
56
+ total += collection.each(&block).size
57
+ options[:page] += 1
58
+ end until collection.size < collection.per_page
59
+
60
+ total
61
+ end
62
+
63
+ protected
64
+
65
+ def leaf_parse_options(options)
66
+ raise ArgumentError, 'parameter hash expected' unless Hash === options
67
+ raise ArgumentError, ':page parameter required' unless options.key? :page
68
+
69
+ if options[:count] and options[:total_entries]
70
+ raise ArgumentError, ':count and :total_entries are mutually exclusive'
71
+ end
72
+
73
+ page = options[:page] || 1
74
+ per_page = options[:per_page] || self.per_page
75
+ total = options[:total_entries]
76
+
77
+ return [page, per_page, total]
78
+ end
79
+
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,23 @@
1
+ require 'sequel'
2
+ require 'sequel/extensions/pagination'
3
+ require 'leaf/core_ext'
4
+
5
+ existing_methods = Sequel::Dataset::Pagination.instance_methods
6
+
7
+ Sequel::Dataset::Pagination.module_eval do
8
+ # it should quack like a Leaf::Collection
9
+
10
+ alias :total_pages :page_count unless existing_methods.include_method? :total_pages
11
+ alias :per_page :page_size unless existing_methods.include_method? :per_page
12
+ alias :previous_page :prev_page unless existing_methods.include_method? :previous_page
13
+ alias :total_entries :pagination_record_count unless existing_methods.include_method? :total_entries
14
+
15
+ def out_of_bounds?
16
+ current_page > total_pages
17
+ end
18
+
19
+ # Current offset of the paginated collection
20
+ def offset
21
+ (current_page - 1) * per_page
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ module Leaf
2
+ module VERSION
3
+ MAJOR = 0
4
+ MINOR = 0
5
+ TINY = 1
6
+
7
+ STRING = [MAJOR, MINOR, TINY].join('.')
8
+ end
9
+ end
@@ -0,0 +1,34 @@
1
+ module Leaf
2
+ # = Leaf view helpers
3
+ #
4
+ # The main view helper is +leaf+. It renders the pagination links
5
+ # for the given collection.
6
+ #
7
+ # Read more in Leaf::ViewHelpers::Base
8
+ module ViewHelpers
9
+ # ==== Global options for helpers
10
+ #
11
+ # Options for pagination helpers are optional and get their default values
12
+ # from the Leaf::ViewHelpers.pagination_options hash. You can write
13
+ # to this hash to override default options on the global level:
14
+ #
15
+ # Leaf::ViewHelpers.pagination_options[:previous_label] = 'Previous page'
16
+ def self.pagination_options() @pagination_options; end
17
+ # Overrides the default +pagination_options+
18
+ def self.pagination_options=(value) @pagination_options = value; end
19
+
20
+ self.pagination_options = {
21
+ :class => 'pagination',
22
+ :previous_label => '&#8592; Previous',
23
+ :next_label => 'Next &#8594;',
24
+ :inner_window => 4, # links around the current page
25
+ :outer_window => 1, # links around beginning and end
26
+ :separator => ' ', # single space is friendly to spiders and non-graphic browsers
27
+ :param_name => :page,
28
+ :params => nil,
29
+ :renderer => 'Leaf::ViewHelpers::LinkRenderer',
30
+ :page_links => true,
31
+ :container => true
32
+ }
33
+ end
34
+ end
@@ -0,0 +1,60 @@
1
+ require 'leaf/core_ext'
2
+ require 'leaf/view_helpers'
3
+
4
+ module Leaf
5
+ module ViewHelpers
6
+ # = The main view helpers module
7
+ #
8
+ # This is the base module which provides the +leaf+ view helper.
9
+ module Base
10
+ # Renders Digg/Flickr-style pagination for a Leaf::Collection object. Nil is
11
+ # returned if there is only one page in total; pagination links aren't needed in that case.
12
+ #
13
+ # ==== Options
14
+ # * <tt>:class</tt> -- CSS class name for the generated DIV (default: "pagination")
15
+ # * <tt>:previous_label</tt> -- default: "« Previous"
16
+ # * <tt>:next_label</tt> -- default: "Next »"
17
+ # * <tt>:inner_window</tt> -- how many links are shown around the current page (default: 4)
18
+ # * <tt>:outer_window</tt> -- how many links are around the first and the last page (default: 1)
19
+ # * <tt>:separator</tt> -- string separator for page HTML elements (default: single space)
20
+ # * <tt>:param_name</tt> -- parameter name for page number in URLs (default: <tt>:page</tt>)
21
+ # * <tt>:params</tt> -- additional parameters when generating pagination links
22
+ # (eg. <tt>:controller => "foo", :action => nil</tt>)
23
+ # * <tt>:renderer</tt> -- class name, class or instance of a link renderer (default:
24
+ # <tt>Leaf::LinkRenderer</tt>)
25
+ # * <tt>:page_links</tt> -- when false, only previous/next links are rendered (default: true)
26
+ # * <tt>:container</tt> -- toggles rendering of the DIV container for pagination links, set to
27
+ # false only when you are rendering your own pagination markup (default: true)
28
+ # * <tt>:id</tt> -- HTML ID for the container (default: nil). Pass +true+ to have the ID
29
+ # automatically generated from the class name of objects in collection: for example, paginating
30
+ # ArticleComment models would yield an ID of "article_comments_pagination".
31
+ #
32
+ # All options beside listed ones are passed as HTML attributes to the container
33
+ # element for pagination links (the DIV). For example:
34
+ #
35
+ # <%= leaf @posts, :id => 'leaf_posts' %>
36
+ #
37
+ # ... will result in:
38
+ #
39
+ # <div class="pagination" id="leaf_posts"> ... </div>
40
+ #
41
+ def leaf(collection, options = {})
42
+ # early exit if there is nothing to render
43
+ return nil unless collection.total_pages > 1
44
+
45
+ options = Leaf::ViewHelpers.pagination_options.merge(options)
46
+
47
+ # get the renderer instance
48
+ renderer = case options[:renderer]
49
+ when String then options[:renderer].constantize.new
50
+ when Class then options[:renderer].new
51
+ else options[:renderer]
52
+ end
53
+
54
+ # render HTML for pagination
55
+ renderer.prepare collection, options, self
56
+ renderer.to_html
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,129 @@
1
+ require 'leaf/core_ext'
2
+ require 'leaf/view_helpers/link_renderer_base'
3
+
4
+ module Leaf
5
+ module ViewHelpers
6
+ # This class does the heavy lifting of actually building the pagination
7
+ # links. It is used by +leaf+ helper internally.
8
+ class LinkRenderer < LinkRendererBase
9
+
10
+ # * +collection+ is a Leaf::Collection instance or any other object
11
+ # that conforms to that API
12
+ # * +options+ are forwarded from +leaf+ view helper
13
+ # * +template+ is the reference to the template being rendered
14
+ def prepare(collection, options, template)
15
+ super(collection, options)
16
+ @template = template
17
+ @container_attributes = @base_url_params = nil
18
+ end
19
+
20
+ # Process it! This method returns the complete HTML string which contains
21
+ # pagination links. Feel free to subclass LinkRenderer and change this
22
+ # method as you see fit.
23
+ def to_html
24
+ html = pagination.map do |item|
25
+ item.is_a?(Fixnum) ?
26
+ page_number(item) :
27
+ send(item)
28
+ end.join(@options[:separator])
29
+
30
+ @options[:container] ? html_container(html) : html
31
+ end
32
+
33
+ # Returns the subset of +options+ this instance was initialized with that
34
+ # represent HTML attributes for the container element of pagination links.
35
+ def container_attributes
36
+ @container_attributes ||= begin
37
+ attributes = @options.except *(Leaf::ViewHelpers.pagination_options.keys - [:class])
38
+ # pagination of Post models will have the ID of "posts_pagination"
39
+ if @options[:container] and @options[:id] === true
40
+ attributes[:id] = @collection.first.class.name.underscore.pluralize + '_pagination'
41
+ end
42
+ attributes
43
+ end
44
+ end
45
+
46
+ protected
47
+
48
+ def page_number(page)
49
+ unless page == current_page
50
+ link(page, page, :rel => rel_value(page))
51
+ else
52
+ tag(:em, page)
53
+ end
54
+ end
55
+
56
+ def gap
57
+ '<span class="gap">&hellip;</span>'
58
+ end
59
+
60
+ def previous_page
61
+ previous_or_next_page(@collection.previous_page, @options[:previous_label], 'previous_page')
62
+ end
63
+
64
+ def next_page
65
+ previous_or_next_page(@collection.next_page, @options[:next_label], 'next_page')
66
+ end
67
+
68
+ def previous_or_next_page(page, text, classname)
69
+ if page
70
+ link(text, page, :class => classname)
71
+ else
72
+ tag(:span, text, :class => classname + ' disabled')
73
+ end
74
+ end
75
+
76
+ def html_container(html)
77
+ tag(:div, html, container_attributes)
78
+ end
79
+
80
+ # Returns URL params for +page_link_or_span+, taking the current GET params
81
+ # and <tt>:params</tt> option into account.
82
+ def url(page)
83
+ raise NotImplementedError
84
+ end
85
+
86
+ private
87
+
88
+ def link(text, target, attributes = {})
89
+ if target.is_a? Fixnum
90
+ attributes[:rel] = rel_value(target)
91
+ target = url(target)
92
+ end
93
+ attributes[:href] = target
94
+ tag(:a, text, attributes)
95
+ end
96
+
97
+ def tag(name, value, attributes = {})
98
+ string_attributes = attributes.inject('') do |attrs, pair|
99
+ unless pair.last.nil?
100
+ attrs << %( #{pair.first}="#{Rack::Utils.escape_html(pair.last.to_s)}")
101
+ end
102
+ attrs
103
+ end
104
+ "<#{name}#{string_attributes}>#{value}</#{name}>"
105
+ end
106
+
107
+ def rel_value(page)
108
+ case page
109
+ when @collection.previous_page; 'prev' + (page == 1 ? ' start' : '')
110
+ when @collection.next_page; 'next'
111
+ when 1; 'start'
112
+ end
113
+ end
114
+
115
+ def symbolized_update(target, other)
116
+ other.each do |key, value|
117
+ key = key.to_sym
118
+ existing = target[key]
119
+
120
+ if value.is_a?(Hash) and (existing.is_a?(Hash) or existing.nil?)
121
+ symbolized_update(existing || (target[key] = {}), value)
122
+ else
123
+ target[key] = value
124
+ end
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
@@ -0,0 +1,83 @@
1
+ require 'leaf/view_helpers'
2
+
3
+ module Leaf
4
+ module ViewHelpers
5
+ # This class does the heavy lifting of actually building the pagination
6
+ # links. It is used by +leaf+ helper internally.
7
+ class LinkRendererBase
8
+
9
+ # * +collection+ is a Leaf::Collection instance or any other object
10
+ # that conforms to that API
11
+ # * +options+ are forwarded from +leaf+ view helper
12
+ def prepare(collection, options)
13
+ @collection = collection
14
+ @options = options
15
+
16
+ # reset values in case we're re-using this instance
17
+ @total_pages = @param_name = nil
18
+ end
19
+
20
+ def pagination
21
+ items = @options[:page_links] ? windowed_page_numbers : []
22
+ items.unshift :previous_page
23
+ items.push :next_page
24
+ end
25
+
26
+ protected
27
+
28
+ # Calculates visible page numbers using the <tt>:inner_window</tt> and
29
+ # <tt>:outer_window</tt> options.
30
+ def windowed_page_numbers
31
+ inner_window, outer_window = @options[:inner_window].to_i, @options[:outer_window].to_i
32
+ window_from = current_page - inner_window
33
+ window_to = current_page + inner_window
34
+
35
+ # adjust lower or upper limit if other is out of bounds
36
+ if window_to > total_pages
37
+ window_from -= window_to - total_pages
38
+ window_to = total_pages
39
+ end
40
+ if window_from < 1
41
+ window_to += 1 - window_from
42
+ window_from = 1
43
+ window_to = total_pages if window_to > total_pages
44
+ end
45
+
46
+ # these are always visible
47
+ middle = window_from..window_to
48
+
49
+ # left window
50
+ if outer_window + 3 < middle.first # there's a gap
51
+ left = (1..(outer_window + 1)).to_a
52
+ left << :gap
53
+ else # runs into visible pages
54
+ left = 1...middle.first
55
+ end
56
+
57
+ # right window
58
+ if total_pages - outer_window - 2 > middle.last # again, gap
59
+ right = ((total_pages - outer_window)..total_pages).to_a
60
+ right.unshift :gap
61
+ else # runs into visible pages
62
+ right = (middle.last + 1)..total_pages
63
+ end
64
+
65
+ left.to_a + middle.to_a + right.to_a
66
+ end
67
+
68
+ private
69
+
70
+ def current_page
71
+ @collection.current_page
72
+ end
73
+
74
+ def total_pages
75
+ @collection.total_pages
76
+ end
77
+
78
+ def param_name
79
+ @param_name ||= @options[:param_name].to_s
80
+ end
81
+ end
82
+ end
83
+ end
@@ -0,0 +1,39 @@
1
+ require 'leaf/view_helpers/link_renderer'
2
+
3
+ module Leaf
4
+ module ViewHelpers
5
+ class ListRenderer < LinkRenderer
6
+ def to_html
7
+ html = pagination.map do |item|
8
+ "\n " + tag(:li, (item.is_a?(Fixnum) ?
9
+ page_number(item) :
10
+ send(item)))
11
+ end.join(@options[:separator])
12
+
13
+ @options[:container] ? html_container(html) : html
14
+ end
15
+
16
+ def previous_or_next_page(page, text, classname)
17
+ if page
18
+ link(tag(:span, text), page, :class => classname)
19
+ else
20
+ tag(:span, tag(:span, text), :class => classname + ' disabled')
21
+ end
22
+ end
23
+
24
+ def html_container(html)
25
+ tag(:div, "\n " +
26
+ tag(:ul, html + "\n ") + "\n", container_attributes) + "\n"
27
+ end
28
+
29
+ private
30
+ def page_number(page)
31
+ unless page == current_page
32
+ link(tag(:span, page), page, :rel => rel_value(page))
33
+ else
34
+ tag(:em, tag(:span, page))
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,20 @@
1
+ require 'leaf/view_helpers/base'
2
+ require 'leaf/view_helpers/link_renderer'
3
+ require 'leaf/view_helpers/list_renderer'
4
+
5
+ Leaf::ViewHelpers::LinkRenderer.class_eval do
6
+ protected
7
+ def url(page)
8
+ url = @template.request.url
9
+ if page == 1
10
+ # strip out page param and trailing ? and & if they exists
11
+ url.gsub(/(\?|\&)#{@options[:param_name]}=[0-9]+/, '').gsub(/\?$/, '').gsub(/\&$/, '')
12
+ else
13
+ if url =~ /(\?|\&)#{@options[:param_name]}=[0-9]+/
14
+ url.gsub(/#{@options[:param_name]}=[0-9]+/, "#{@options[:param_name]}=#{page}").gsub(/\&+/, '&')
15
+ else
16
+ (url =~ /\?/) ? url + "&#{@options[:param_name]}=#{page}" : url + "?#{@options[:param_name]}=#{page}"
17
+ end
18
+ end
19
+ end
20
+ end
metadata ADDED
@@ -0,0 +1,94 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mjfreshyfresh-leaf
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 0
8
+ - 1
9
+ version: 0.0.1
10
+ platform: ruby
11
+ authors:
12
+ - Peter Hellberg
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-09-13 00:00:00 -07:00
18
+ default_executable:
19
+ dependencies:
20
+ - !ruby/object:Gem::Dependency
21
+ name: rack
22
+ prerelease: false
23
+ requirement: &id001 !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ">="
26
+ - !ruby/object:Gem::Version
27
+ segments:
28
+ - 1
29
+ - 2
30
+ - 0
31
+ version: 1.2.0
32
+ type: :runtime
33
+ version_requirements: *id001
34
+ description: A really simple pagination library, heavily based on the agnostic branch of will_paginate
35
+ email: peter@c7.se
36
+ executables: []
37
+
38
+ extensions: []
39
+
40
+ extra_rdoc_files:
41
+ - README.rdoc
42
+ - MIT-LICENSE
43
+ files:
44
+ - lib/leaf/array.rb
45
+ - lib/leaf/collection.rb
46
+ - lib/leaf/core_ext.rb
47
+ - lib/leaf/finders/base.rb
48
+ - lib/leaf/finders/sequel.rb
49
+ - lib/leaf/finders.rb
50
+ - lib/leaf/version.rb
51
+ - lib/leaf/view_helpers/base.rb
52
+ - lib/leaf/view_helpers/link_renderer.rb
53
+ - lib/leaf/view_helpers/link_renderer_base.rb
54
+ - lib/leaf/view_helpers/list_renderer.rb
55
+ - lib/leaf/view_helpers/sinatra.rb
56
+ - lib/leaf/view_helpers.rb
57
+ - lib/leaf.rb
58
+ - MIT-LICENSE
59
+ - Rakefile
60
+ - README.rdoc
61
+ has_rdoc: true
62
+ homepage: http://c7.github.com/leaf/
63
+ licenses:
64
+ - MIT-LICENSE
65
+ post_install_message:
66
+ rdoc_options:
67
+ - --main
68
+ - README.rdoc
69
+ - --charset=UTF-8
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ requirements:
74
+ - - ">="
75
+ - !ruby/object:Gem::Version
76
+ segments:
77
+ - 0
78
+ version: "0"
79
+ required_rubygems_version: !ruby/object:Gem::Requirement
80
+ requirements:
81
+ - - ">="
82
+ - !ruby/object:Gem::Version
83
+ segments:
84
+ - 0
85
+ version: "0"
86
+ requirements: []
87
+
88
+ rubyforge_project: leaf
89
+ rubygems_version: 1.3.6
90
+ signing_key:
91
+ specification_version: 3
92
+ summary: Simple pagination library
93
+ test_files: []
94
+