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