godfat-pagify 0.5.0

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.
@@ -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