kaminari-surface 0.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 +7 -0
- data/.document +5 -0
- data/.gitignore +5 -0
- data/.rdoc_options +16 -0
- data/.rspec +1 -0
- data/ChangeLog.md +4 -0
- data/Gemfile +3 -0
- data/LICENSE.txt +20 -0
- data/README.md +96 -0
- data/Rakefile +25 -0
- data/TRACKER.md +17 -0
- data/capture.gif +0 -0
- data/example/.gitignore +17 -0
- data/example/Gemfile +52 -0
- data/example/Gemfile.lock +179 -0
- data/example/README.rdoc +28 -0
- data/example/Rakefile +6 -0
- data/example/app/assets/javascripts/application.js +16 -0
- data/example/app/assets/stylesheets/application.css +15 -0
- data/example/app/controllers/application_controller.rb +5 -0
- data/example/app/controllers/products_controller.rb +6 -0
- data/example/app/helpers/application_helper.rb +2 -0
- data/example/app/models/product.rb +2 -0
- data/example/app/views/layouts/application.html.erb +23 -0
- data/example/app/views/products/index.haml +18 -0
- data/example/bin/bundle +3 -0
- data/example/bin/rails +9 -0
- data/example/bin/rake +9 -0
- data/example/bin/setup +29 -0
- data/example/bin/spring +15 -0
- data/example/config.ru +4 -0
- data/example/config/application.rb +26 -0
- data/example/config/boot.rb +3 -0
- data/example/config/database.yml +25 -0
- data/example/config/environment.rb +5 -0
- data/example/config/environments/development.rb +41 -0
- data/example/config/environments/production.rb +79 -0
- data/example/config/environments/test.rb +42 -0
- data/example/config/initializers/assets.rb +11 -0
- data/example/config/initializers/backtrace_silencers.rb +7 -0
- data/example/config/initializers/cookies_serializer.rb +3 -0
- data/example/config/initializers/filter_parameter_logging.rb +4 -0
- data/example/config/initializers/inflections.rb +16 -0
- data/example/config/initializers/mime_types.rb +4 -0
- data/example/config/initializers/session_store.rb +3 -0
- data/example/config/initializers/surface.rb +74 -0
- data/example/config/initializers/wrap_parameters.rb +14 -0
- data/example/config/locales/en.yml +23 -0
- data/example/config/routes.rb +58 -0
- data/example/config/secrets.yml +22 -0
- data/example/db/migrate/20160501023444_create_products.rb +9 -0
- data/example/db/schema.rb +22 -0
- data/example/db/seeds.rb +7 -0
- data/example/lib/tasks/seed.rake +9 -0
- data/example/public/404.html +67 -0
- data/example/public/422.html +67 -0
- data/example/public/500.html +66 -0
- data/example/public/favicon.ico +0 -0
- data/example/public/robots.txt +5 -0
- data/example/test/fixtures/products.yml +7 -0
- data/example/test/models/product_test.rb +7 -0
- data/example/test/test_helper.rb +10 -0
- data/kaminari-surface.gemspec +51 -0
- data/lib/kaminari/surface.rb +15 -0
- data/lib/kaminari/surface/data_mapper_extension.rb +15 -0
- data/lib/kaminari/surface/page_scope_methods.rb +78 -0
- data/lib/kaminari/surface/paginatable_array_extension.rb +27 -0
- data/lib/kaminari/surface/version.rb +5 -0
- data/spec/acceptance/active_record_spec.rb +83 -0
- data/spec/acceptance/data_mapper_spec.rb +83 -0
- data/spec/acceptance/mongo_mapper_spec.rb +84 -0
- data/spec/acceptance/mongoid_spec.rb +84 -0
- data/spec/spec_helper.rb +112 -0
- data/spec/surface/page_scope_methods_spec.rb +129 -0
- data/spec/surface_spec.rb +8 -0
- metadata +364 -0
@@ -0,0 +1,66 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html>
|
3
|
+
<head>
|
4
|
+
<title>We're sorry, but something went wrong (500)</title>
|
5
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
6
|
+
<style>
|
7
|
+
body {
|
8
|
+
background-color: #EFEFEF;
|
9
|
+
color: #2E2F30;
|
10
|
+
text-align: center;
|
11
|
+
font-family: arial, sans-serif;
|
12
|
+
margin: 0;
|
13
|
+
}
|
14
|
+
|
15
|
+
div.dialog {
|
16
|
+
width: 95%;
|
17
|
+
max-width: 33em;
|
18
|
+
margin: 4em auto 0;
|
19
|
+
}
|
20
|
+
|
21
|
+
div.dialog > div {
|
22
|
+
border: 1px solid #CCC;
|
23
|
+
border-right-color: #999;
|
24
|
+
border-left-color: #999;
|
25
|
+
border-bottom-color: #BBB;
|
26
|
+
border-top: #B00100 solid 4px;
|
27
|
+
border-top-left-radius: 9px;
|
28
|
+
border-top-right-radius: 9px;
|
29
|
+
background-color: white;
|
30
|
+
padding: 7px 12% 0;
|
31
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
32
|
+
}
|
33
|
+
|
34
|
+
h1 {
|
35
|
+
font-size: 100%;
|
36
|
+
color: #730E15;
|
37
|
+
line-height: 1.5em;
|
38
|
+
}
|
39
|
+
|
40
|
+
div.dialog > p {
|
41
|
+
margin: 0 0 1em;
|
42
|
+
padding: 1em;
|
43
|
+
background-color: #F7F7F7;
|
44
|
+
border: 1px solid #CCC;
|
45
|
+
border-right-color: #999;
|
46
|
+
border-left-color: #999;
|
47
|
+
border-bottom-color: #999;
|
48
|
+
border-bottom-left-radius: 4px;
|
49
|
+
border-bottom-right-radius: 4px;
|
50
|
+
border-top-color: #DADADA;
|
51
|
+
color: #666;
|
52
|
+
box-shadow: 0 3px 8px rgba(50, 50, 50, 0.17);
|
53
|
+
}
|
54
|
+
</style>
|
55
|
+
</head>
|
56
|
+
|
57
|
+
<body>
|
58
|
+
<!-- This file lives in public/500.html -->
|
59
|
+
<div class="dialog">
|
60
|
+
<div>
|
61
|
+
<h1>We're sorry, but something went wrong.</h1>
|
62
|
+
</div>
|
63
|
+
<p>If you are the application owner check the logs for more information.</p>
|
64
|
+
</div>
|
65
|
+
</body>
|
66
|
+
</html>
|
File without changes
|
@@ -0,0 +1,10 @@
|
|
1
|
+
ENV['RAILS_ENV'] ||= 'test'
|
2
|
+
require File.expand_path('../../config/environment', __FILE__)
|
3
|
+
require 'rails/test_help'
|
4
|
+
|
5
|
+
class ActiveSupport::TestCase
|
6
|
+
# Setup all fixtures in test/fixtures/*.yml for all tests in alphabetical order.
|
7
|
+
fixtures :all
|
8
|
+
|
9
|
+
# Add more helper methods to be used by all tests here...
|
10
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
|
3
|
+
lib = File.expand_path('../lib', __FILE__)
|
4
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
5
|
+
require 'kaminari/surface/version'
|
6
|
+
|
7
|
+
Gem::Specification.new do |gem|
|
8
|
+
gem.name = "kaminari-surface"
|
9
|
+
gem.version = Kaminari::Surface::VERSION
|
10
|
+
gem.summary = %q{Surface the final search result page when desirable}
|
11
|
+
gem.description = %q{An extension to Kaminari pagination that brings the final page of a search results to the surface, and prevents unnecessary paging requests.}
|
12
|
+
gem.license = "MIT"
|
13
|
+
gem.authors = ["James Kassemi"]
|
14
|
+
gem.email = "jkassemi@gmail.com"
|
15
|
+
gem.homepage = "https://github.com/jkassemi/kaminari-surface"
|
16
|
+
|
17
|
+
gem.files = `git ls-files`.split($/)
|
18
|
+
|
19
|
+
`git submodule --quiet foreach --recursive pwd`.split($/).each do |submodule|
|
20
|
+
submodule.sub!("#{Dir.pwd}/",'')
|
21
|
+
|
22
|
+
Dir.chdir(submodule) do
|
23
|
+
`git ls-files`.split($/).map do |subpath|
|
24
|
+
gem.files << File.join(submodule,subpath)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
29
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
30
|
+
gem.require_paths = ['lib']
|
31
|
+
|
32
|
+
gem.add_runtime_dependency 'kaminari', '~> 0.16'
|
33
|
+
|
34
|
+
gem.add_development_dependency 'pry', '~> 0.10'
|
35
|
+
gem.add_development_dependency 'bundler', '~> 1'
|
36
|
+
gem.add_development_dependency 'faker', '~> 1.4'
|
37
|
+
gem.add_development_dependency 'rake', '~> 10.0'
|
38
|
+
gem.add_development_dependency 'rdoc', '~> 4.0'
|
39
|
+
gem.add_development_dependency 'rspec', '~> 3.0'
|
40
|
+
gem.add_development_dependency 'rubygems-tasks', '~> 0.2'
|
41
|
+
|
42
|
+
gem.add_development_dependency 'activerecord'
|
43
|
+
gem.add_development_dependency 'sqlite3'
|
44
|
+
gem.add_development_dependency 'data_mapper'
|
45
|
+
gem.add_development_dependency 'dm-sqlite-adapter'
|
46
|
+
gem.add_development_dependency 'kaminari-data_mapper'
|
47
|
+
gem.add_development_dependency 'mongoid'
|
48
|
+
gem.add_development_dependency 'kaminari-mongoid'
|
49
|
+
gem.add_development_dependency 'mongo_mapper'
|
50
|
+
gem.add_development_dependency 'kaminari-mongo_mapper'
|
51
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'kaminari'
|
2
|
+
|
3
|
+
require 'kaminari/surface/version'
|
4
|
+
require 'kaminari/surface/page_scope_methods'
|
5
|
+
|
6
|
+
Kaminari::PageScopeMethods.prepend(Kaminari::Surface::PageScopeMethods)
|
7
|
+
|
8
|
+
require 'kaminari/surface/paginatable_array_extension'
|
9
|
+
require 'kaminari/models/array_extension'
|
10
|
+
|
11
|
+
Kaminari::PaginatableArray.prepend(Kaminari::Surface::PaginatableArrayExtension)
|
12
|
+
|
13
|
+
if defined?(DataMapper)
|
14
|
+
# require 'kaminari/surface/data_mapper_extension'
|
15
|
+
end
|
@@ -0,0 +1,78 @@
|
|
1
|
+
module Kaminari
|
2
|
+
module Surface
|
3
|
+
module PageScopeMethods
|
4
|
+
def per(num)
|
5
|
+
fail ArgumentError if defined?(@_surface_value)
|
6
|
+
@_per_value = num
|
7
|
+
extend_state_to(super)
|
8
|
+
end
|
9
|
+
|
10
|
+
def padding(num)
|
11
|
+
fail ArgumentError if defined?(@_surface_value)
|
12
|
+
_padding = num
|
13
|
+
extend_state_to(super)
|
14
|
+
end
|
15
|
+
|
16
|
+
def surface(num)
|
17
|
+
remaining = total_count - (offset_value + per_value)
|
18
|
+
|
19
|
+
if remaining > 0 && remaining <= num
|
20
|
+
result = limit(limit_value + remaining).offset(offset_value)
|
21
|
+
else
|
22
|
+
result = self
|
23
|
+
end
|
24
|
+
|
25
|
+
@_surface_value = num
|
26
|
+
|
27
|
+
extend_state_to(result.extend(Kaminari::PageScopeMethods))
|
28
|
+
end
|
29
|
+
|
30
|
+
def current_page
|
31
|
+
offset_without_padding = offset_value
|
32
|
+
offset_without_padding -= @_padding if padding_enabled?
|
33
|
+
offset_without_padding = 0 if offset_without_padding < 0
|
34
|
+
|
35
|
+
(offset_without_padding / per_value) + 1
|
36
|
+
end
|
37
|
+
|
38
|
+
def total_pages
|
39
|
+
count_without_padding = total_count
|
40
|
+
count_without_padding -= @_padding if padding_enabled?
|
41
|
+
count_without_padding = 0 if count_without_padding < 0
|
42
|
+
|
43
|
+
total_pages_count = ((count_without_padding - surface_value).to_f / per_value).ceil
|
44
|
+
|
45
|
+
if max_pages.present? && max_pages < total_pages_count
|
46
|
+
max_pages
|
47
|
+
else
|
48
|
+
total_pages_count
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def num_pages
|
53
|
+
total_pages
|
54
|
+
end
|
55
|
+
|
56
|
+
def per_value
|
57
|
+
(@_per_value || default_per_page).to_i
|
58
|
+
end
|
59
|
+
|
60
|
+
def surface_value
|
61
|
+
(@_surface_value || 0).to_i
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
def padding_enabled?
|
67
|
+
defined?(@_padding) && @_padding
|
68
|
+
end
|
69
|
+
|
70
|
+
def extend_state_to(target)
|
71
|
+
target.instance_variable_set(:@_per_value, @_per_value)
|
72
|
+
target.instance_variable_set(:@_surface_value, @_surface_value)
|
73
|
+
target.instance_variable_set(:@_padding, @_padding)
|
74
|
+
target
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Kaminari
|
2
|
+
module Surface
|
3
|
+
module PaginatableArrayExtension
|
4
|
+
def limit(*_args)
|
5
|
+
copy_attrs(super)
|
6
|
+
end
|
7
|
+
|
8
|
+
def offset(*_args)
|
9
|
+
copy_attrs(super)
|
10
|
+
end
|
11
|
+
|
12
|
+
private
|
13
|
+
|
14
|
+
def copy_attrs(target)
|
15
|
+
if defined?(@_per_value)
|
16
|
+
target.instance_variable_set(:@_per_value, @_per_value)
|
17
|
+
end
|
18
|
+
|
19
|
+
if defined?(@_surface_value)
|
20
|
+
target.instance_variable_set(:@_surface_value, @_surface_value)
|
21
|
+
end
|
22
|
+
|
23
|
+
target
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'ActiveRecord integration' do
|
4
|
+
before(:context) do
|
5
|
+
23.times { SampleModels::ActiveRecord::Widget.create! }
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'first page when no surfacing requested' do
|
9
|
+
subject(:widgets) { SampleModels::ActiveRecord::Widget.page(1).per(5) }
|
10
|
+
|
11
|
+
it 'has 5 pages' do
|
12
|
+
expect(widgets.total_pages).to eq(5)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'has 5 widgets' do
|
16
|
+
expect(widgets.size).to eq(5)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has 23 total widgets' do
|
20
|
+
expect(widgets.total_count).to eq(23)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'first page when surfacing is 0' do
|
25
|
+
subject(:widgets) { SampleModels::ActiveRecord::Widget.page(1).per(5) }
|
26
|
+
|
27
|
+
it 'has 5 pages' do
|
28
|
+
expect(widgets.total_pages).to eq(5)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'first page when surfacing is 3' do
|
33
|
+
subject(:widgets) { SampleModels::ActiveRecord::Widget.page(1).per(5).surface(3) }
|
34
|
+
|
35
|
+
it 'has 4 pages' do
|
36
|
+
expect(widgets.total_pages).to eq(4)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'has 5 widgets' do
|
40
|
+
expect(widgets.size).to eq(5)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'last page when surfacing is 3' do
|
45
|
+
subject(:widgets){ SampleModels::ActiveRecord::Widget.page(4).per(5).surface(3) }
|
46
|
+
|
47
|
+
it 'has 8 widgets' do
|
48
|
+
expect(widgets.size).to eq(8)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'last page when surfacing is 8 (per + remainder)' do
|
53
|
+
subject(:widgets) { SampleModels::ActiveRecord::Widget.page(3).per(5).surface(8) }
|
54
|
+
|
55
|
+
it 'has 3 pages' do
|
56
|
+
expect(widgets.total_pages).to eq(3)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'has 13 widgets' do
|
60
|
+
expect(widgets.size).to eq(13)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'has 23 total widgets' do
|
64
|
+
expect(widgets.total_count).to eq(23)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when `per` is called after `surface`' do
|
69
|
+
subject(:widgets){ ->{ SampleModels::ActiveRecord::Widget.page(1).surface(5).per(5) } }
|
70
|
+
|
71
|
+
it 'should raise an `ArgumentError`' do
|
72
|
+
expect(subject).to raise_error(ArgumentError)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when `padding` is called after `surface`' do
|
77
|
+
subject(:widgets){ ->{ SampleModels::ActiveRecord::Widget.page(1).surface(5).padding(5) } }
|
78
|
+
|
79
|
+
it 'should raise an `ArgumentError`' do
|
80
|
+
expect(subject).to raise_error(ArgumentError)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
RSpec.describe 'DataMapper integration' do
|
4
|
+
before(:context) do
|
5
|
+
23.times { SampleModels::DataMapper::Widget.create }
|
6
|
+
end
|
7
|
+
|
8
|
+
context 'first page when no surfacing requested' do
|
9
|
+
subject(:widgets) { SampleModels::DataMapper::Widget.page(1).per(5) }
|
10
|
+
|
11
|
+
it 'has 5 pages' do
|
12
|
+
expect(widgets.total_pages).to eq(5)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'has 5 widgets' do
|
16
|
+
expect(widgets.size).to eq(5)
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'has 23 total widgets' do
|
20
|
+
expect(widgets.total_count).to eq(23)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'first page when surfacing is 0' do
|
25
|
+
subject(:widgets) { SampleModels::DataMapper::Widget.page(1).per(5) }
|
26
|
+
|
27
|
+
it 'has 5 pages' do
|
28
|
+
expect(widgets.total_pages).to eq(5)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
context 'first page when surfacing is 3' do
|
33
|
+
subject(:widgets) { SampleModels::DataMapper::Widget.page(1).per(5).surface(3) }
|
34
|
+
|
35
|
+
it 'has 4 pages' do
|
36
|
+
expect(widgets.total_pages).to eq(4)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'has 5 widgets' do
|
40
|
+
expect(widgets.size).to eq(5)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
context 'last page when surfacing is 3' do
|
45
|
+
subject(:widgets){ SampleModels::DataMapper::Widget.page(4).per(5).surface(3) }
|
46
|
+
|
47
|
+
it 'has 8 widgets' do
|
48
|
+
expect(widgets.size).to eq(8)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
context 'last page when surfacing is 8 (per + remainder)' do
|
53
|
+
subject(:widgets) { SampleModels::DataMapper::Widget.page(3).per(5).surface(8) }
|
54
|
+
|
55
|
+
it 'has 3 pages' do
|
56
|
+
expect(widgets.total_pages).to eq(3)
|
57
|
+
end
|
58
|
+
|
59
|
+
it 'has 13 widgets' do
|
60
|
+
expect(widgets.size).to eq(13)
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'has 23 total widgets' do
|
64
|
+
expect(widgets.total_count).to eq(23)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
context 'when `per` is called after `surface`' do
|
69
|
+
subject(:widgets){ ->{ SampleModels::DataMapper::Widget.page(1).surface(5).per(5) } }
|
70
|
+
|
71
|
+
it 'should raise an `ArgumentError`' do
|
72
|
+
expect(subject).to raise_error(ArgumentError)
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
76
|
+
context 'when `padding` is called after `surface`' do
|
77
|
+
subject(:widgets){ ->{ SampleModels::DataMapper::Widget.page(1).surface(5).padding(5) } }
|
78
|
+
|
79
|
+
it 'should raise an `ArgumentError`' do
|
80
|
+
expect(subject).to raise_error(ArgumentError)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|