bootstrap_pager 0.9.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (109) hide show
  1. checksums.yaml +15 -0
  2. data/.gitignore +18 -0
  3. data/.idea/encodings.xml +5 -0
  4. data/.idea/misc.xml +5 -0
  5. data/.idea/modules.xml +9 -0
  6. data/.idea/pager.iml +50 -0
  7. data/.idea/scopes/scope_settings.xml +5 -0
  8. data/.idea/vcs.xml +7 -0
  9. data/Gemfile +4 -0
  10. data/LICENSE +20 -0
  11. data/MIT-LICENSE +20 -0
  12. data/README.rdoc +307 -0
  13. data/Rakefile +42 -0
  14. data/app/assets/javascripts/infinitescroll.js +8 -0
  15. data/app/helpers/pager_helper.rb +5 -0
  16. data/app/views/pager/_first_page.html.erb +11 -0
  17. data/app/views/pager/_first_page.html.haml +9 -0
  18. data/app/views/pager/_first_page.html.slim +10 -0
  19. data/app/views/pager/_gap.html.erb +8 -0
  20. data/app/views/pager/_gap.html.haml +8 -0
  21. data/app/views/pager/_gap.html.slim +10 -0
  22. data/app/views/pager/_last_page.html.erb +11 -0
  23. data/app/views/pager/_last_page.html.haml +9 -0
  24. data/app/views/pager/_last_page.html.slim +10 -0
  25. data/app/views/pager/_next_page.html.erb +11 -0
  26. data/app/views/pager/_next_page.html.haml +9 -0
  27. data/app/views/pager/_next_page.html.slim +10 -0
  28. data/app/views/pager/_page.html.erb +14 -0
  29. data/app/views/pager/_page.html.haml +11 -0
  30. data/app/views/pager/_page.html.slim +13 -0
  31. data/app/views/pager/_paginator.html.erb +25 -0
  32. data/app/views/pager/_paginator.html.haml +19 -0
  33. data/app/views/pager/_paginator.html.slim +20 -0
  34. data/app/views/pager/_prev_page.html.erb +11 -0
  35. data/app/views/pager/_prev_page.html.haml +9 -0
  36. data/app/views/pager/_prev_page.html.slim +10 -0
  37. data/bootstrap_pager.gemspec +36 -0
  38. data/config/locales/pager.yml +19 -0
  39. data/gemfiles/active_record_30.gemfile +9 -0
  40. data/gemfiles/active_record_31.gemfile +7 -0
  41. data/gemfiles/active_record_32.gemfile +10 -0
  42. data/gemfiles/active_record_40.gemfile +8 -0
  43. data/gemfiles/active_record_edge.gemfile +11 -0
  44. data/gemfiles/data_mapper_12.gemfile +15 -0
  45. data/gemfiles/mongo_mapper.gemfile +10 -0
  46. data/gemfiles/mongoid_24.gemfile +7 -0
  47. data/gemfiles/mongoid_30.gemfile +12 -0
  48. data/gemfiles/sinatra_13.gemfile +13 -0
  49. data/gemfiles/sinatra_14.gemfile +13 -0
  50. data/lib/bootstrap_pager/config.rb +51 -0
  51. data/lib/bootstrap_pager/grape.rb +4 -0
  52. data/lib/bootstrap_pager/helpers/action_view_extension.rb +152 -0
  53. data/lib/bootstrap_pager/helpers/paginator.rb +186 -0
  54. data/lib/bootstrap_pager/helpers/sinatra_helpers.rb +144 -0
  55. data/lib/bootstrap_pager/helpers/tags.rb +96 -0
  56. data/lib/bootstrap_pager/hooks.rb +41 -0
  57. data/lib/bootstrap_pager/models/active_record_extension.rb +22 -0
  58. data/lib/bootstrap_pager/models/active_record_model_extension.rb +20 -0
  59. data/lib/bootstrap_pager/models/active_record_relation_methods.rb +29 -0
  60. data/lib/bootstrap_pager/models/array_extension.rb +58 -0
  61. data/lib/bootstrap_pager/models/configuration_methods.rb +48 -0
  62. data/lib/bootstrap_pager/models/data_mapper_collection_methods.rb +15 -0
  63. data/lib/bootstrap_pager/models/data_mapper_extension.rb +48 -0
  64. data/lib/bootstrap_pager/models/mongo_mapper_extension.rb +18 -0
  65. data/lib/bootstrap_pager/models/mongoid_criteria_methods.rb +23 -0
  66. data/lib/bootstrap_pager/models/mongoid_extension.rb +33 -0
  67. data/lib/bootstrap_pager/models/page_scope_methods.rb +70 -0
  68. data/lib/bootstrap_pager/models/plucky_criteria_methods.rb +18 -0
  69. data/lib/bootstrap_pager/railtie.rb +7 -0
  70. data/lib/bootstrap_pager/sinatra.rb +5 -0
  71. data/lib/bootstrap_pager/version.rb +3 -0
  72. data/lib/bootstrap_pager.rb +38 -0
  73. data/lib/generators/pager/config_generator.rb +16 -0
  74. data/lib/generators/pager/templates/pager_config.rb +10 -0
  75. data/lib/generators/pager/views_generator.rb +118 -0
  76. data/spec/config/config_spec.rb +91 -0
  77. data/spec/fake_app/active_record/config.rb +3 -0
  78. data/spec/fake_app/active_record/models.rb +57 -0
  79. data/spec/fake_app/data_mapper/config.rb +7 -0
  80. data/spec/fake_app/data_mapper/models.rb +27 -0
  81. data/spec/fake_app/log/development.log +832 -0
  82. data/spec/fake_app/mongo_mapper/config.rb +2 -0
  83. data/spec/fake_app/mongo_mapper/models.rb +9 -0
  84. data/spec/fake_app/mongoid/config.rb +16 -0
  85. data/spec/fake_app/mongoid/models.rb +22 -0
  86. data/spec/fake_app/rails_app.rb +67 -0
  87. data/spec/fake_app/sinatra_app.rb +22 -0
  88. data/spec/fake_gem.rb +4 -0
  89. data/spec/helpers/action_view_extension_spec.rb +292 -0
  90. data/spec/helpers/helpers_spec.rb +135 -0
  91. data/spec/helpers/sinatra_helpers_spec.rb +170 -0
  92. data/spec/helpers/tags_spec.rb +140 -0
  93. data/spec/models/active_record/active_record_relation_methods_spec.rb +47 -0
  94. data/spec/models/active_record/default_per_page_spec.rb +32 -0
  95. data/spec/models/active_record/max_pages_spec.rb +23 -0
  96. data/spec/models/active_record/max_per_page_spec.rb +32 -0
  97. data/spec/models/active_record/scopes_spec.rb +242 -0
  98. data/spec/models/array_spec.rb +150 -0
  99. data/spec/models/data_mapper/data_mapper_spec.rb +207 -0
  100. data/spec/models/mongo_mapper/mongo_mapper_spec.rb +84 -0
  101. data/spec/models/mongoid/mongoid_spec.rb +126 -0
  102. data/spec/requests/users_spec.rb +53 -0
  103. data/spec/spec_helper.rb +33 -0
  104. data/spec/spec_helper_for_sinatra.rb +34 -0
  105. data/spec/support/database_cleaner.rb +16 -0
  106. data/spec/support/matchers.rb +52 -0
  107. data/vendor/assets/javascripts/jquery.infinitescroll.js +814 -0
  108. data/vendor/assets/javascripts/jquery.infinitescroll.min.js +1 -0
  109. 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