pagify 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (55) hide show
  1. data/CHANGES +42 -0
  2. data/LICENSE +201 -0
  3. data/NOTICE +5 -0
  4. data/README +135 -0
  5. data/Rakefile +54 -0
  6. data/TODO +12 -0
  7. data/lib/pagify.rb +14 -0
  8. data/lib/pagify/active_record.rb +17 -0
  9. data/lib/pagify/array.rb +6 -0
  10. data/lib/pagify/data_mapper.rb +8 -0
  11. data/lib/pagify/helper/abstract.rb +112 -0
  12. data/lib/pagify/helper/detail/setting.rb +54 -0
  13. data/lib/pagify/helper/detail/setup.rb +23 -0
  14. data/lib/pagify/helper/html.rb +86 -0
  15. data/lib/pagify/helper/rails.rb +23 -0
  16. data/lib/pagify/page/basic.rb +56 -0
  17. data/lib/pagify/page/null.rb +10 -0
  18. data/lib/pagify/pager/active_record.rb +30 -0
  19. data/lib/pagify/pager/array.rb +21 -0
  20. data/lib/pagify/pager/basic.rb +114 -0
  21. data/lib/pagify/pager/data_mapper.rb +29 -0
  22. data/lib/pagify/pager/detail/page_accept_string_or_blank.rb +13 -0
  23. data/lib/pagify/pager/null.rb +15 -0
  24. data/lib/pagify/pagifier/abstract.rb +10 -0
  25. data/lib/pagify/pagifier/active_record.rb +11 -0
  26. data/lib/pagify/pagifier/array.rb +9 -0
  27. data/lib/pagify/pagifier/data_mapper.rb +11 -0
  28. data/lib/pagify/version.rb +4 -0
  29. data/pagify.gemspec +43 -0
  30. data/spec/pagify_spec.rb +8 -0
  31. data/spec/spec_helper.rb +17 -0
  32. data/tasks/ann.rake +80 -0
  33. data/tasks/bones.rake +20 -0
  34. data/tasks/gem.rake +201 -0
  35. data/tasks/git.rake +40 -0
  36. data/tasks/notes.rake +27 -0
  37. data/tasks/post_load.rake +34 -0
  38. data/tasks/rdoc.rake +51 -0
  39. data/tasks/rubyforge.rake +55 -0
  40. data/tasks/setup.rb +292 -0
  41. data/tasks/spec.rake +54 -0
  42. data/tasks/svn.rake +47 -0
  43. data/tasks/test.rake +40 -0
  44. data/tasks/zentest.rake +36 -0
  45. data/test/helper.rb +26 -0
  46. data/test/helper_model.rb +83 -0
  47. data/test/helper_pagify.rb +39 -0
  48. data/test/test_active_record.rb +66 -0
  49. data/test/test_array.rb +19 -0
  50. data/test/test_basic.rb +22 -0
  51. data/test/test_data_mapper.rb +56 -0
  52. data/test/test_html.rb +186 -0
  53. data/test/test_null.rb +31 -0
  54. data/test/test_rails.rb +32 -0
  55. metadata +163 -0
