kaminari-rails4 0.15.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (102) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gemtest +0 -0
  4. data/.gitignore +9 -0
  5. data/.rspec +2 -0
  6. data/.travis.yml +16 -0
  7. data/CHANGELOG +351 -0
  8. data/Gemfile +4 -0
  9. data/MIT-LICENSE +20 -0
  10. data/README.rdoc +300 -0
  11. data/Rakefile +42 -0
  12. data/app/helpers/kaminari_helper.rb +5 -0
  13. data/app/views/kaminari/_first_page.html.erb +11 -0
  14. data/app/views/kaminari/_first_page.html.haml +9 -0
  15. data/app/views/kaminari/_first_page.html.slim +10 -0
  16. data/app/views/kaminari/_gap.html.erb +8 -0
  17. data/app/views/kaminari/_gap.html.haml +8 -0
  18. data/app/views/kaminari/_gap.html.slim +9 -0
  19. data/app/views/kaminari/_last_page.html.erb +11 -0
  20. data/app/views/kaminari/_last_page.html.haml +9 -0
  21. data/app/views/kaminari/_last_page.html.slim +10 -0
  22. data/app/views/kaminari/_next_page.html.erb +11 -0
  23. data/app/views/kaminari/_next_page.html.haml +9 -0
  24. data/app/views/kaminari/_next_page.html.slim +10 -0
  25. data/app/views/kaminari/_page.html.erb +12 -0
  26. data/app/views/kaminari/_page.html.haml +10 -0
  27. data/app/views/kaminari/_page.html.slim +11 -0
  28. data/app/views/kaminari/_paginator.html.erb +23 -0
  29. data/app/views/kaminari/_paginator.html.haml +18 -0
  30. data/app/views/kaminari/_paginator.html.slim +19 -0
  31. data/app/views/kaminari/_prev_page.html.erb +11 -0
  32. data/app/views/kaminari/_prev_page.html.haml +9 -0
  33. data/app/views/kaminari/_prev_page.html.slim +10 -0
  34. data/config/locales/kaminari.yml +19 -0
  35. data/gemfiles/active_record_30.gemfile +7 -0
  36. data/gemfiles/active_record_31.gemfile +7 -0
  37. data/gemfiles/active_record_32.gemfile +10 -0
  38. data/gemfiles/active_record_40.gemfile +8 -0
  39. data/gemfiles/data_mapper_12.gemfile +15 -0
  40. data/gemfiles/mongo_mapper.gemfile +10 -0
  41. data/gemfiles/mongoid_24.gemfile +7 -0
  42. data/gemfiles/mongoid_30.gemfile +12 -0
  43. data/gemfiles/sinatra.gemfile +13 -0
  44. data/kaminari.gemspec +36 -0
  45. data/lib/generators/kaminari/config_generator.rb +16 -0
  46. data/lib/generators/kaminari/templates/kaminari_config.rb +10 -0
  47. data/lib/generators/kaminari/views_generator.rb +118 -0
  48. data/lib/kaminari.rb +38 -0
  49. data/lib/kaminari/config.rb +51 -0
  50. data/lib/kaminari/engine.rb +4 -0
  51. data/lib/kaminari/grape.rb +4 -0
  52. data/lib/kaminari/helpers/action_view_extension.rb +151 -0
  53. data/lib/kaminari/helpers/paginator.rb +186 -0
  54. data/lib/kaminari/helpers/sinatra_helpers.rb +144 -0
  55. data/lib/kaminari/helpers/tags.rb +95 -0
  56. data/lib/kaminari/hooks.rb +33 -0
  57. data/lib/kaminari/models/active_record_extension.rb +22 -0
  58. data/lib/kaminari/models/active_record_model_extension.rb +20 -0
  59. data/lib/kaminari/models/active_record_relation_methods.rb +29 -0
  60. data/lib/kaminari/models/array_extension.rb +58 -0
  61. data/lib/kaminari/models/configuration_methods.rb +48 -0
  62. data/lib/kaminari/models/data_mapper_collection_methods.rb +15 -0
  63. data/lib/kaminari/models/data_mapper_extension.rb +48 -0
  64. data/lib/kaminari/models/mongo_mapper_extension.rb +18 -0
  65. data/lib/kaminari/models/mongoid_criteria_methods.rb +23 -0
  66. data/lib/kaminari/models/mongoid_extension.rb +33 -0
  67. data/lib/kaminari/models/page_scope_methods.rb +70 -0
  68. data/lib/kaminari/models/plucky_criteria_methods.rb +18 -0
  69. data/lib/kaminari/railtie.rb +7 -0
  70. data/lib/kaminari/sinatra.rb +5 -0
  71. data/lib/kaminari/version.rb +3 -0
  72. data/spec/config/config_spec.rb +91 -0
  73. data/spec/fake_app/active_record/config.rb +3 -0
  74. data/spec/fake_app/active_record/models.rb +57 -0
  75. data/spec/fake_app/data_mapper/config.rb +1 -0
  76. data/spec/fake_app/data_mapper/models.rb +27 -0
  77. data/spec/fake_app/mongo_mapper/config.rb +2 -0
  78. data/spec/fake_app/mongo_mapper/models.rb +9 -0
  79. data/spec/fake_app/mongoid/config.rb +16 -0
  80. data/spec/fake_app/mongoid/models.rb +22 -0
  81. data/spec/fake_app/rails_app.rb +56 -0
  82. data/spec/fake_app/sinatra_app.rb +22 -0
  83. data/spec/fake_gem.rb +4 -0
  84. data/spec/helpers/action_view_extension_spec.rb +262 -0
  85. data/spec/helpers/helpers_spec.rb +135 -0
  86. data/spec/helpers/sinatra_helpers_spec.rb +170 -0
  87. data/spec/helpers/tags_spec.rb +140 -0
  88. data/spec/models/active_record/active_record_relation_methods_spec.rb +47 -0
  89. data/spec/models/active_record/default_per_page_spec.rb +32 -0
  90. data/spec/models/active_record/max_pages_spec.rb +23 -0
  91. data/spec/models/active_record/max_per_page_spec.rb +32 -0
  92. data/spec/models/active_record/scopes_spec.rb +242 -0
  93. data/spec/models/array_spec.rb +150 -0
  94. data/spec/models/data_mapper/data_mapper_spec.rb +179 -0
  95. data/spec/models/mongo_mapper/mongo_mapper_spec.rb +84 -0
  96. data/spec/models/mongoid/mongoid_spec.rb +126 -0
  97. data/spec/requests/users_spec.rb +53 -0
  98. data/spec/spec_helper.rb +33 -0
  99. data/spec/spec_helper_for_sinatra.rb +33 -0
  100. data/spec/support/database_cleaner.rb +16 -0
  101. data/spec/support/matchers.rb +52 -0
  102. metadata +300 -0
