kaminari-jets-core 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +1 -0
  3. data/MIT-LICENSE +21 -0
  4. data/README.md +31 -0
  5. data/app/views/kaminari/_first_page.html.erb +11 -0
  6. data/app/views/kaminari/_first_page.html.haml +9 -0
  7. data/app/views/kaminari/_first_page.html.slim +10 -0
  8. data/app/views/kaminari/_gap.html.erb +8 -0
  9. data/app/views/kaminari/_gap.html.haml +8 -0
  10. data/app/views/kaminari/_gap.html.slim +9 -0
  11. data/app/views/kaminari/_last_page.html.erb +11 -0
  12. data/app/views/kaminari/_last_page.html.haml +9 -0
  13. data/app/views/kaminari/_last_page.html.slim +10 -0
  14. data/app/views/kaminari/_next_page.html.erb +11 -0
  15. data/app/views/kaminari/_next_page.html.haml +9 -0
  16. data/app/views/kaminari/_next_page.html.slim +10 -0
  17. data/app/views/kaminari/_page.html.erb +12 -0
  18. data/app/views/kaminari/_page.html.haml +10 -0
  19. data/app/views/kaminari/_page.html.slim +11 -0
  20. data/app/views/kaminari/_paginator.html.erb +25 -0
  21. data/app/views/kaminari/_paginator.html.haml +18 -0
  22. data/app/views/kaminari/_paginator.html.slim +19 -0
  23. data/app/views/kaminari/_prev_page.html.erb +11 -0
  24. data/app/views/kaminari/_prev_page.html.haml +9 -0
  25. data/app/views/kaminari/_prev_page.html.slim +10 -0
  26. data/config/locales/kaminari.yml +23 -0
  27. data/kaminari-core.gemspec +26 -0
  28. data/lib/generators/kaminari/config_generator.rb +19 -0
  29. data/lib/generators/kaminari/templates/kaminari_config.rb +14 -0
  30. data/lib/generators/kaminari/views_generator.rb +137 -0
  31. data/lib/kaminari/config.rb +40 -0
  32. data/lib/kaminari/core/version.rb +8 -0
  33. data/lib/kaminari/core.rb +29 -0
  34. data/lib/kaminari/engine.rb +6 -0
  35. data/lib/kaminari/exceptions.rb +5 -0
  36. data/lib/kaminari/helpers/helper_methods.rb +247 -0
  37. data/lib/kaminari/helpers/paginator.rb +191 -0
  38. data/lib/kaminari/helpers/tags.rb +164 -0
  39. data/lib/kaminari/jets/engine.rb +6 -0
  40. data/lib/kaminari/jets/turbine.rb +9 -0
  41. data/lib/kaminari/models/array_extension.rb +73 -0
  42. data/lib/kaminari/models/configuration_methods.rb +60 -0
  43. data/lib/kaminari/models/page_scope_methods.rb +91 -0
  44. data/lib/kaminari/railtie.rb +9 -0
  45. metadata +115 -0
