bootstrap_pager 0.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +18 -0
- data/.idea/encodings.xml +5 -0
- data/.idea/misc.xml +5 -0
- data/.idea/modules.xml +9 -0
- data/.idea/pager.iml +50 -0
- data/.idea/scopes/scope_settings.xml +5 -0
- data/.idea/vcs.xml +7 -0
- data/Gemfile +4 -0
- data/LICENSE +20 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +307 -0
- data/Rakefile +42 -0
- data/app/assets/javascripts/infinitescroll.js +8 -0
- data/app/helpers/pager_helper.rb +5 -0
- data/app/views/pager/_first_page.html.erb +11 -0
- data/app/views/pager/_first_page.html.haml +9 -0
- data/app/views/pager/_first_page.html.slim +10 -0
- data/app/views/pager/_gap.html.erb +8 -0
- data/app/views/pager/_gap.html.haml +8 -0
- data/app/views/pager/_gap.html.slim +10 -0
- data/app/views/pager/_last_page.html.erb +11 -0
- data/app/views/pager/_last_page.html.haml +9 -0
- data/app/views/pager/_last_page.html.slim +10 -0
- data/app/views/pager/_next_page.html.erb +11 -0
- data/app/views/pager/_next_page.html.haml +9 -0
- data/app/views/pager/_next_page.html.slim +10 -0
- data/app/views/pager/_page.html.erb +14 -0
- data/app/views/pager/_page.html.haml +11 -0
- data/app/views/pager/_page.html.slim +13 -0
- data/app/views/pager/_paginator.html.erb +25 -0
- data/app/views/pager/_paginator.html.haml +19 -0
- data/app/views/pager/_paginator.html.slim +20 -0
- data/app/views/pager/_prev_page.html.erb +11 -0
- data/app/views/pager/_prev_page.html.haml +9 -0
- data/app/views/pager/_prev_page.html.slim +10 -0
- data/bootstrap_pager.gemspec +36 -0
- data/config/locales/pager.yml +19 -0
- data/gemfiles/active_record_30.gemfile +9 -0
- data/gemfiles/active_record_31.gemfile +7 -0
- data/gemfiles/active_record_32.gemfile +10 -0
- data/gemfiles/active_record_40.gemfile +8 -0
- data/gemfiles/active_record_edge.gemfile +11 -0
- data/gemfiles/data_mapper_12.gemfile +15 -0
- data/gemfiles/mongo_mapper.gemfile +10 -0
- data/gemfiles/mongoid_24.gemfile +7 -0
- data/gemfiles/mongoid_30.gemfile +12 -0
- data/gemfiles/sinatra_13.gemfile +13 -0
- data/gemfiles/sinatra_14.gemfile +13 -0
- data/lib/bootstrap_pager/config.rb +51 -0
- data/lib/bootstrap_pager/grape.rb +4 -0
- data/lib/bootstrap_pager/helpers/action_view_extension.rb +152 -0
- data/lib/bootstrap_pager/helpers/paginator.rb +186 -0
- data/lib/bootstrap_pager/helpers/sinatra_helpers.rb +144 -0
- data/lib/bootstrap_pager/helpers/tags.rb +96 -0
- data/lib/bootstrap_pager/hooks.rb +41 -0
- data/lib/bootstrap_pager/models/active_record_extension.rb +22 -0
- data/lib/bootstrap_pager/models/active_record_model_extension.rb +20 -0
- data/lib/bootstrap_pager/models/active_record_relation_methods.rb +29 -0
- data/lib/bootstrap_pager/models/array_extension.rb +58 -0
- data/lib/bootstrap_pager/models/configuration_methods.rb +48 -0
- data/lib/bootstrap_pager/models/data_mapper_collection_methods.rb +15 -0
- data/lib/bootstrap_pager/models/data_mapper_extension.rb +48 -0
- data/lib/bootstrap_pager/models/mongo_mapper_extension.rb +18 -0
- data/lib/bootstrap_pager/models/mongoid_criteria_methods.rb +23 -0
- data/lib/bootstrap_pager/models/mongoid_extension.rb +33 -0
- data/lib/bootstrap_pager/models/page_scope_methods.rb +70 -0
- data/lib/bootstrap_pager/models/plucky_criteria_methods.rb +18 -0
- data/lib/bootstrap_pager/railtie.rb +7 -0
- data/lib/bootstrap_pager/sinatra.rb +5 -0
- data/lib/bootstrap_pager/version.rb +3 -0
- data/lib/bootstrap_pager.rb +38 -0
- data/lib/generators/pager/config_generator.rb +16 -0
- data/lib/generators/pager/templates/pager_config.rb +10 -0
- data/lib/generators/pager/views_generator.rb +118 -0
- data/spec/config/config_spec.rb +91 -0
- data/spec/fake_app/active_record/config.rb +3 -0
- data/spec/fake_app/active_record/models.rb +57 -0
- data/spec/fake_app/data_mapper/config.rb +7 -0
- data/spec/fake_app/data_mapper/models.rb +27 -0
- data/spec/fake_app/log/development.log +832 -0
- data/spec/fake_app/mongo_mapper/config.rb +2 -0
- data/spec/fake_app/mongo_mapper/models.rb +9 -0
- data/spec/fake_app/mongoid/config.rb +16 -0
- data/spec/fake_app/mongoid/models.rb +22 -0
- data/spec/fake_app/rails_app.rb +67 -0
- data/spec/fake_app/sinatra_app.rb +22 -0
- data/spec/fake_gem.rb +4 -0
- data/spec/helpers/action_view_extension_spec.rb +292 -0
- data/spec/helpers/helpers_spec.rb +135 -0
- data/spec/helpers/sinatra_helpers_spec.rb +170 -0
- data/spec/helpers/tags_spec.rb +140 -0
- data/spec/models/active_record/active_record_relation_methods_spec.rb +47 -0
- data/spec/models/active_record/default_per_page_spec.rb +32 -0
- data/spec/models/active_record/max_pages_spec.rb +23 -0
- data/spec/models/active_record/max_per_page_spec.rb +32 -0
- data/spec/models/active_record/scopes_spec.rb +242 -0
- data/spec/models/array_spec.rb +150 -0
- data/spec/models/data_mapper/data_mapper_spec.rb +207 -0
- data/spec/models/mongo_mapper/mongo_mapper_spec.rb +84 -0
- data/spec/models/mongoid/mongoid_spec.rb +126 -0
- data/spec/requests/users_spec.rb +53 -0
- data/spec/spec_helper.rb +33 -0
- data/spec/spec_helper_for_sinatra.rb +34 -0
- data/spec/support/database_cleaner.rb +16 -0
- data/spec/support/matchers.rb +52 -0
- data/vendor/assets/javascripts/jquery.infinitescroll.js +814 -0
- data/vendor/assets/javascripts/jquery.infinitescroll.min.js +1 -0
- metadata +311 -0
@@ -0,0 +1,186 @@
|
|
1
|
+
require 'active_support/inflector'
|
2
|
+
require 'action_view'
|
3
|
+
require 'action_view/log_subscriber'
|
4
|
+
require 'action_view/context'
|
5
|
+
require 'bootstrap_pager/helpers/tags'
|
6
|
+
|
7
|
+
module BootstrapPager
|
8
|
+
module Helpers
|
9
|
+
# The main container tag
|
10
|
+
class Paginator < Tag
|
11
|
+
# so that this instance can actually "render"
|
12
|
+
include ::ActionView::Context
|
13
|
+
|
14
|
+
def initialize(template, options) #:nodoc:
|
15
|
+
@window_options = {}.tap do |h|
|
16
|
+
h[:window] = options.delete(:window) || options.delete(:inner_window) || BootstrapPager.config.window
|
17
|
+
outer_window = options.delete(:outer_window) || BootstrapPager.config.outer_window
|
18
|
+
h[:left] = options.delete(:left) || BootstrapPager.config.left
|
19
|
+
h[:left] = outer_window if h[:left] == 0
|
20
|
+
h[:right] = options.delete(:right) || BootstrapPager.config.right
|
21
|
+
h[:right] = outer_window if h[:right] == 0
|
22
|
+
end
|
23
|
+
@template, @options = template, options
|
24
|
+
@theme = @options[:theme] ? "#{@options[:theme]}/" : ''
|
25
|
+
@options[:current_page] = PageProxy.new @window_options.merge(@options), @options[:current_page], nil
|
26
|
+
#FIXME for compatibility. remove num_pages at some time in the future
|
27
|
+
@options[:total_pages] ||= @options[:num_pages]
|
28
|
+
@options[:num_pages] ||= @options[:total_pages]
|
29
|
+
@last = nil
|
30
|
+
# initialize the output_buffer for Context
|
31
|
+
@output_buffer = ActionView::OutputBuffer.new
|
32
|
+
end
|
33
|
+
|
34
|
+
# render given block as a view template
|
35
|
+
def render(&block)
|
36
|
+
instance_eval(&block) if @options[:total_pages] > 1
|
37
|
+
@output_buffer
|
38
|
+
end
|
39
|
+
|
40
|
+
# enumerate each page providing PageProxy object as the block parameter
|
41
|
+
# Because of performance reason, this doesn't actually enumerate all pages but pages that are seemingly relevant to the paginator.
|
42
|
+
# "Relevant" pages are:
|
43
|
+
# * pages inside the left outer window plus one for showing the gap tag
|
44
|
+
# * pages inside the inner window plus one on the left plus one on the right for showing the gap tags
|
45
|
+
# * pages inside the right outer window plus one for showing the gap tag
|
46
|
+
def each_relevant_page
|
47
|
+
return to_enum(:each_relevant_page) unless block_given?
|
48
|
+
|
49
|
+
relevant_pages(@window_options.merge(@options)).each do |i|
|
50
|
+
yield PageProxy.new(@window_options.merge(@options), i, @last)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
alias each_page each_relevant_page
|
54
|
+
|
55
|
+
def relevant_pages(options)
|
56
|
+
left_window_plus_one = 1.upto(options[:left] + 1).to_a
|
57
|
+
right_window_plus_one = (options[:total_pages] - options[:right]).upto(options[:total_pages]).to_a
|
58
|
+
inside_window_plus_each_sides = (options[:current_page] - options[:window] - 1).upto(options[:current_page] + options[:window] + 1).to_a
|
59
|
+
|
60
|
+
(left_window_plus_one + inside_window_plus_each_sides + right_window_plus_one).uniq.sort.reject {|x| (x < 1) || (x > options[:total_pages])}
|
61
|
+
end
|
62
|
+
private :relevant_pages
|
63
|
+
|
64
|
+
def page_tag(page)
|
65
|
+
@last = Page.new @template, @options.merge(:page => page)
|
66
|
+
end
|
67
|
+
|
68
|
+
%w[first_page prev_page next_page last_page gap].each do |tag|
|
69
|
+
eval <<-DEF
|
70
|
+
def #{tag}_tag
|
71
|
+
@last = #{tag.classify}.new @template, @options
|
72
|
+
end
|
73
|
+
DEF
|
74
|
+
end
|
75
|
+
|
76
|
+
def to_s #:nodoc:
|
77
|
+
subscriber = ActionView::LogSubscriber.log_subscribers.detect {|ls| ls.is_a? ActionView::LogSubscriber}
|
78
|
+
|
79
|
+
# There is a logging subscriber
|
80
|
+
# and we don't want it to log render_partial
|
81
|
+
# It is threadsafe, but might not repress logging
|
82
|
+
# consistently in a high-load environment
|
83
|
+
if subscriber
|
84
|
+
unless defined? subscriber.render_partial_with_logging
|
85
|
+
class << subscriber
|
86
|
+
alias_method :render_partial_with_logging, :render_partial
|
87
|
+
attr_accessor :render_without_logging
|
88
|
+
# ugly hack to make a renderer where
|
89
|
+
# we can turn logging on or off
|
90
|
+
def render_partial(event)
|
91
|
+
render_partial_with_logging(event) unless render_without_logging
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
subscriber.render_without_logging = true
|
97
|
+
ret = super @window_options.merge(@options).merge :paginator => self
|
98
|
+
subscriber.render_without_logging = false
|
99
|
+
|
100
|
+
ret
|
101
|
+
else
|
102
|
+
super @window_options.merge(@options).merge :paginator => self
|
103
|
+
end
|
104
|
+
end
|
105
|
+
|
106
|
+
# Wraps a "page number" and provides some utility methods
|
107
|
+
class PageProxy
|
108
|
+
include Comparable
|
109
|
+
|
110
|
+
def initialize(options, page, last) #:nodoc:
|
111
|
+
@options, @page, @last = options, page, last
|
112
|
+
end
|
113
|
+
|
114
|
+
# the page number
|
115
|
+
def number
|
116
|
+
@page
|
117
|
+
end
|
118
|
+
|
119
|
+
# current page or not
|
120
|
+
def current?
|
121
|
+
@page == @options[:current_page]
|
122
|
+
end
|
123
|
+
|
124
|
+
# the first page or not
|
125
|
+
def first?
|
126
|
+
@page == 1
|
127
|
+
end
|
128
|
+
|
129
|
+
# the last page or not
|
130
|
+
def last?
|
131
|
+
@page == @options[:total_pages]
|
132
|
+
end
|
133
|
+
|
134
|
+
# the previous page or not
|
135
|
+
def prev?
|
136
|
+
@page == @options[:current_page] - 1
|
137
|
+
end
|
138
|
+
|
139
|
+
# the next page or not
|
140
|
+
def next?
|
141
|
+
@page == @options[:current_page] + 1
|
142
|
+
end
|
143
|
+
|
144
|
+
# within the left outer window or not
|
145
|
+
def left_outer?
|
146
|
+
@page <= @options[:left]
|
147
|
+
end
|
148
|
+
|
149
|
+
# within the right outer window or not
|
150
|
+
def right_outer?
|
151
|
+
@options[:total_pages] - @page < @options[:right]
|
152
|
+
end
|
153
|
+
|
154
|
+
# inside the inner window or not
|
155
|
+
def inside_window?
|
156
|
+
(@options[:current_page] - @page).abs <= @options[:window]
|
157
|
+
end
|
158
|
+
|
159
|
+
# The last rendered tag was "truncated" or not
|
160
|
+
def was_truncated?
|
161
|
+
@last.is_a? Gap
|
162
|
+
end
|
163
|
+
|
164
|
+
def to_i
|
165
|
+
number
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_s
|
169
|
+
number.to_s
|
170
|
+
end
|
171
|
+
|
172
|
+
def +(other)
|
173
|
+
to_i + other.to_i
|
174
|
+
end
|
175
|
+
|
176
|
+
def -(other)
|
177
|
+
to_i - other.to_i
|
178
|
+
end
|
179
|
+
|
180
|
+
def <=>(other)
|
181
|
+
to_i <=> other.to_i
|
182
|
+
end
|
183
|
+
end
|
184
|
+
end
|
185
|
+
end
|
186
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
require 'active_support/core_ext/object'
|
2
|
+
require 'active_support/core_ext/string'
|
3
|
+
|
4
|
+
begin
|
5
|
+
|
6
|
+
require 'padrino-helpers'
|
7
|
+
module BootstrapPager::Helpers
|
8
|
+
module SinatraHelpers
|
9
|
+
class << self
|
10
|
+
def registered(app)
|
11
|
+
app.register Padrino::Helpers
|
12
|
+
app.helpers HelperMethods
|
13
|
+
@app = app
|
14
|
+
end
|
15
|
+
|
16
|
+
def view_paths
|
17
|
+
@app.views
|
18
|
+
end
|
19
|
+
|
20
|
+
alias included registered
|
21
|
+
end
|
22
|
+
|
23
|
+
class ActionViewTemplateProxy
|
24
|
+
include Padrino::Helpers::OutputHelpers
|
25
|
+
include Padrino::Helpers::TagHelpers
|
26
|
+
include Padrino::Helpers::AssetTagHelpers
|
27
|
+
include Padrino::Helpers::FormatHelpers
|
28
|
+
include Padrino::Helpers::TranslationHelpers
|
29
|
+
|
30
|
+
def initialize(opts={})
|
31
|
+
@current_path = opts[:current_path]
|
32
|
+
@param_name = (opts[:param_name] || :page).to_sym
|
33
|
+
@current_params = opts[:current_params]
|
34
|
+
@current_params.delete(@param_name)
|
35
|
+
end
|
36
|
+
|
37
|
+
def render(*args)
|
38
|
+
base = ActionView::Base.new.tap do |a|
|
39
|
+
a.view_paths << SinatraHelpers.view_paths
|
40
|
+
a.view_paths << File.expand_path('../../../../app/views', __FILE__)
|
41
|
+
end
|
42
|
+
base.render(*args)
|
43
|
+
end
|
44
|
+
|
45
|
+
def url_for(params)
|
46
|
+
extra_params = {}
|
47
|
+
if page = params[@param_name] and page != 1
|
48
|
+
extra_params[@param_name] = page
|
49
|
+
end
|
50
|
+
query = @current_params.merge(extra_params)
|
51
|
+
@current_path + (query.empty? ? '' : "?#{query.to_query}")
|
52
|
+
end
|
53
|
+
|
54
|
+
def link_to_unless(condition, name, options = {}, html_options = {}, &block)
|
55
|
+
options = url_for(options) if options.is_a? Hash
|
56
|
+
if condition
|
57
|
+
if block_given?
|
58
|
+
block.arity <= 1 ? capture(name, &block) : capture(name, options, html_options, &block)
|
59
|
+
else
|
60
|
+
name
|
61
|
+
end
|
62
|
+
else
|
63
|
+
link_to(name, options, html_options)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def params
|
68
|
+
@current_params
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
module HelperMethods
|
73
|
+
# A helper that renders the pagination links - for Sinatra.
|
74
|
+
#
|
75
|
+
# <%= paginate @articles %>
|
76
|
+
#
|
77
|
+
# ==== Options
|
78
|
+
# * <tt>:window</tt> - The "inner window" size (4 by default).
|
79
|
+
# * <tt>:outer_window</tt> - The "outer window" size (0 by default).
|
80
|
+
# * <tt>:left</tt> - The "left outer window" size (0 by default).
|
81
|
+
# * <tt>:right</tt> - The "right outer window" size (0 by default).
|
82
|
+
# * <tt>:params</tt> - url_for parameters for the links (:id, :locale, etc.)
|
83
|
+
# * <tt>:param_name</tt> - parameter name for page number in the links (:page by default)
|
84
|
+
# * <tt>:remote</tt> - Ajax? (false by default)
|
85
|
+
# * <tt>:ANY_OTHER_VALUES</tt> - Any other hash key & values would be directly passed into each tag as :locals value.
|
86
|
+
def paginate(scope, options = {}, &block)
|
87
|
+
current_path = env['PATH_INFO'] rescue nil
|
88
|
+
current_params = Rack::Utils.parse_query(env['QUERY_STRING']).symbolize_keys rescue {}
|
89
|
+
paginator = BootstrapPager::Helpers::Paginator.new(
|
90
|
+
ActionViewTemplateProxy.new(:current_params => current_params, :current_path => current_path, :param_name => options[:param_name] || BootstrapPager.config.param_name),
|
91
|
+
options.reverse_merge(:current_page => scope.current_page, :total_pages => scope.total_pages, :per_page => scope.limit_value, :param_name => BootstrapPager.config.param_name, :remote => false)
|
92
|
+
)
|
93
|
+
paginator.to_s
|
94
|
+
end
|
95
|
+
|
96
|
+
# A simple "Twitter like" pagination link that creates a link to the next page.
|
97
|
+
# Works on Sinatra.
|
98
|
+
#
|
99
|
+
# ==== Examples
|
100
|
+
# Basic usage:
|
101
|
+
#
|
102
|
+
# <%= link_to_next_page @items, 'Next Page' %>
|
103
|
+
#
|
104
|
+
# Ajax:
|
105
|
+
#
|
106
|
+
# <%= link_to_next_page @items, 'Next Page', :remote => true %>
|
107
|
+
#
|
108
|
+
# By default, it renders nothing if there are no more results on the next page.
|
109
|
+
# You can customize this output by passing a parameter <tt>:placeholder</tt>.
|
110
|
+
#
|
111
|
+
# <%= link_to_next_page @items, 'Next Page', :placeholder => %{<span>No More Pages</span>} %>
|
112
|
+
#
|
113
|
+
def link_to_next_page(scope, name, options = {})
|
114
|
+
params = options.delete(:params) || (Rack::Utils.parse_query(env['QUERY_STRING']).symbolize_keys rescue {})
|
115
|
+
param_name = options.delete(:param_name) || BootstrapPager.config.param_name
|
116
|
+
placeholder = options.delete(:placeholder)
|
117
|
+
query = params.merge(param_name => (scope.current_page + 1))
|
118
|
+
unless scope.last_page?
|
119
|
+
link_to name, env['PATH_INFO'] + (query.empty? ? '' : "?#{query.to_query}"), options.reverse_merge(:rel => 'next')
|
120
|
+
else
|
121
|
+
placeholder
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
if defined? I18n
|
129
|
+
I18n.load_path += Dir.glob(File.expand_path('../../../../config/locales/*.yml', __FILE__))
|
130
|
+
end
|
131
|
+
|
132
|
+
rescue LoadError
|
133
|
+
|
134
|
+
$stderr.puts "[!]You should install `padrino-helpers' gem if you want to use bootstrap_pager's pagination helpers with Sinatra."
|
135
|
+
$stderr.puts "[!]BootstrapPager::Helpers::SinatraHelper does nothing now..."
|
136
|
+
|
137
|
+
module BootstrapPager::Helpers
|
138
|
+
module SinatraHelper
|
139
|
+
def self.registered(*)
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module BootstrapPager
|
2
|
+
module Helpers
|
3
|
+
# A tag stands for an HTML tag inside the paginator.
|
4
|
+
# Basically, a tag has its own partial template file, so every tag can be
|
5
|
+
# rendered into String using its partial template.
|
6
|
+
#
|
7
|
+
# The template file should be placed in your app/views/bootstrap_pager/ directory
|
8
|
+
# with underscored class name (besides the "Tag" class. Tag is an abstract
|
9
|
+
# class, so _tag partial is not needed).
|
10
|
+
# e.g.) PrevLink -> app/views/bootstrap_pager/_prev_link.html.erb
|
11
|
+
#
|
12
|
+
# When no matching template were found in your app, the engine's pre
|
13
|
+
# installed template will be used.
|
14
|
+
# e.g.) Paginator -> $GEM_HOME/bootstrap_pager-x.x.x/app/views/bootstrap_pager/_paginator.html.erb
|
15
|
+
class Tag
|
16
|
+
def initialize(template, options = {}) #:nodoc:
|
17
|
+
@template, @options = template, options.dup
|
18
|
+
@param_name = @options.delete(:param_name)
|
19
|
+
@engine_namespace = @options.delete(:engine_namespace)
|
20
|
+
@theme = @options[:theme] ? "#{@options.delete(:theme)}/" : ''
|
21
|
+
@params = @options[:params] ? template.params.merge(@options.delete :params) : template.params
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s(locals = {}) #:nodoc:
|
25
|
+
@template.render :partial => "bootstrap_pager/#{@theme}#{self.class.name.demodulize.underscore}", :locals => @options.merge(locals), :formats => [:html]
|
26
|
+
end
|
27
|
+
|
28
|
+
def page_url_for(page)
|
29
|
+
(@engine_namespace || @template).url_for @params.merge(@param_name => (page <= 1 ? nil : page))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Tag that contains a link
|
34
|
+
module Link
|
35
|
+
# target page number
|
36
|
+
def page
|
37
|
+
raise 'Override page with the actual page value to be a Page.'
|
38
|
+
end
|
39
|
+
# the link's href
|
40
|
+
def url
|
41
|
+
page_url_for page
|
42
|
+
end
|
43
|
+
def to_s(locals = {}) #:nodoc:
|
44
|
+
super locals.merge(:url => url)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# A page
|
49
|
+
class Page < Tag
|
50
|
+
include Link
|
51
|
+
# target page number
|
52
|
+
def page
|
53
|
+
@options[:page]
|
54
|
+
end
|
55
|
+
def to_s(locals = {}) #:nodoc:
|
56
|
+
super locals.merge(:page => page)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# Link with page number that appears at the leftmost
|
61
|
+
class FirstPage < Tag
|
62
|
+
include Link
|
63
|
+
def page #:nodoc:
|
64
|
+
1
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
# Link with page number that appears at the rightmost
|
69
|
+
class LastPage < Tag
|
70
|
+
include Link
|
71
|
+
def page #:nodoc:
|
72
|
+
@options[:total_pages]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
# The "previous" page of the current page
|
77
|
+
class PrevPage < Tag
|
78
|
+
include Link
|
79
|
+
def page #:nodoc:
|
80
|
+
@options[:current_page] - 1
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# The "next" page of the current page
|
85
|
+
class NextPage < Tag
|
86
|
+
include Link
|
87
|
+
def page #:nodoc:
|
88
|
+
@options[:current_page] + 1
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
# Non-link tag that stands for skipped pages...
|
93
|
+
class Gap < Tag
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module BootstrapPager
|
2
|
+
class Hooks
|
3
|
+
def self.init
|
4
|
+
ActiveSupport.on_load(:active_record) do
|
5
|
+
require 'bootstrap_pager/models/active_record_extension'
|
6
|
+
::ActiveRecord::Base.send :include, BootstrapPager::ActiveRecordExtension
|
7
|
+
end
|
8
|
+
|
9
|
+
begin; require 'data_mapper'; rescue LoadError; end
|
10
|
+
if defined? ::DataMapper
|
11
|
+
require 'dm-aggregates'
|
12
|
+
require 'bootstrap_pager/models/data_mapper_extension'
|
13
|
+
::DataMapper::Collection.send :include, BootstrapPager::DataMapperExtension::Collection
|
14
|
+
::DataMapper::Model.append_extensions BootstrapPager::DataMapperExtension::Model
|
15
|
+
# ::DataMapper::Model.send :extend, BootstrapPager::DataMapperExtension::Model
|
16
|
+
end
|
17
|
+
|
18
|
+
begin; require 'mongoid'; rescue LoadError; end
|
19
|
+
if defined? ::Mongoid
|
20
|
+
require 'bootstrap_pager/models/mongoid_extension'
|
21
|
+
::Mongoid::Criteria.send :include, BootstrapPager::MongoidExtension::Criteria
|
22
|
+
::Mongoid::Document.send :include, BootstrapPager::MongoidExtension::Document
|
23
|
+
end
|
24
|
+
|
25
|
+
ActiveSupport.on_load(:mongo_mapper) do
|
26
|
+
require 'bootstrap_pager/models/mongo_mapper_extension'
|
27
|
+
::MongoMapper::Document.send :include, BootstrapPager::MongoMapperExtension::Document
|
28
|
+
::Plucky::Query.send :include, BootstrapPager::PluckyCriteriaMethods
|
29
|
+
end
|
30
|
+
|
31
|
+
# Rails 3.0.x fails to load helpers in Engines (?)
|
32
|
+
if defined?(::ActionView) && ::ActionPack::VERSION::STRING < '3.1'
|
33
|
+
ActiveSupport.on_load(:action_view) do
|
34
|
+
require 'bootstrap_pager/helpers/action_view_extension'
|
35
|
+
::ActionView::Base.send :include, BootstrapPager::ActionViewExtension
|
36
|
+
end
|
37
|
+
end
|
38
|
+
require 'bootstrap_pager/models/array_extension'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'bootstrap_pager/models/active_record_model_extension'
|
2
|
+
|
3
|
+
module BootstrapPager
|
4
|
+
module ActiveRecordExtension
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
included do
|
7
|
+
# Future subclasses will pick up the model extension
|
8
|
+
class << self
|
9
|
+
def inherited_with_bootstrap_pager(kls) #:nodoc:
|
10
|
+
inherited_without_bootstrap_pager kls
|
11
|
+
kls.send(:include, BootstrapPager::ActiveRecordModelExtension) if kls.superclass == ActiveRecord::Base
|
12
|
+
end
|
13
|
+
alias_method_chain :inherited, :bootstrap_pager
|
14
|
+
end
|
15
|
+
|
16
|
+
# Existing subclasses pick up the model extension as well
|
17
|
+
self.descendants.each do |kls|
|
18
|
+
kls.send(:include, BootstrapPager::ActiveRecordModelExtension) if kls.superclass == ActiveRecord::Base
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'bootstrap_pager/models/active_record_relation_methods'
|
2
|
+
|
3
|
+
module BootstrapPager
|
4
|
+
module ActiveRecordModelExtension
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
self.send(:include, BootstrapPager::ConfigurationMethods)
|
9
|
+
|
10
|
+
# Fetch the values at the specified page number
|
11
|
+
# Model.page(5)
|
12
|
+
self.scope BootstrapPager.config.page_method_name, Proc.new {|num|
|
13
|
+
limit(default_per_page).offset(default_per_page * ([num.to_i, 1].max - 1))
|
14
|
+
} do
|
15
|
+
include BootstrapPager::ActiveRecordRelationMethods
|
16
|
+
include BootstrapPager::PageScopeMethods
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module BootstrapPager
|
2
|
+
module ActiveRecordRelationMethods
|
3
|
+
# a workaround for AR 3.0.x that returns 0 for #count when page > 1
|
4
|
+
# if +limit_value+ is specified, load all the records and count them
|
5
|
+
if ActiveRecord::VERSION::STRING < '3.1'
|
6
|
+
def count(column_name = nil, options = {}) #:nodoc:
|
7
|
+
limit_value ? length : super(column_name, options)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def total_count(column_name = nil, options = {}) #:nodoc:
|
12
|
+
# #count overrides the #select which could include generated columns referenced in #order, so skip #order here, where it's irrelevant to the result anyway
|
13
|
+
@total_count ||= begin
|
14
|
+
c = except(:offset, :limit, :order)
|
15
|
+
|
16
|
+
# Remove includes only if they are irrelevant
|
17
|
+
c = c.except(:includes) unless references_eager_loaded_tables?
|
18
|
+
|
19
|
+
# .group returns an OrderedHash that responds to #count
|
20
|
+
c = c.count(column_name, options)
|
21
|
+
if c.is_a?(Hash) || c.is_a?(ActiveSupport::OrderedHash)
|
22
|
+
c.count
|
23
|
+
else
|
24
|
+
c.respond_to?(:count) ? c.count(column_name, options) : c
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
require 'active_support/core_ext/module'
|
2
|
+
module BootstrapPager
|
3
|
+
# Kind of Array that can paginate
|
4
|
+
class PaginatableArray < Array
|
5
|
+
include BootstrapPager::ConfigurationMethods::ClassMethods
|
6
|
+
|
7
|
+
attr_internal_accessor :limit_value, :offset_value
|
8
|
+
|
9
|
+
# ==== Options
|
10
|
+
# * <tt>:limit</tt> - limit
|
11
|
+
# * <tt>:offset</tt> - offset
|
12
|
+
# * <tt>:total_count</tt> - total_count
|
13
|
+
def initialize(original_array = [], options = {})
|
14
|
+
@_original_array, @_limit_value, @_offset_value, @_total_count, @_padding = original_array, (options[:limit] || default_per_page).to_i, options[:offset].to_i, options[:total_count], options[:padding].to_i
|
15
|
+
|
16
|
+
if options[:limit] && options[:offset]
|
17
|
+
extend BootstrapPager::PageScopeMethods
|
18
|
+
end
|
19
|
+
|
20
|
+
if options[:total_count]
|
21
|
+
super original_array
|
22
|
+
else
|
23
|
+
super(original_array[@_offset_value, @_limit_value] || [])
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
# items at the specified "page"
|
28
|
+
class_eval <<-RUBY, __FILE__, __LINE__ + 1
|
29
|
+
def #{BootstrapPager.config.page_method_name}(num = 1)
|
30
|
+
offset(limit_value * ([num.to_i, 1].max - 1))
|
31
|
+
end
|
32
|
+
RUBY
|
33
|
+
|
34
|
+
# returns another chunk of the original array
|
35
|
+
def limit(num)
|
36
|
+
self.class.new @_original_array, :limit => num, :offset => @_offset_value, :total_count => @_total_count, :padding => @_padding
|
37
|
+
end
|
38
|
+
|
39
|
+
# total item numbers of the original array
|
40
|
+
def total_count
|
41
|
+
@_total_count || @_original_array.count
|
42
|
+
end
|
43
|
+
|
44
|
+
# returns another chunk of the original array
|
45
|
+
def offset(num)
|
46
|
+
self.class.new @_original_array, :limit => @_limit_value, :offset => num, :total_count => @_total_count, :padding => @_padding
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
# Wrap an Array object to make it paginatable
|
51
|
+
# ==== Options
|
52
|
+
# * <tt>:limit</tt> - limit
|
53
|
+
# * <tt>:offset</tt> - offset
|
54
|
+
# * <tt>:total_count</tt> - total_count
|
55
|
+
def self.paginate_array(array, options = {})
|
56
|
+
PaginatableArray.new array, options
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
module BootstrapPager
|
2
|
+
module ConfigurationMethods
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
module ClassMethods
|
5
|
+
# Overrides the default +per_page+ value per model
|
6
|
+
# class Article < ActiveRecord::Base
|
7
|
+
# paginates_per 10
|
8
|
+
# end
|
9
|
+
def paginates_per(val)
|
10
|
+
@_default_per_page = val
|
11
|
+
end
|
12
|
+
|
13
|
+
# This model's default +per_page+ value
|
14
|
+
# returns +default_per_page+ value unless explicitly overridden via <tt>paginates_per</tt>
|
15
|
+
def default_per_page
|
16
|
+
(defined?(@_default_per_page) && @_default_per_page) || BootstrapPager.config.default_per_page
|
17
|
+
end
|
18
|
+
|
19
|
+
# Overrides the max +per_page+ value per model
|
20
|
+
# class Article < ActiveRecord::Base
|
21
|
+
# max_paginates_per 100
|
22
|
+
# end
|
23
|
+
def max_paginates_per(val)
|
24
|
+
@_max_per_page = val
|
25
|
+
end
|
26
|
+
|
27
|
+
# This model's max +per_page+ value
|
28
|
+
# returns +max_per_page+ value unless explicitly overridden via <tt>max_paginates_per</tt>
|
29
|
+
def max_per_page
|
30
|
+
(defined?(@_max_per_page) && @_max_per_page) || BootstrapPager.config.max_per_page
|
31
|
+
end
|
32
|
+
|
33
|
+
# Overrides the max_pages value per model
|
34
|
+
# class Article < ActiveRecord::Base
|
35
|
+
# max_pages_per 100
|
36
|
+
# end
|
37
|
+
def max_pages_per(val)
|
38
|
+
@_max_pages = val
|
39
|
+
end
|
40
|
+
|
41
|
+
# This model's max_pages value
|
42
|
+
# returns max_pages value unless explicitly overridden via <tt>max_pages_per</tt>
|
43
|
+
def max_pages
|
44
|
+
(defined?(@_max_pages) && @_max_pages) || BootstrapPager.config.max_pages
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module BootstrapPager
|
2
|
+
module DataMapperCollectionMethods
|
3
|
+
def limit_value #:nodoc:
|
4
|
+
query.options[:limit] || 0
|
5
|
+
end
|
6
|
+
|
7
|
+
def offset_value #:nodoc:
|
8
|
+
query.options[:offset] || 0
|
9
|
+
end
|
10
|
+
|
11
|
+
def total_count #:nodoc:
|
12
|
+
model.count(query.options.except(:limit, :offset, :order))
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|