@@ -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 Kaminari::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 = Kaminari::Helpers::Paginator.new(
90
+ ActionViewTemplateProxy.new(:current_params => current_params, :current_path => current_path, :param_name => options[:param_name] || Kaminari.config.param_name),
91
+ options.reverse_merge(:current_page => scope.current_page, :total_pages => scope.total_pages, :per_page => scope.limit_value, :param_name => Kaminari.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) || Kaminari.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 kaminari's pagination helpers with Sinatra."
135
+ $stderr.puts "[!]Kaminari::Helpers::SinatraHelper does nothing now..."
136
+
137
+ module Kaminari::Helpers
138
+ module SinatraHelper
139
+ def self.registered(*)
140
+ end
141
+ end
142
+ end
143
+
144
+ end
@@ -0,0 +1,95 @@
1
+ module Kaminari
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/kaminari/ directory
8
+ # with underscored class name (besides the "Tag" class. Tag is an abstract
9
+ # class, so _tag parital is not needed).
10
+ # e.g.) PrevLink -> app/views/kaminari/_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/kaminari-x.x.x/app/views/kaminari/_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
+ @theme = @options[:theme] ? "#{@options.delete(:theme)}/" : ''
20
+ @params = @options[:params] ? template.params.merge(@options.delete :params) : template.params
21
+ end
22
+
23
+ def to_s(locals = {}) #:nodoc:
24
+ @template.render :partial => "kaminari/#{@theme}#{self.class.name.demodulize.underscore}", :locals => @options.merge(locals), :formats => [:html]
25
+ end
26
+
27
+ def page_url_for(page)
28
+ @template.url_for @params.merge(@param_name => (page <= 1 ? nil : page))
29
+ end
30
+ end
31
+
32
+ # Tag that contains a link
33
+ module Link
34
+ # target page number
35
+ def page
36
+ raise 'Override page with the actual page value to be a Page.'
37
+ end
38
+ # the link's href
39
+ def url
40
+ page_url_for page
41
+ end
42
+ def to_s(locals = {}) #:nodoc:
43
+ super locals.merge(:url => url)
44
+ end
45
+ end
46
+
47
+ # A page
48
+ class Page < Tag
49
+ include Link
50
+ # target page number
51
+ def page
52
+ @options[:page]
53
+ end
54
+ def to_s(locals = {}) #:nodoc:
55
+ super locals.merge(:page => page)
56
+ end
57
+ end
58
+
59
+ # Link with page number that appears at the leftmost
60
+ class FirstPage < Tag
61
+ include Link
62
+ def page #:nodoc:
63
+ 1
64
+ end
65
+ end
66
+
67
+ # Link with page number that appears at the rightmost
68
+ class LastPage < Tag
69
+ include Link
70
+ def page #:nodoc:
71
+ @options[:total_pages]
72
+ end
73
+ end
74
+
75
+ # The "previous" page of the current page
76
+ class PrevPage < Tag
77
+ include Link
78
+ def page #:nodoc:
79
+ @options[:current_page] - 1
80
+ end
81
+ end
82
+
83
+ # The "next" page of the current page
84
+ class NextPage < Tag
85
+ include Link
86
+ def page #:nodoc:
87
+ @options[:current_page] + 1
88
+ end
89
+ end
90
+
91
+ # Non-link tag that stands for skipped pages...
92
+ class Gap < Tag
93
+ end
94
+ end
95
+ end
@@ -0,0 +1,33 @@
1
+ module Kaminari
2
+ class Hooks
3
+ def self.init
4
+ ActiveSupport.on_load(:active_record) do
5
+ require 'kaminari/models/active_record_extension'
6
+ ::ActiveRecord::Base.send :include, Kaminari::ActiveRecordExtension
7
+ end
8
+
9
+ begin; require 'data_mapper'; rescue LoadError; end
10
+ if defined? ::DataMapper
11
+ require 'dm-aggregates'
12
+ require 'kaminari/models/data_mapper_extension'
13
+ ::DataMapper::Collection.send :include, Kaminari::DataMapperExtension::Collection
14
+ ::DataMapper::Model.append_extensions Kaminari::DataMapperExtension::Model
15
+ # ::DataMapper::Model.send :extend, Kaminari::DataMapperExtension::Model
16
+ end
17
+
18
+ begin; require 'mongoid'; rescue LoadError; end
19
+ if defined? ::Mongoid
20
+ require 'kaminari/models/mongoid_extension'
21
+ ::Mongoid::Criteria.send :include, Kaminari::MongoidExtension::Criteria
22
+ ::Mongoid::Document.send :include, Kaminari::MongoidExtension::Document
23
+ end
24
+
25
+ ActiveSupport.on_load(:mongo_mapper) do
26
+ require 'kaminari/models/mongo_mapper_extension'
27
+ ::MongoMapper::Document.send :include, Kaminari::MongoMapperExtension::Document
28
+ ::Plucky::Query.send :include, Kaminari::PluckyCriteriaMethods
29
+ end
30
+ require 'kaminari/models/array_extension'
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,22 @@
1
+ require 'kaminari/models/active_record_model_extension'
2
+
3
+ module Kaminari
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_kaminari(kls) #:nodoc:
10
+ inherited_without_kaminari kls
11
+ kls.send(:include, Kaminari::ActiveRecordModelExtension) if kls.superclass == ActiveRecord::Base
12
+ end
13
+ alias_method_chain :inherited, :kaminari
14
+ end
15
+
16
+ # Existing subclasses pick up the model extension as well
17
+ self.descendants.each do |kls|
18
+ kls.send(:include, Kaminari::ActiveRecordModelExtension) if kls.superclass == ActiveRecord::Base
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,20 @@
1
+ require 'kaminari/models/active_record_relation_methods'
2
+
3
+ module Kaminari
4
+ module ActiveRecordModelExtension
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+ self.send(:include, Kaminari::ConfigurationMethods)
9
+
10
+ # Fetch the values at the specified page number
11
+ # Model.page(5)
12
+ self.scope Kaminari.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 Kaminari::ActiveRecordRelationMethods
16
+ include Kaminari::PageScopeMethods
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,29 @@
1
+ module Kaminari
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 OrderdHash 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 Kaminari
3
+ # Kind of Array that can paginate
4
+ class PaginatableArray < Array
5
+ include Kaminari::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 Kaminari::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 #{Kaminari.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 Kaminari
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) || Kaminari.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) || Kaminari.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) || Kaminari.config.max_pages
45
+ end
46
+ end
47
+ end
48
+ end