@@ -0,0 +1,247 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kaminari
4
+ module Helpers
5
+
6
+ # The Kaminari::Helpers::UrlHelper module provides useful methods for
7
+ # generating a path or url to a particular page. A class must implement the
8
+ # following methods:
9
+ #
10
+ # * <tt>url_for</tt>: A method that generates an actual path
11
+ # * <tt>params</tt>: A method that returns query string parameters
12
+ # * <tt>request</tt>: A method that returns a Rack::Request object
13
+ #
14
+ # A normal Rails controller implements all the methods, which make it
15
+ # trivial to use this module:
16
+ #
17
+ # ==== Examples
18
+ #
19
+ # class UsersController < ApplicationController
20
+ # include Kaminari::Helpers::UrlHelper
21
+ #
22
+ # def index
23
+ # @users = User.page(1)
24
+ #
25
+ # path_to_next_page(@items)
26
+ # # => /items?page=2
27
+ # end
28
+ # end
29
+ #
30
+ module UrlHelper
31
+
32
+ # A helper that calculates the url to the next page.
33
+ #
34
+ # ==== Examples
35
+ # Basic usage:
36
+ #
37
+ # <%= next_page_url @items %>
38
+ # #-> http://www.example.org/items?page=2
39
+ #
40
+ # It will return `nil` if there is no next page.
41
+ def next_page_url(scope, options = {})
42
+ "#{request.base_url}#{next_page_path(scope, options)}" if scope.next_page
43
+ end
44
+ alias url_to_next_page next_page_url
45
+
46
+ def path_to_next_url(scope, options = {})
47
+ ActiveSupport::Deprecation.warn 'path_to_next_url is deprecated. Use next_page_url or url_to_next_page instead.'
48
+ next_page_url(scope, options)
49
+ end
50
+
51
+ # A helper that calculates the url to the previous page.
52
+ #
53
+ # ==== Examples
54
+ # Basic usage:
55
+ #
56
+ # <%= prev_page_url @items %>
57
+ # #-> http://www.example.org/items
58
+ #
59
+ # It will return `nil` if there is no previous page.
60
+ def prev_page_url(scope, options = {})
61
+ "#{request.base_url}#{prev_page_path(scope, options)}" if scope.prev_page
62
+ end
63
+ alias previous_page_url prev_page_url
64
+ alias url_to_prev_page prev_page_url
65
+ alias url_to_previous_page prev_page_url
66
+
67
+ # A helper that calculates the path to the next page.
68
+ #
69
+ # ==== Examples
70
+ # Basic usage:
71
+ #
72
+ # <%= path_to_next_page @items %>
73
+ # #-> /items?page=2
74
+ #
75
+ # It will return `nil` if there is no next page.
76
+ def next_page_path(scope, options = {})
77
+ Kaminari::Helpers::NextPage.new(self, **options.reverse_merge(current_page: scope.current_page)).url if scope.next_page
78
+ end
79
+ alias path_to_next_page next_page_path
80
+
81
+ # A helper that calculates the path to the previous page.
82
+ #
83
+ # ==== Examples
84
+ # Basic usage:
85
+ #
86
+ # <%= path_to_prev_page @items %>
87
+ # #-> /items
88
+ #
89
+ # It will return `nil` if there is no previous page.
90
+ def prev_page_path(scope, options = {})
91
+ Kaminari::Helpers::PrevPage.new(self, **options.reverse_merge(current_page: scope.current_page)).url if scope.prev_page
92
+ end
93
+ alias previous_page_path prev_page_path
94
+ alias path_to_previous_page prev_page_path
95
+ alias path_to_prev_page prev_page_path
96
+ end
97
+
98
+ module HelperMethods
99
+ include UrlHelper
100
+
101
+ # A helper that renders the pagination links.
102
+ #
103
+ # <%= paginate @articles %>
104
+ #
105
+ # ==== Options
106
+ # * <tt>:window</tt> - The "inner window" size (4 by default).
107
+ # * <tt>:outer_window</tt> - The "outer window" size (0 by default).
108
+ # * <tt>:left</tt> - The "left outer window" size (0 by default).
109
+ # * <tt>:right</tt> - The "right outer window" size (0 by default).
110
+ # * <tt>:params</tt> - url_for parameters for the links (:controller, :action, etc.)
111
+ # * <tt>:param_name</tt> - parameter name for page number in the links (:page by default)
112
+ # * <tt>:remote</tt> - Ajax? (false by default)
113
+ # * <tt>:paginator_class</tt> - Specify a custom Paginator (Kaminari::Helpers::Paginator by default)
114
+ # * <tt>:template</tt> - Specify a custom template renderer for rendering the Paginator (receiver by default)
115
+ # * <tt>:ANY_OTHER_VALUES</tt> - Any other hash key & values would be directly passed into each tag as :locals value.
116
+ def paginate(scope, paginator_class: Kaminari::Helpers::Paginator, template: nil, **options)
117
+ options[:total_pages] ||= scope.total_pages
118
+ options.reverse_merge! current_page: scope.current_page, per_page: scope.limit_value, remote: false
119
+
120
+ paginator = paginator_class.new (template || self), **options
121
+ paginator.to_s
122
+ end
123
+
124
+ # A simple "Twitter like" pagination link that creates a link to the previous page.
125
+ #
126
+ # ==== Examples
127
+ # Basic usage:
128
+ #
129
+ # <%= link_to_previous_page @items, 'Previous Page' %>
130
+ #
131
+ # Ajax:
132
+ #
133
+ # <%= link_to_previous_page @items, 'Previous Page', remote: true %>
134
+ #
135
+ # By default, it renders nothing if there are no more results on the previous page.
136
+ # You can customize this output by passing a block.
137
+ #
138
+ # <%= link_to_previous_page @users, 'Previous Page' do %>
139
+ # <span>At the Beginning</span>
140
+ # <% end %>
141
+ def link_to_previous_page(scope, name, **options)
142
+ prev_page = path_to_prev_page(scope, options)
143
+
144
+ options.except! :params, :param_name
145
+ options[:rel] ||= 'prev'
146
+
147
+ if prev_page
148
+ link_to name, prev_page, options
149
+ elsif block_given?
150
+ yield
151
+ end
152
+ end
153
+ alias link_to_prev_page link_to_previous_page
154
+
155
+ # A simple "Twitter like" pagination link that creates a link to the next page.
156
+ #
157
+ # ==== Examples
158
+ # Basic usage:
159
+ #
160
+ # <%= link_to_next_page @items, 'Next Page' %>
161
+ #
162
+ # Ajax:
163
+ #
164
+ # <%= link_to_next_page @items, 'Next Page', remote: true %>
165
+ #
166
+ # By default, it renders nothing if there are no more results on the next page.
167
+ # You can customize this output by passing a block.
168
+ #
169
+ # <%= link_to_next_page @users, 'Next Page' do %>
170
+ # <span>No More Pages</span>
171
+ # <% end %>
172
+ def link_to_next_page(scope, name, **options)
173
+ next_page = path_to_next_page(scope, options)
174
+
175
+ options.except! :params, :param_name
176
+ options[:rel] ||= 'next'
177
+
178
+ if next_page
179
+ link_to name, next_page, options
180
+ elsif block_given?
181
+ yield
182
+ end
183
+ end
184
+
185
+ # Renders a helpful message with numbers of displayed vs. total entries.
186
+ # Ported from mislav/will_paginate
187
+ #
188
+ # ==== Examples
189
+ # Basic usage:
190
+ #
191
+ # <%= page_entries_info @posts %>
192
+ # #-> Displaying posts 6 - 10 of 26 in total
193
+ #
194
+ # By default, the message will use the humanized class name of objects
195
+ # in collection: for instance, "project types" for ProjectType models.
196
+ # The namespace will be cutted out and only the last name will be used.
197
+ # Override this with the <tt>:entry_name</tt> parameter:
198
+ #
199
+ # <%= page_entries_info @posts, entry_name: 'item' %>
200
+ # #-> Displaying items 6 - 10 of 26 in total
201
+ def page_entries_info(collection, entry_name: nil)
202
+ entry_name = if entry_name
203
+ entry_name.pluralize(collection.size, I18n.locale)
204
+ else
205
+ collection.entry_name(count: collection.size).downcase
206
+ end
207
+
208
+ if collection.total_pages < 2
209
+ t('helpers.page_entries_info.one_page.display_entries', entry_name: entry_name, count: collection.total_count)
210
+ else
211
+ from = collection.offset_value + 1
212
+ to = collection.offset_value + (collection.respond_to?(:records) ? collection.records : collection.to_a).size
213
+
214
+ t('helpers.page_entries_info.more_pages.display_entries', entry_name: entry_name, first: from, last: to, total: collection.total_count)
215
+ end.html_safe
216
+ end
217
+
218
+ # Renders rel="next" and rel="prev" links to be used in the head.
219
+ #
220
+ # ==== Examples
221
+ # Basic usage:
222
+ #
223
+ # In head:
224
+ # <head>
225
+ # <title>My Website</title>
226
+ # <%= yield :head %>
227
+ # </head>
228
+ #
229
+ # Somewhere in body:
230
+ # <% content_for :head do %>
231
+ # <%= rel_next_prev_link_tags @items %>
232
+ # <% end %>
233
+ #
234
+ # #-> <link rel="next" href="/items/page/3"><link rel="prev" href="/items/page/1">
235
+ #
236
+ def rel_next_prev_link_tags(scope, options = {})
237
+ next_page = path_to_next_page(scope, options)
238
+ prev_page = path_to_prev_page(scope, options)
239
+
240
+ output = String.new
241
+ output << %Q|<link rel="next" href="#{next_page}">| if next_page
242
+ output << %Q|<link rel="prev" href="#{prev_page}">| if prev_page
243
+ output.html_safe
244
+ end
245
+ end
246
+ end
247
+ end
@@ -0,0 +1,191 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/inflector'
4
+ require 'kaminari/helpers/tags'
5
+
6
+ module Kaminari
7
+ module Helpers
8
+ # The main container tag
9
+ class Paginator < Tag
10
+ def initialize(template, window: nil, outer_window: Kaminari.config.outer_window, left: Kaminari.config.left, right: Kaminari.config.right, inner_window: Kaminari.config.window, **options) #:nodoc:
11
+ @window_options = {window: window || inner_window, left: left.zero? ? outer_window : left, right: right.zero? ? outer_window : right}
12
+
13
+ @template, @options, @theme, @views_prefix, @last = template, options, options[:theme], options[:views_prefix], nil
14
+ @window_options.merge! @options
15
+ @window_options[:current_page] = @options[:current_page] = PageProxy.new(@window_options, @options[:current_page], nil)
16
+
17
+ #XXX Using parent template's buffer class for rendering each partial here. This might cause problems if the handler mismatches
18
+ @output_buffer = if defined?(::ActionView::OutputBuffer)
19
+ ::ActionView::OutputBuffer.new
20
+ elsif template.instance_variable_get(:@output_buffer)
21
+ template.instance_variable_get(:@output_buffer).class.new
22
+ else
23
+ ActiveSupport::SafeBuffer.new
24
+ end
25
+ end
26
+
27
+ # render given block as a view template
28
+ def render(&block)
29
+ instance_eval(&block) if @options[:total_pages] > 1
30
+
31
+ # This allows for showing fall-back HTML when there's only one page:
32
+ #
33
+ # <%= paginate(@search_results) || "Showing all search results" %>
34
+ @output_buffer.presence
35
+ end
36
+
37
+ # enumerate each page providing PageProxy object as the block parameter
38
+ # Because of performance reason, this doesn't actually enumerate all pages but pages that are seemingly relevant to the paginator.
39
+ # "Relevant" pages are:
40
+ # * pages inside the left outer window plus one for showing the gap tag
41
+ # * pages inside the inner window plus one on the left plus one on the right for showing the gap tags
42
+ # * pages inside the right outer window plus one for showing the gap tag
43
+ def each_relevant_page
44
+ return to_enum(:each_relevant_page) unless block_given?
45
+
46
+ relevant_pages(@window_options).each do |page|
47
+ yield PageProxy.new(@window_options, page, @last)
48
+ end
49
+ end
50
+ alias each_page each_relevant_page
51
+
52
+ def relevant_pages(options)
53
+ left_window_plus_one = [*1..options[:left] + 1]
54
+ right_window_plus_one = [*options[:total_pages] - options[:right]..options[:total_pages]]
55
+ inside_window_plus_each_sides = [*options[:current_page] - options[:window] - 1..options[:current_page] + options[:window] + 1]
56
+
57
+ (left_window_plus_one | inside_window_plus_each_sides | right_window_plus_one).sort.reject {|x| (x < 1) || (x > options[:total_pages])}
58
+ end
59
+ private :relevant_pages
60
+
61
+ def page_tag(page)
62
+ @last = Page.new @template, **@options.merge(page: page)
63
+ end
64
+
65
+ %w[first_page prev_page next_page last_page gap].each do |tag|
66
+ eval <<-DEF, nil, __FILE__, __LINE__ + 1
67
+ def #{tag}_tag
68
+ @last = #{tag.classify}.new @template, **@options
69
+ end
70
+ DEF
71
+ end
72
+
73
+ def to_s #:nodoc:
74
+ Thread.current[:kaminari_rendering] = true
75
+ super @window_options.merge paginator: self
76
+ ensure
77
+ Thread.current[:kaminari_rendering] = false
78
+ end
79
+
80
+ # delegates view helper methods to @template
81
+ def method_missing(name, *args, &block)
82
+ @template.respond_to?(name) ? @template.send(name, *args, &block) : super
83
+ end
84
+ private :method_missing
85
+
86
+ # Wraps a "page number" and provides some utility methods
87
+ class PageProxy
88
+ include Comparable
89
+
90
+ def initialize(options, page, last) #:nodoc:
91
+ @options, @page, @last = options, page, last
92
+ end
93
+
94
+ # the page number
95
+ def number
96
+ @page
97
+ end
98
+
99
+ # current page or not
100
+ def current?
101
+ @page == @options[:current_page]
102
+ end
103
+
104
+ # the first page or not
105
+ def first?
106
+ @page == 1
107
+ end
108
+
109
+ # the last page or not
110
+ def last?
111
+ @page == @options[:total_pages]
112
+ end
113
+
114
+ # the previous page or not
115
+ def prev?
116
+ @page == @options[:current_page] - 1
117
+ end
118
+
119
+ # the next page or not
120
+ def next?
121
+ @page == @options[:current_page] + 1
122
+ end
123
+
124
+ # relationship with the current page
125
+ def rel
126
+ if next?
127
+ 'next'
128
+ elsif prev?
129
+ 'prev'
130
+ end
131
+ end
132
+
133
+ # within the left outer window or not
134
+ def left_outer?
135
+ @page <= @options[:left]
136
+ end
137
+
138
+ # within the right outer window or not
139
+ def right_outer?
140
+ @options[:total_pages] - @page < @options[:right]
141
+ end
142
+
143
+ # inside the inner window or not
144
+ def inside_window?
145
+ (@options[:current_page] - @page).abs <= @options[:window]
146
+ end
147
+
148
+ # Current page is an isolated gap or not
149
+ def single_gap?
150
+ ((@page == @options[:current_page] - @options[:window] - 1) && (@page == @options[:left] + 1)) ||
151
+ ((@page == @options[:current_page] + @options[:window] + 1) && (@page == @options[:total_pages] - @options[:right]))
152
+ end
153
+
154
+ # The page number exceeds the range of pages or not
155
+ def out_of_range?
156
+ @page > @options[:total_pages]
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
+ #Should we display the link tag?
165
+ def display_tag?
166
+ left_outer? || right_outer? || inside_window? || single_gap?
167
+ end
168
+
169
+ def to_i #:nodoc:
170
+ number
171
+ end
172
+
173
+ def to_s #:nodoc:
174
+ number.to_s
175
+ end
176
+
177
+ def +(other) #:nodoc:
178
+ to_i + other.to_i
179
+ end
180
+
181
+ def -(other) #:nodoc:
182
+ to_i - other.to_i
183
+ end
184
+
185
+ def <=>(other) #:nodoc:
186
+ to_i <=> other.to_i
187
+ end
188
+ end
189
+ end
190
+ end
191
+ end
@@ -0,0 +1,164 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kaminari
4
+ module Helpers
5
+ PARAM_KEY_EXCEPT_LIST = [:authenticity_token, :commit, :utf8, :_method, :script_name, :original_script_name].freeze
6
+
7
+ # A tag stands for an HTML tag inside the paginator.
8
+ # Basically, a tag has its own partial template file, so every tag can be
9
+ # rendered into String using its partial template.
10
+ #
11
+ # The template file should be placed in your app/views/kaminari/ directory
12
+ # with underscored class name (besides the "Tag" class. Tag is an abstract
13
+ # class, so _tag partial is not needed).
14
+ # e.g.) PrevLink -> app/views/kaminari/_prev_link.html.erb
15
+ #
16
+ # When no matching templates were found in your app, the engine's pre
17
+ # installed template will be used.
18
+ # e.g.) Paginator -> $GEM_HOME/kaminari-x.x.x/app/views/kaminari/_paginator.html.erb
19
+ class Tag
20
+ def initialize(template, params: {}, param_name: nil, theme: nil, views_prefix: nil, **options) #:nodoc:
21
+ @template, @theme, @views_prefix, @options = template, theme, views_prefix, options
22
+ @param_name = param_name || Kaminari.config.param_name
23
+ @params = template.params
24
+ # @params in Rails 5 no longer inherits from Hash
25
+ @params = @params.to_unsafe_h if @params.respond_to?(:to_unsafe_h)
26
+ @params = @params.with_indifferent_access
27
+ @params.except!(*PARAM_KEY_EXCEPT_LIST)
28
+ @params.merge! params
29
+ end
30
+
31
+ def to_s(locals = {}) #:nodoc:
32
+ formats = (@template.respond_to?(:formats) ? @template.formats : Array(@template.params[:format])) + [:html]
33
+ @template.render partial: partial_path, locals: @options.merge(locals), formats: formats
34
+ end
35
+
36
+ def page_url_for(page)
37
+ params = params_for(page)
38
+ params[:only_path] = true
39
+ @template.url_for params
40
+ end
41
+
42
+ private
43
+
44
+ def params_for(page)
45
+ page_params = Rack::Utils.parse_nested_query("#{@param_name}=#{page}")
46
+ page_params = @params.deep_merge(page_params)
47
+
48
+ if !Kaminari.config.params_on_first_page && (page <= 1)
49
+ # This converts a hash:
50
+ # from: {other: "params", page: 1}
51
+ # to: {other: "params", page: nil}
52
+ # (when @param_name == "page")
53
+ #
54
+ # from: {other: "params", user: {name: "yuki", page: 1}}
55
+ # to: {other: "params", user: {name: "yuki", page: nil}}
56
+ # (when @param_name == "user[page]")
57
+ @param_name.to_s.scan(/[\w\.]+/)[0..-2].inject(page_params){|h, k| h[k] }[$&] = nil
58
+ end
59
+
60
+ page_params
61
+ end
62
+
63
+ def partial_path
64
+ [
65
+ @views_prefix,
66
+ "kaminari",
67
+ @theme,
68
+ self.class.name.demodulize.underscore
69
+ ].compact.join("/").gsub('//', '/')
70
+ end
71
+ end
72
+
73
+ # Tag that contains a link
74
+ module Link
75
+ # target page number
76
+ def page
77
+ raise 'Override page with the actual page value to be a Page.'
78
+ end
79
+ # the link's href
80
+ def url
81
+ page_url_for page
82
+ end
83
+ def to_s(locals = {}) #:nodoc:
84
+ locals[:url] = url
85
+ super locals
86
+ end
87
+ end
88
+
89
+ # A page
90
+ class Page < Tag
91
+ include Link
92
+ # target page number
93
+ def page
94
+ @options[:page]
95
+ end
96
+ def to_s(locals = {}) #:nodoc:
97
+ locals[:page] = page
98
+ super locals
99
+ end
100
+ end
101
+
102
+ # Link with page number that appears at the leftmost
103
+ class FirstPage < Tag
104
+ include Link
105
+ def page #:nodoc:
106
+ 1
107
+ end
108
+ end
109
+
110
+ # Link with page number that appears at the rightmost
111
+ class LastPage < Tag
112
+ include Link
113
+ def page #:nodoc:
114
+ @options[:total_pages]
115
+ end
116
+ end
117
+
118
+ # The "previous" page of the current page
119
+ class PrevPage < Tag
120
+ include Link
121
+
122
+ # TODO: Remove this initializer before 1.3.0.
123
+ def initialize(template, params: {}, param_name: nil, theme: nil, views_prefix: nil, **options) #:nodoc:
124
+ # params in Rails 5 may not be a Hash either,
125
+ # so it must be converted to a Hash to be merged into @params
126
+ if params && params.respond_to?(:to_unsafe_h)
127
+ ActiveSupport::Deprecation.warn 'Explicitly passing params to helpers could be omitted.'
128
+ params = params.to_unsafe_h
129
+ end
130
+
131
+ super(template, params: params, param_name: param_name, theme: theme, views_prefix: views_prefix, **options)
132
+ end
133
+
134
+ def page #:nodoc:
135
+ @options[:current_page] - 1
136
+ end
137
+ end
138
+
139
+ # The "next" page of the current page
140
+ class NextPage < Tag
141
+ include Link
142
+
143
+ # TODO: Remove this initializer before 1.3.0.
144
+ def initialize(template, params: {}, param_name: nil, theme: nil, views_prefix: nil, **options) #:nodoc:
145
+ # params in Rails 5 may not be a Hash either,
146
+ # so it must be converted to a Hash to be merged into @params
147
+ if params && params.respond_to?(:to_unsafe_h)
148
+ ActiveSupport::Deprecation.warn 'Explicitly passing params to helpers could be omitted.'
149
+ params = params.to_unsafe_h
150
+ end
151
+
152
+ super(template, params: params, param_name: param_name, theme: theme, views_prefix: views_prefix, **options)
153
+ end
154
+
155
+ def page #:nodoc:
156
+ @options[:current_page] + 1
157
+ end
158
+ end
159
+
160
+ # Non-link tag that stands for skipped pages...
161
+ class Gap < Tag
162
+ end
163
+ end
164
+ end
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kaminari #:nodoc:
4
+ class Engine < ::Jets::Engine #:nodoc:
5
+ end
6
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kaminari
4
+ class Turbine < ::Jets::Turbine #:nodoc:
5
+ # Doesn't actually do anything. Just keeping this hook point, mainly for compatibility
6
+ initializer 'kaminari' do
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'active_support/core_ext/module'
4
+ module Kaminari
5
+ # Kind of Array that can paginate
6
+ class PaginatableArray < Array
7
+ include Kaminari::ConfigurationMethods::ClassMethods
8
+
9
+ ENTRY = 'entry'.freeze
10
+
11
+ attr_internal_accessor :limit_value, :offset_value
12
+
13
+ # ==== Options
14
+ # * <tt>:limit</tt> - limit
15
+ # * <tt>:offset</tt> - offset
16
+ # * <tt>:total_count</tt> - total_count
17
+ # * <tt>:padding</tt> - padding
18
+ def initialize(original_array = [], limit: nil, offset: nil, total_count: nil, padding: nil)
19
+ @_original_array, @_limit_value, @_offset_value, @_total_count, @_padding = original_array, (limit || default_per_page).to_i, offset.to_i, total_count, padding.to_i
20
+
21
+ if limit && offset
22
+ extend Kaminari::PageScopeMethods
23
+ end
24
+
25
+ if @_total_count && (@_total_count <= original_array.count)
26
+ original_array = original_array.first(@_total_count)[@_offset_value, @_limit_value]
27
+ end
28
+
29
+ unless @_total_count
30
+ original_array = original_array[@_offset_value, @_limit_value]
31
+ end
32
+
33
+ super(original_array || [])
34
+ end
35
+
36
+ # Used for page_entry_info
37
+ def entry_name(options = {})
38
+ I18n.t('helpers.page_entries_info.entry', **options.reverse_merge(default: ENTRY.pluralize(options[:count])))
39
+ end
40
+
41
+ # items at the specified "page"
42
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
43
+ def #{Kaminari.config.page_method_name}(num = 1)
44
+ offset(limit_value * ((num = num.to_i - 1) < 0 ? 0 : num))
45
+ end
46
+ RUBY
47
+
48
+ # returns another chunk of the original array
49
+ def limit(num)
50
+ self.class.new @_original_array, limit: num, offset: @_offset_value, total_count: @_total_count, padding: @_padding
51
+ end
52
+
53
+ # total item numbers of the original array
54
+ def total_count
55
+ @_total_count || @_original_array.length
56
+ end
57
+
58
+ # returns another chunk of the original array
59
+ def offset(num)
60
+ self.class.new @_original_array, limit: @_limit_value, offset: num, total_count: @_total_count, padding: @_padding
61
+ end
62
+ end
63
+
64
+ # Wrap an Array object to make it paginatable
65
+ # ==== Options
66
+ # * <tt>:limit</tt> - limit
67
+ # * <tt>:offset</tt> - offset
68
+ # * <tt>:total_count</tt> - total_count
69
+ # * <tt>:padding</tt> - padding
70
+ def self.paginate_array(array, limit: nil, offset: nil, total_count: nil, padding: nil)
71
+ PaginatableArray.new array, limit: limit, offset: offset, total_count: total_count, padding: padding
72
+ end
73
+ end