bootstrap_pager 0.9.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.
- 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
|