hobo_will_paginate 2.1.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/LICENSE +18 -0
- data/README.md +61 -0
- data/Rakefile +25 -0
- data/lib/will_paginate.rb +25 -0
- data/lib/will_paginate/active_record.rb +216 -0
- data/lib/will_paginate/array.rb +57 -0
- data/lib/will_paginate/collection.rb +149 -0
- data/lib/will_paginate/core_ext.rb +30 -0
- data/lib/will_paginate/data_mapper.rb +95 -0
- data/lib/will_paginate/deprecation.rb +55 -0
- data/lib/will_paginate/i18n.rb +22 -0
- data/lib/will_paginate/locale/en.yml +33 -0
- data/lib/will_paginate/page_number.rb +57 -0
- data/lib/will_paginate/per_page.rb +27 -0
- data/lib/will_paginate/railtie.rb +68 -0
- data/lib/will_paginate/sequel.rb +39 -0
- data/lib/will_paginate/version.rb +9 -0
- data/lib/will_paginate/view_helpers.rb +161 -0
- data/lib/will_paginate/view_helpers/action_view.rb +148 -0
- data/lib/will_paginate/view_helpers/link_renderer.rb +132 -0
- data/lib/will_paginate/view_helpers/link_renderer_base.rb +77 -0
- data/lib/will_paginate/view_helpers/merb.rb +26 -0
- data/lib/will_paginate/view_helpers/sinatra.rb +41 -0
- data/spec/ci.rb +29 -0
- data/spec/collection_spec.rb +139 -0
- data/spec/console +12 -0
- data/spec/console_fixtures.rb +28 -0
- data/spec/database.yml +22 -0
- data/spec/finders/active_record_spec.rb +543 -0
- data/spec/finders/activerecord_test_connector.rb +113 -0
- data/spec/finders/data_mapper_spec.rb +103 -0
- data/spec/finders/data_mapper_test_connector.rb +54 -0
- data/spec/finders/sequel_spec.rb +67 -0
- data/spec/finders/sequel_test_connector.rb +9 -0
- data/spec/fixtures/admin.rb +3 -0
- data/spec/fixtures/developer.rb +13 -0
- data/spec/fixtures/developers_projects.yml +13 -0
- data/spec/fixtures/project.rb +15 -0
- data/spec/fixtures/projects.yml +6 -0
- data/spec/fixtures/replies.yml +29 -0
- data/spec/fixtures/reply.rb +9 -0
- data/spec/fixtures/schema.rb +38 -0
- data/spec/fixtures/topic.rb +7 -0
- data/spec/fixtures/topics.yml +30 -0
- data/spec/fixtures/user.rb +2 -0
- data/spec/fixtures/users.yml +35 -0
- data/spec/page_number_spec.rb +65 -0
- data/spec/per_page_spec.rb +41 -0
- data/spec/spec_helper.rb +71 -0
- data/spec/view_helpers/action_view_spec.rb +423 -0
- data/spec/view_helpers/base_spec.rb +130 -0
- data/spec/view_helpers/link_renderer_base_spec.rb +87 -0
- data/spec/view_helpers/view_example_group.rb +114 -0
- metadata +104 -0
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'set'
|
2
|
+
|
3
|
+
# copied from ActiveSupport so we don't depend on it
|
4
|
+
|
5
|
+
unless Hash.method_defined? :except
|
6
|
+
Hash.class_eval do
|
7
|
+
# Returns a new hash without the given keys.
|
8
|
+
def except(*keys)
|
9
|
+
rejected = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
|
10
|
+
reject { |key,| rejected.include?(key) }
|
11
|
+
end
|
12
|
+
|
13
|
+
# Replaces the hash without only the given keys.
|
14
|
+
def except!(*keys)
|
15
|
+
replace(except(*keys))
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
unless String.method_defined? :underscore
|
21
|
+
String.class_eval do
|
22
|
+
def underscore
|
23
|
+
self.to_s.gsub(/::/, '/').
|
24
|
+
gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
|
25
|
+
gsub(/([a-z\d])([A-Z])/,'\1_\2').
|
26
|
+
tr("-", "_").
|
27
|
+
downcase
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
require 'dm-core'
|
2
|
+
require 'dm-aggregates'
|
3
|
+
require 'will_paginate/per_page'
|
4
|
+
require 'will_paginate/page_number'
|
5
|
+
require 'will_paginate/collection'
|
6
|
+
|
7
|
+
module WillPaginate
|
8
|
+
module DataMapper
|
9
|
+
module Pagination
|
10
|
+
def page(num)
|
11
|
+
pagenum = ::WillPaginate::PageNumber(num.nil? ? 1 : num)
|
12
|
+
per_page = query.limit || self.per_page
|
13
|
+
options = {:offset => pagenum.to_offset(per_page).to_i}
|
14
|
+
options[:limit] = per_page unless query.limit
|
15
|
+
col = new_collection(query.merge(options))
|
16
|
+
col.current_page = pagenum
|
17
|
+
col
|
18
|
+
end
|
19
|
+
|
20
|
+
def paginate(options)
|
21
|
+
options = options.dup
|
22
|
+
pagenum = options.fetch(:page) { raise ArgumentError, ":page parameter required" }
|
23
|
+
per_page = options.delete(:per_page) || self.per_page
|
24
|
+
|
25
|
+
options.delete(:page)
|
26
|
+
options[:limit] = per_page.to_i
|
27
|
+
|
28
|
+
all(options).page(pagenum)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
module CollectionMethods
|
33
|
+
include WillPaginate::CollectionMethods
|
34
|
+
|
35
|
+
attr_accessor :current_page
|
36
|
+
|
37
|
+
def paginated?
|
38
|
+
!current_page.nil?
|
39
|
+
end
|
40
|
+
|
41
|
+
def per_page
|
42
|
+
query.limit || model.per_page
|
43
|
+
end
|
44
|
+
|
45
|
+
def offset
|
46
|
+
query.offset
|
47
|
+
end
|
48
|
+
|
49
|
+
def total_entries
|
50
|
+
@total_entries ||= begin
|
51
|
+
if loaded? and @array.size < per_page and (current_page == 1 or @array.size > 0)
|
52
|
+
offset + @array.size
|
53
|
+
else
|
54
|
+
# :reload prevents Collection.filter from being run, which
|
55
|
+
# would cause a stack overflow
|
56
|
+
clean_query = query.merge(:reload => true)
|
57
|
+
# seems like the only way
|
58
|
+
clean_query.instance_variable_set('@limit', nil)
|
59
|
+
clean_query.instance_variable_set('@offset', 0)
|
60
|
+
new_collection(clean_query).count
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_a
|
66
|
+
if paginated?
|
67
|
+
::WillPaginate::Collection.create(current_page, per_page) do |col|
|
68
|
+
col.replace super
|
69
|
+
col.total_entries ||= total_entries
|
70
|
+
end
|
71
|
+
else
|
72
|
+
super
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
private
|
77
|
+
|
78
|
+
def new_collection(query, resources = nil)
|
79
|
+
col = super
|
80
|
+
col.current_page = self.current_page
|
81
|
+
col
|
82
|
+
end
|
83
|
+
|
84
|
+
def initialize_copy(original)
|
85
|
+
super
|
86
|
+
@total_entries = nil
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
::DataMapper::Model.append_extensions PerPage
|
91
|
+
::DataMapper::Model.append_extensions Pagination
|
92
|
+
::DataMapper::Collection.send(:include, Pagination)
|
93
|
+
::DataMapper::Collection.send(:include, CollectionMethods)
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
module WillPaginate::Deprecation
|
2
|
+
class << self
|
3
|
+
def warn(message, stack = caller)
|
4
|
+
offending_line = origin_of_call(stack)
|
5
|
+
full_message = "DEPRECATION WARNING: #{message} (called from #{offending_line})"
|
6
|
+
logger = rails_logger || Kernel
|
7
|
+
logger.warn full_message
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def rails_logger
|
13
|
+
defined?(Rails) && Rails.logger
|
14
|
+
end
|
15
|
+
|
16
|
+
def origin_of_call(stack)
|
17
|
+
lib_root = File.expand_path('../../..', __FILE__)
|
18
|
+
stack.find { |line| line.index(lib_root) != 0 } || stack.first
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Hash < ::Hash
|
23
|
+
def initialize(values = {})
|
24
|
+
super()
|
25
|
+
update values
|
26
|
+
@deprecated = {}
|
27
|
+
end
|
28
|
+
|
29
|
+
def []=(key, value)
|
30
|
+
check_deprecated(key, value)
|
31
|
+
super
|
32
|
+
end
|
33
|
+
|
34
|
+
def deprecate_key(*keys)
|
35
|
+
message = block_given? ? Proc.new : keys.pop
|
36
|
+
Array(keys).each { |key| @deprecated[key] = message }
|
37
|
+
end
|
38
|
+
|
39
|
+
def merge(another)
|
40
|
+
to_hash.update(another)
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_hash
|
44
|
+
::Hash.new.update(self)
|
45
|
+
end
|
46
|
+
|
47
|
+
private
|
48
|
+
|
49
|
+
def check_deprecated(key, value)
|
50
|
+
if msg = @deprecated[key] and (!msg.respond_to?(:call) or (msg = msg.call(key, value)))
|
51
|
+
WillPaginate::Deprecation.warn(msg)
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
55
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module WillPaginate
|
2
|
+
module I18n
|
3
|
+
def self.locale_dir
|
4
|
+
File.expand_path('../locale', __FILE__)
|
5
|
+
end
|
6
|
+
|
7
|
+
def self.load_path
|
8
|
+
Dir["#{locale_dir}/*.{rb,yml}"]
|
9
|
+
end
|
10
|
+
|
11
|
+
def will_paginate_translate(keys, options = {})
|
12
|
+
if defined? ::I18n
|
13
|
+
defaults = Array(keys).dup
|
14
|
+
defaults << Proc.new if block_given?
|
15
|
+
::I18n.translate(defaults.shift, options.merge(:default => defaults, :scope => :will_paginate))
|
16
|
+
else
|
17
|
+
key = Array === keys ? keys.first : keys
|
18
|
+
yield key, options
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
en:
|
2
|
+
will_paginate:
|
3
|
+
previous_label: "← Previous"
|
4
|
+
next_label: "Next →"
|
5
|
+
page_gap: "…"
|
6
|
+
|
7
|
+
page_entries_info:
|
8
|
+
single_page:
|
9
|
+
zero: "No %{model} found"
|
10
|
+
one: "Displaying 1 %{model}"
|
11
|
+
other: "Displaying all %{count} %{model}"
|
12
|
+
single_page_html:
|
13
|
+
zero: "No %{model} found"
|
14
|
+
one: "Displaying <b>1</b> %{model}"
|
15
|
+
other: "Displaying <b>all %{count}</b> %{model}"
|
16
|
+
|
17
|
+
multi_page: "Displaying %{model} %{from} - %{to} of %{count} in total"
|
18
|
+
multi_page_html: "Displaying %{model} <b>%{from} - %{to}</b> of <b>%{count}</b> in total"
|
19
|
+
|
20
|
+
# models:
|
21
|
+
# entry:
|
22
|
+
# zero: entries
|
23
|
+
# one: entry
|
24
|
+
# few: entries
|
25
|
+
# other: entries
|
26
|
+
|
27
|
+
# line_item:
|
28
|
+
# page_entries_info:
|
29
|
+
# single_page:
|
30
|
+
# zero: "Your shopping cart is empty"
|
31
|
+
# one: "Displaying one item in your cart"
|
32
|
+
# other: "Displaying all %{count} items"
|
33
|
+
# multi_page: "Displaying items %{from} - %{to} of %{count} in total"
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'delegate'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module WillPaginate
|
5
|
+
# a module that page number exceptions are tagged with
|
6
|
+
module InvalidPage; end
|
7
|
+
|
8
|
+
# integer representing a page number
|
9
|
+
class PageNumber < DelegateClass(Integer)
|
10
|
+
# a value larger than this is not supported in SQL queries
|
11
|
+
BIGINT = 9223372036854775807
|
12
|
+
|
13
|
+
extend Forwardable
|
14
|
+
|
15
|
+
def initialize(value, name)
|
16
|
+
value = Integer(value)
|
17
|
+
if 'offset' == name ? (value < 0 or value > BIGINT) : value < 1
|
18
|
+
raise RangeError, "invalid #{name}: #{value.inspect}"
|
19
|
+
end
|
20
|
+
@name = name
|
21
|
+
super(value)
|
22
|
+
rescue ArgumentError, TypeError, RangeError => error
|
23
|
+
error.extend InvalidPage
|
24
|
+
raise error
|
25
|
+
end
|
26
|
+
|
27
|
+
alias_method :to_i, :__getobj__
|
28
|
+
|
29
|
+
def inspect
|
30
|
+
"#{@name} #{to_i}"
|
31
|
+
end
|
32
|
+
|
33
|
+
def to_offset(per_page)
|
34
|
+
PageNumber.new((to_i - 1) * per_page.to_i, 'offset')
|
35
|
+
end
|
36
|
+
|
37
|
+
def kind_of?(klass)
|
38
|
+
super || to_i.kind_of?(klass)
|
39
|
+
end
|
40
|
+
alias is_a? kind_of?
|
41
|
+
end
|
42
|
+
|
43
|
+
# Ultrahax: makes `Fixnum === current_page` checks pass
|
44
|
+
Numeric.extend Module.new {
|
45
|
+
def ===(obj)
|
46
|
+
obj.instance_of? PageNumber or super
|
47
|
+
end
|
48
|
+
}
|
49
|
+
|
50
|
+
# An idemptotent coercion method
|
51
|
+
def self.PageNumber(value, name = 'page')
|
52
|
+
case value
|
53
|
+
when PageNumber then value
|
54
|
+
else PageNumber.new(value, name)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module WillPaginate
|
2
|
+
module PerPage
|
3
|
+
def per_page
|
4
|
+
defined?(@per_page) ? @per_page : WillPaginate.per_page
|
5
|
+
end
|
6
|
+
|
7
|
+
def per_page=(limit)
|
8
|
+
@per_page = limit.to_i
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.extended(base)
|
12
|
+
base.extend Inheritance if base.is_a? Class
|
13
|
+
end
|
14
|
+
|
15
|
+
module Inheritance
|
16
|
+
def inherited(subclass)
|
17
|
+
super
|
18
|
+
subclass.per_page = self.per_page
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
extend PerPage
|
24
|
+
|
25
|
+
# default number of items per page
|
26
|
+
self.per_page = 30
|
27
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'will_paginate'
|
2
|
+
require 'will_paginate/page_number'
|
3
|
+
require 'will_paginate/collection'
|
4
|
+
require 'will_paginate/i18n'
|
5
|
+
|
6
|
+
module WillPaginate
|
7
|
+
class Railtie < Rails::Railtie
|
8
|
+
initializer "will_paginate" do |app|
|
9
|
+
ActiveSupport.on_load :active_record do
|
10
|
+
require 'will_paginate/active_record'
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveSupport.on_load :action_controller do
|
14
|
+
WillPaginate::Railtie.setup_actioncontroller
|
15
|
+
end
|
16
|
+
|
17
|
+
ActiveSupport.on_load :action_view do
|
18
|
+
require 'will_paginate/view_helpers/action_view'
|
19
|
+
end
|
20
|
+
|
21
|
+
self.class.add_locale_path config
|
22
|
+
|
23
|
+
# early access to ViewHelpers.pagination_options
|
24
|
+
require 'will_paginate/view_helpers'
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.setup_actioncontroller
|
28
|
+
( defined?(ActionDispatch::ExceptionWrapper) ?
|
29
|
+
ActionDispatch::ExceptionWrapper : ActionDispatch::ShowExceptions
|
30
|
+
).send :include, ShowExceptionsPatch
|
31
|
+
ActionController::Base.extend ControllerRescuePatch
|
32
|
+
end
|
33
|
+
|
34
|
+
def self.add_locale_path(config)
|
35
|
+
config.i18n.railties_load_path.unshift(*WillPaginate::I18n.load_path)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Extending the exception handler middleware so it properly detects
|
39
|
+
# WillPaginate::InvalidPage regardless of it being a tag module.
|
40
|
+
module ShowExceptionsPatch
|
41
|
+
extend ActiveSupport::Concern
|
42
|
+
included { alias_method_chain :status_code, :paginate }
|
43
|
+
def status_code_with_paginate(exception = @exception)
|
44
|
+
if exception.is_a?(WillPaginate::InvalidPage) or
|
45
|
+
(exception.respond_to?(:original_exception) &&
|
46
|
+
exception.original_exception.is_a?(WillPaginate::InvalidPage))
|
47
|
+
Rack::Utils.status_code(:not_found)
|
48
|
+
else
|
49
|
+
original_method = method(:status_code_without_paginate)
|
50
|
+
if original_method.arity != 0
|
51
|
+
original_method.call(exception)
|
52
|
+
else
|
53
|
+
original_method.call()
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
module ControllerRescuePatch
|
60
|
+
def rescue_from(*args, &block)
|
61
|
+
if idx = args.index(WillPaginate::InvalidPage)
|
62
|
+
args[idx] = args[idx].name
|
63
|
+
end
|
64
|
+
super(*args, &block)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'sequel'
|
2
|
+
require 'sequel/extensions/pagination'
|
3
|
+
require 'will_paginate/collection'
|
4
|
+
|
5
|
+
module WillPaginate
|
6
|
+
# Sequel already supports pagination; we only need to make the
|
7
|
+
# resulting dataset look a bit more like WillPaginate::Collection
|
8
|
+
module SequelMethods
|
9
|
+
include WillPaginate::CollectionMethods
|
10
|
+
|
11
|
+
def total_pages
|
12
|
+
page_count
|
13
|
+
end
|
14
|
+
|
15
|
+
def per_page
|
16
|
+
page_size
|
17
|
+
end
|
18
|
+
|
19
|
+
def size
|
20
|
+
current_page_record_count
|
21
|
+
end
|
22
|
+
alias length size
|
23
|
+
|
24
|
+
def total_entries
|
25
|
+
pagination_record_count
|
26
|
+
end
|
27
|
+
|
28
|
+
def out_of_bounds?
|
29
|
+
current_page > total_pages
|
30
|
+
end
|
31
|
+
|
32
|
+
# Current offset of the paginated collection
|
33
|
+
def offset
|
34
|
+
(current_page - 1) * per_page
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
Sequel::Dataset::Pagination.send(:include, SequelMethods)
|
39
|
+
end
|