godfat-pagify 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,6 @@
1
+
2
+ # anything for array
3
+
4
+ require 'pagify'
5
+ require 'pagify/pagers/array'
6
+ require 'pagify/pagifiers/array'
@@ -0,0 +1,3 @@
1
+
2
+ require 'pagify/pagers/basic'
3
+ require 'pagify/pages/basic'
@@ -0,0 +1,16 @@
1
+
2
+ # anything for data_mapper
3
+
4
+ gem 'extlib', '<=0.9.8'
5
+ gem 'dm-core', '<=0.9.7'
6
+ gem 'dm-aggregates', '<=0.9.7'
7
+
8
+ require 'dm-core'
9
+ require 'dm-aggregates'
10
+
11
+ require 'pagify'
12
+ require 'pagify/pagers/details/page_accept_string_or_blank'
13
+
14
+ require 'pagify/pagers/data_mapper'
15
+ require 'pagify/pagifiers/data_mapper'
16
+
@@ -0,0 +1,92 @@
1
+
2
+ module Pagify
3
+ module Helpers
4
+ class Abstract
5
+ def self.default_attributes
6
+ raise NotImplementedError
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_prev(outer_prev, inner_prev),
37
+ caculate_post(inner_post, outer_post) ]
38
+ end
39
+ def caculate_prev outer_prev, inner_prev
40
+ # concat outer and inner, remove overlap
41
+ links_prev = outer_prev + [setting[:ellipsis]] + inner_prev
42
+
43
+ # clean up overlap and remove '...' if there's overlap or no pages there
44
+ links_prev.delete(setting[:ellipsis]) if links_prev.uniq! || links_prev.size == 1
45
+
46
+ links_prev
47
+ end
48
+ def caculate_post inner_post, outer_post
49
+ # concat outer and inner, remove overlap
50
+ links_post = inner_post + [setting[:ellipsis]] + outer_post
51
+
52
+ # clean up overlap and remove '...' if there's overlap or no pages there
53
+ links_post.delete(setting[:ellipsis]) if links_post.uniq! || links_post.size == 1
54
+
55
+ links_post
56
+ end
57
+ def caculate_4_parts page, size
58
+ inner_prev, inner_post = caculate_inner(setting[:inner_links], page, size)
59
+ outer_prev, outer_post = caculate_outer(setting[:outer_links], page, size,
60
+ inner_prev, inner_post)
61
+
62
+ [outer_prev, inner_prev, inner_post, outer_post]
63
+ end
64
+ def caculate_inner limit, page, size
65
+ [ (1..limit).map{ |i| page_exists?(page - i, size) }.reverse.compact,
66
+ (1..limit).map{ |i| page_exists?(page + i, size) }.compact ]
67
+ end
68
+ def caculate_outer limit, page, size, inner_prev, inner_post
69
+ # caculate index
70
+ prev_last = [limit, (inner_prev.first || 1) ].min
71
+ post_first = [size - limit + 1, (inner_post.last || size)].max
72
+
73
+ # create outer
74
+ outer_prev, outer_post = [ ( 1 .. prev_last ).to_a,
75
+ ( post_first .. size ).to_a ]
76
+
77
+ # remove current page
78
+ outer_prev.delete(page)
79
+ outer_post.delete(page)
80
+
81
+ [outer_prev, outer_post]
82
+ end
83
+ def page_exists? page, size
84
+ if page >= 1 && page <= size
85
+ page
86
+ else
87
+ nil
88
+ end
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,54 @@
1
+
2
+ require 'pagify/helpers/abstract'
3
+
4
+ module Pagify
5
+ module Helpers
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
53
+ end
54
+ end
@@ -0,0 +1,23 @@
1
+
2
+ module Pagify
3
+ module Helpers
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,85 @@
1
+
2
+ require 'pagify/helpers/abstract'
3
+
4
+ require 'pagify/helpers/details/setting'
5
+ require 'pagify/helpers/details/setup'
6
+
7
+ module Pagify
8
+ module Helpers
9
+ class HTML < Abstract
10
+ def self.default_attributes
11
+ { :first_text => '&laquo; First',
12
+ :last_text => 'Last &raquo;',
13
+ :prev_text => '&lt; Previous',
14
+ :next_text => 'Next &gt;',
15
+ :inner_links => 4,
16
+ :outer_links => 2,
17
+ # :step => 3,
18
+ :separator => ' ',
19
+ :ellipsis => '...' }
20
+ end
21
+
22
+ def links_full page, &block
23
+ page = normalize_page(page)
24
+ "#{links_navigate(page, &block)}<br />\n#{links(page, &block)}"
25
+ end
26
+
27
+ def links_navigate page
28
+ page = normalize_page(page)
29
+ size = pager.size
30
+ attrs = extract_html_attributes
31
+
32
+ prev = if page > 1 && page_exists?(page - 1, size)
33
+ "<a href=\"#{yield(page - 1)}\"#{attrs}>#{setting[:prev_text]}</a>"
34
+ else
35
+ nil
36
+ end
37
+
38
+ post = if page < size && page_exists?(page + 1, size)
39
+ "<a href=\"#{yield(page + 1)}\"#{attrs}>#{setting[:next_text]}</a>"
40
+ else
41
+ nil
42
+ end
43
+
44
+ [prev, post].compact.join(setting[:separator])
45
+ end
46
+
47
+ def links page
48
+ page = normalize_page(page)
49
+ size = pager.size
50
+ attrs = extract_html_attributes
51
+
52
+ prepare_links(page).map{ |i|
53
+ if i == page
54
+ case page
55
+ when 1; setting[:first_text]
56
+ when size; setting[ :last_text]
57
+ else; page
58
+ end
59
+ else
60
+ case i
61
+ when 1; "<a href=\"#{yield(i)}\"#{attrs}>#{setting[:first_text]}</a>"
62
+ when size; "<a href=\"#{yield(i)}\"#{attrs}>#{setting[ :last_text]}</a>"
63
+ when Fixnum; "<a href=\"#{yield(i)}\"#{attrs}>#{i}</a>"
64
+ else; i.to_s
65
+ end
66
+ end
67
+ }.join(setting[:separator])
68
+ end
69
+
70
+ private
71
+ def extract_html_attributes
72
+ attrs = setting.additional_attributes.
73
+ map{ |key, value| "#{key}=\"#{value}\"" }.join(' ')
74
+
75
+ attrs = ' ' + attrs if attrs != ''
76
+ attrs
77
+ end
78
+
79
+ def normalize_page page
80
+ pager.__send__(:normalize_page, page)
81
+ end
82
+ end
83
+ setup HTML
84
+ end
85
+ end
@@ -0,0 +1,5 @@
1
+
2
+ require 'singleton'
3
+
4
+ require 'pagify/pagers/null'
5
+ require 'pagify/pages/null'
@@ -0,0 +1,32 @@
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
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,22 @@
1
+
2
+ module Pagify
3
+ # array paginator would just simply assume your data is an array,
4
+ # and create pages simply for your_data[offset, per_page]
5
+ # if your data is much more complex, use Paginator instead of this
6
+ class ArrayPager < BasicPager
7
+ attr_reader :data
8
+
9
+ # data that you passed in this paginator
10
+ def initialize data, opts = {}
11
+ @data = data
12
+ super(opts.merge(
13
+ :fetcher => lambda{ |offset, per_page|
14
+ data[offset, per_page]
15
+ },
16
+ :counter => lambda{
17
+ data.size
18
+ }))
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,114 @@
1
+
2
+ module Pagify
3
+ # pager is something help you paginate the data,
4
+ # through the fetcher and counter you pass in.
5
+ # e.g., for a array, the fetcher would be array[offset, per_page],
6
+ # the counter would be array.size. and for data mapper's resource,
7
+ # fetcher would be Model.all :offset => offset, :limit => per_page.
8
+ # see ArrayPager and DataMapperPager, ActiveRecordPager, etc.
9
+ # if you just simply need them or have a look at the source code
10
+ # for example usage for Pager.
11
+ class BasicPager
12
+ include Enumerable
13
+ attr_reader :fetcher, :counter
14
+ attr_accessor :opts
15
+
16
+ [:per_page, :null_page].each{ |attr|
17
+ define_method(attr) do
18
+ opts[attr]
19
+ end
20
+
21
+ define_method("#{attr}=") do |new_value|
22
+ opts[attr] = new_value
23
+ end
24
+ }
25
+
26
+ # fetcher is function that fetch the data,
27
+ # counter is function that count the data,
28
+ # null_page indicates that pager sohuld
29
+ # the default per_page is 20. you can reset per_page property later.
30
+ def initialize opts = {}
31
+ raise ArgumentError.new('missing fetcher and/or counter.') if
32
+ !opts[:fetcher] || !opts[:counter]
33
+
34
+ raise ArgumentError.new('fetcher or counter does not respond to call.') if
35
+ !opts[:fetcher].respond_to?(:call) || !opts[:counter].respond_to?(:call)
36
+
37
+ @opts = {}
38
+ @fetcher = opts[:fetcher]
39
+ @counter = opts[:counter]
40
+
41
+ self.per_page = opts[:per_page] || 20
42
+ self.null_page = opts[:null_page] || true
43
+ end
44
+
45
+ # if two paginators are equal, then the properties of
46
+ # per_page, fetcher, counter are all equal.
47
+ def == rhs
48
+ return false unless rhs
49
+ self.per_page == rhs.per_page and
50
+ self.fetcher == rhs.fetcher and
51
+ self.counter == rhs.counter
52
+ end
53
+
54
+ # for each page...
55
+ def each; 1.upto(size){ |i| yield page(i) }; end
56
+
57
+ # return all pages in an array
58
+ def to_a; map{ |e| e }; end
59
+ alias_method :pages, :to_a
60
+
61
+ def page_exists? page
62
+ page = normalize_page(page)
63
+ offset = (page - 1) * per_page
64
+ page > 0 && offset < entries_count
65
+ end
66
+
67
+ # create page instance by page number.
68
+ # if page number you specified was not existed,
69
+ # nil would be returned. note, page start at 1, not zero.
70
+ def page page
71
+ if page_exists?(page)
72
+ return BasicPage.new(self, normalize_page(page))
73
+
74
+ else
75
+ if null_page
76
+ return null_page_instance
77
+ else
78
+ return nil
79
+ end
80
+
81
+ end
82
+
83
+ end
84
+ alias_method :[], :page
85
+
86
+ # return the amount of pages
87
+ def size; (entries_count / per_page.to_f).ceil; end
88
+
89
+ # simply call @counter.call
90
+ def entries_count; counter.call; end
91
+
92
+ # get the offset property about the page.
93
+ # it is simply (page-1)*@per_page
94
+ def offset page
95
+ (normalize_page(page) - 1) * per_page
96
+ end
97
+
98
+ protected
99
+ def reject_pager_opts opts
100
+ opts.reject{ |key, value| [:per_page, :null_page].member?(key) }
101
+ end
102
+
103
+ # do nothing for basic pager
104
+ def normalize_page page
105
+ page
106
+ end
107
+
108
+ # return a null pager that stubs anything to 0
109
+ def null_page_instance
110
+ @null_page_instance ||= NullPage.new(self)
111
+ end
112
+
113
+ end
114
+ end