@@ -0,0 +1,14 @@
1
+
2
+ module Pagify
3
+ autoload :ArrayPager, 'pagify/array'
4
+ autoload :DataMapperPager, 'pagify/data_mapper'
5
+ autoload :ActiveRecordPager, 'pagify/active_record'
6
+ end
7
+
8
+ require 'pagify/pager/basic'
9
+ require 'pagify/page/basic'
10
+
11
+ require 'singleton'
12
+
13
+ require 'pagify/pager/null'
14
+ require 'pagify/page/null'
@@ -0,0 +1,17 @@
1
+
2
+ # anything for active_record
3
+
4
+ require 'pagify'
5
+ require 'pagify/pager/detail/page_accept_string_or_blank'
6
+
7
+ require 'pagify/pager/active_record'
8
+ require 'pagify/pagifier/active_record'
9
+
10
+ require 'pagify/array' # this was needed for active_record...
11
+
12
+ # workaround for active_record...
13
+ module Pagify
14
+ class ArrayPager < BasicPager
15
+ include PageAcceptStringOrBlank
16
+ end
17
+ end
@@ -0,0 +1,6 @@
1
+
2
+ # anything for array
3
+
4
+ require 'pagify'
5
+ require 'pagify/pager/array'
6
+ require 'pagify/pagifier/array'
@@ -0,0 +1,8 @@
1
+
2
+ # anything for data_mapper
3
+
4
+ require 'pagify'
5
+ require 'pagify/pager/detail/page_accept_string_or_blank'
6
+
7
+ require 'pagify/pager/data_mapper'
8
+ require 'pagify/pagifier/data_mapper'
@@ -0,0 +1,112 @@
1
+
2
+ module Pagify
3
+ module Helper
4
+ class Abstract
5
+ def self.default_attributes
6
+ {}
7
+ end
8
+
9
+ def self.setting
10
+ @setting ||= Setting.new(self)
11
+ end
12
+
13
+ attr_reader :pager
14
+ def initialize pager
15
+ @pager = pager
16
+ end
17
+
18
+ def setting
19
+ @setting ||= Setting.new(self, self.class.setting)
20
+ end
21
+
22
+ protected
23
+ def prepare_links page
24
+ size = pager.size
25
+ return [] if page < 1 || page > size
26
+
27
+ links_prev, links_post = caculate_2_parts(*caculate_4_parts(page, size))
28
+
29
+ current = links_prev.empty? && links_post.empty? ? [] : [page]
30
+
31
+ links_prev + current + links_post
32
+ end
33
+
34
+ private
35
+ def caculate_2_parts outer_prev, inner_prev, inner_post, outer_post
36
+ [ caculate_part(outer_prev, inner_prev),
37
+ caculate_part(inner_post, outer_post) ]
38
+ end
39
+
40
+ # concat outer and inner, remove overlap
41
+ def caculate_part left, right
42
+ # don't use ellipsis if there's no spaces between them
43
+ ellipsis = !left.empty? && (left.last + 1) == right.first ?
44
+ [] : [setting[:ellipsis]]
45
+
46
+ part = left + ellipsis + right
47
+
48
+ # clean up overlap and remove '...' if
49
+ part.delete(setting[:ellipsis]) if
50
+ part.uniq! || # there's overlap
51
+ part.size == 1 # no pages there
52
+
53
+ part
54
+ end
55
+
56
+ def caculate_4_parts page, size
57
+ inner_prev, inner_post = caculate_inner(setting[:inner_links], page, size)
58
+ outer_prev, outer_post = caculate_outer(setting[:outer_links], page, size,
59
+ inner_prev, inner_post)
60
+
61
+ [outer_prev, inner_prev, inner_post, outer_post]
62
+ end
63
+
64
+ def caculate_inner limit, page, size
65
+ prev, post = page - limit, page + limit
66
+
67
+ left = extract_pages( prev ... page, size )
68
+ right = extract_pages( (page + 1) .. post, size )
69
+
70
+ # adding more right pages if left pages were not enough.
71
+ right.push(
72
+ *extract_pages(
73
+ (post + 1) .. (post + limit - left.size), size )) if left.size < limit
74
+
75
+ # ditto
76
+ left.unshift(
77
+ *extract_pages(
78
+ (prev - limit - right.size) .. (prev - 1), size )) if right.size < limit
79
+
80
+ [left, right]
81
+ end
82
+
83
+ def caculate_outer limit, page, size, inner_prev, inner_post
84
+ # caculate index
85
+ prev_last = [limit, (inner_prev.first || 1) ].min
86
+ post_first = [size - limit + 1, (inner_post.last || size)].max
87
+
88
+ # create outer
89
+ outer_prev, outer_post = [ ( 1 .. prev_last ).to_a,
90
+ ( post_first .. size ).to_a ]
91
+
92
+ # remove current page
93
+ outer_prev.delete(page)
94
+ outer_post.delete(page)
95
+
96
+ [outer_prev, outer_post]
97
+ end
98
+
99
+ def extract_pages range, size
100
+ range.map{ |i| page_exists?(i, size) }.compact
101
+ end
102
+
103
+ def page_exists? page, size
104
+ if page >= 1 && page <= size
105
+ page
106
+ else
107
+ nil
108
+ end
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,54 @@
1
+
2
+ require 'pagify/helper/abstract'
3
+
4
+ module Pagify
5
+ module Helper
6
+ # TODO: refactor me!!
7
+ class Setting
8
+ def initialize helper, parent = {}
9
+ @helper = helper
10
+ @parent = parent
11
+ @attributes = extract_default_attributes helper
12
+ end
13
+
14
+ def [] key
15
+ if @attributes[key]
16
+ @attributes[key]
17
+ else
18
+ @parent[key]
19
+ end
20
+ end
21
+
22
+ def []= key, value
23
+ @attributes[key] = value
24
+ end
25
+
26
+ def to_hash
27
+ @attributes.dup
28
+ end
29
+
30
+ def restore_default!
31
+ @attributes = extract_default_attributes @helper
32
+ end
33
+
34
+ def additional_attributes
35
+ defaults = extract_default_attributes(
36
+ @helper.kind_of?(Abstract) ? @helper.class : @helper).keys
37
+
38
+ @parent.to_hash.merge(@attributes).reject{ |key, value| defaults.member?(key) }
39
+ end
40
+
41
+ private
42
+ def extract_default_attributes helper
43
+ if helper.respond_to? :default_attributes
44
+ helper.default_attributes
45
+ else
46
+ {}
47
+ end
48
+ rescue NotImplementedError
49
+ {}
50
+ end
51
+
52
+ end # of Setting
53
+ end # of Helper
54
+ end # of Pagify
@@ -0,0 +1,23 @@
1
+
2
+ module Pagify
3
+ module Helper
4
+ def self.setup helper_class
5
+ Pagify::BasicPager.module_eval do
6
+ helper_name = helper_class.to_s.downcase[/::?(\w+)$/, 1]
7
+ define_method helper_name do
8
+ variable_name = "@helper_#{helper_name}"
9
+ instance_variable_get(variable_name) or
10
+ instance_variable_set(variable_name, helper_class.new(self))
11
+ end
12
+ end
13
+
14
+ # hmm.... how should i define this method?
15
+ # Pagify::BasicPage.module_eval do
16
+ # helper_name = helper_class.to_s.downcase[/::?(\w+)$/, 1]
17
+ # define_method helper_name do
18
+ # pager.__send__ helper_name
19
+ # end
20
+ # end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,86 @@
1
+
2
+ require 'pagify/helper/abstract'
3
+
4
+ require 'pagify/helper/detail/setting'
5
+ require 'pagify/helper/detail/setup'
6
+
7
+ module Pagify
8
+ module Helper
9
+ class HTML < Abstract
10
+ def self.default_attributes
11
+ super.merge(
12
+ { :first_text => '&laquo; First',
13
+ :last_text => 'Last &raquo;',
14
+ :prev_text => '&lt; Previous',
15
+ :next_text => 'Next &gt;',
16
+ :inner_links => 4,
17
+ :outer_links => 2,
18
+ # :step => 3,
19
+ :separator => ' ',
20
+ :ellipsis => '...' })
21
+ end
22
+
23
+ def links_full page, &block
24
+ page = normalize_page(page)
25
+ "#{links_navigate(page, &block)}<br />#{links(page, &block)}"
26
+ end
27
+
28
+ def links_navigate page
29
+ page = normalize_page(page)
30
+ size = pager.size
31
+ attrs = extract_html_attributes
32
+
33
+ prev = if page > 1 && page_exists?(page - 1, size)
34
+ "<a href=\"#{yield(page - 1)}\"#{attrs}>#{setting[:prev_text]}</a>"
35
+ else
36
+ nil
37
+ end
38
+
39
+ post = if page < size && page_exists?(page + 1, size)
40
+ "<a href=\"#{yield(page + 1)}\"#{attrs}>#{setting[:next_text]}</a>"
41
+ else
42
+ nil
43
+ end
44
+
45
+ [prev, post].compact.join(setting[:separator])
46
+ end
47
+
48
+ def links page
49
+ page = normalize_page(page)
50
+ size = pager.size
51
+ attrs = extract_html_attributes
52
+
53
+ prepare_links(page).map{ |i|
54
+ if i == page
55
+ case page
56
+ when 1; setting[:first_text]
57
+ when size; setting[ :last_text]
58
+ else; page
59
+ end
60
+ else
61
+ case i
62
+ when 1; "<a href=\"#{yield(i)}\"#{attrs}>#{setting[:first_text]}</a>"
63
+ when size; "<a href=\"#{yield(i)}\"#{attrs}>#{setting[ :last_text]}</a>"
64
+ when Fixnum; "<a href=\"#{yield(i)}\"#{attrs}>#{i}</a>"
65
+ else; i.to_s
66
+ end
67
+ end
68
+ }.join(setting[:separator])
69
+ end
70
+
71
+ private
72
+ def extract_html_attributes
73
+ attrs = setting.additional_attributes.
74
+ map{ |key, value| "#{key}=\"#{value}\"" }.join(' ')
75
+
76
+ attrs = ' ' + attrs if attrs != ''
77
+ attrs
78
+ end
79
+
80
+ def normalize_page page
81
+ pager.__send__(:normalize_page, page)
82
+ end
83
+ end # of HTML
84
+ setup HTML
85
+ end # of Helper
86
+ end # of Pagify
@@ -0,0 +1,23 @@
1
+
2
+ require 'pagify/helper/html'
3
+
4
+ module Pagify::Helper::Rails
5
+ def default_attributes
6
+ super.merge(:query_name => :page, :wrapper_class => 'pagination')
7
+ end
8
+ end
9
+
10
+ class Pagify::Helper::HTML
11
+ extend Pagify::Helper::Rails
12
+ end
13
+
14
+ module ApplicationHelper
15
+ def would_paginate objs, &path
16
+ path = lambda{ request.path } unless block_given?
17
+ html = objs.pager.html
18
+ name = html.setting[:query_name]
19
+ "<div class=\"#{html.setting[:wrapper_class]}\">" +
20
+ html.links_full(params[name]){ |p| path.call + "?#{name}=#{p}" } +
21
+ '</div>'
22
+ end
23
+ end
@@ -0,0 +1,56 @@
1
+
2
+ module Pagify
3
+ # which was produced by Paginator#page / Paginator#[],
4
+ # representing page that contained target data.
5
+ # it would use lazy fetching, whenever you need the data,
6
+ # the fetcher would be invoked that time. whenever the data
7
+ # was fetched, it won't fetch again. it you need refetch,
8
+ # call Page#fetch
9
+ class BasicPage
10
+ # prvent Page#to_a return [#<Page:0x12345>]
11
+ undef_method :to_a if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('1.9.0')
12
+
13
+ # pager to get the original pager; page to get the number of this page
14
+ attr_reader :pager, :page
15
+
16
+ # don't create a page instance yourself unless you have to
17
+ def initialize pager, page; @pager, @page = pager, page; end
18
+
19
+ # return the page instance next to this page
20
+ def next; pager.page(page + 1); end
21
+
22
+ # return the page instance prev to this page
23
+ def prev; pager.page(page - 1); end
24
+
25
+ # if the page numbers and the pagers are equal,
26
+ # then the pages are equal.
27
+ def == rhs
28
+ rhs.kind_of?(BasicPage) &&
29
+ page == rhs.page &&
30
+ pager == rhs.pager
31
+ end
32
+
33
+ # explicitly fetch the data from pager
34
+ def fetch; @data = pager.fetcher[self.begin, pager.per_page]; end
35
+
36
+ # get the data fetched from pager
37
+ def data; @data ||= fetch; end
38
+
39
+ # the real beginning index
40
+ def begin; pager.offset page; end
41
+
42
+ # the real ending index (need fetch, because we don't know the size)
43
+ def end; self.begin + self.size - 1; end
44
+
45
+ # any other method call would delegate to the data,
46
+ # e.g., each, to_a, map, first, method_you_defined, etc.
47
+ def method_missing msg, *args, &block
48
+ if data.respond_to?(msg)
49
+ data.__send__(msg, *args, &block)
50
+ else
51
+ super(msg, *args, &block)
52
+ end
53
+ end
54
+
55
+ end # of BasicPage
56
+ end # of Pagify
@@ -0,0 +1,10 @@
1
+
2
+ module Pagify
3
+ # a null page that stubs anything to 0 or [], etc.
4
+ class NullPage < BasicPage
5
+ def initialize pager = NullPager.instance, page = 0
6
+ super(pager, page)
7
+ @data = []
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,30 @@
1
+
2
+ # you'll need activerecord to use ActiveRecordPager
3
+
4
+ module Pagify
5
+ # active_record paginator was provided for convenience,
6
+ # it wraps Paginator for you, and you can just pass the model class to it.
7
+ # you don't have to care about the fetcher and counter.
8
+ # additionally, you can pass other options to rails paginator,
9
+ # they would be used in find options. e.g.,
10
+ # ActiveRecordPaginator.new Model, :order => 'created_at DESC'
11
+ # would invoke Model.find :all, :offset => ?, :limit => ?, order => 'created_at DESC'
12
+ class ActiveRecordPager < BasicPager
13
+ include PageAcceptStringOrBlank
14
+ # the model class that you passed in this paginator
15
+ attr_reader :model
16
+
17
+ def initialize model_class, opts = {}
18
+ @model = model_class
19
+ query_opts = reject_pager_opts(opts)
20
+
21
+ super(opts.merge(
22
+ :fetcher => lambda{ |offset, per_page|
23
+ model.find(:all, query_opts.merge(:offset => offset, :limit => per_page))
24
+ },
25
+ :counter => lambda{
26
+ model.count(query_opts)
27
+ }))
28
+ end
29
+ end # of ActiveRecordPager
30
+ end # of Pagify