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.
Files changed (76) hide show
  1. checksums.yaml +7 -0
  2. data/.document +5 -0
  3. data/.gitignore +5 -0
  4. data/.rdoc_options +16 -0
  5. data/.rspec +1 -0
  6. data/ChangeLog.md +4 -0
  7. data/Gemfile +3 -0
  8. data/LICENSE.txt +20 -0
  9. data/README.md +96 -0
  10. data/Rakefile +25 -0
  11. data/TRACKER.md +17 -0
  12. data/capture.gif +0 -0
  13. data/example/.gitignore +17 -0
  14. data/example/Gemfile +52 -0
  15. data/example/Gemfile.lock +179 -0
  16. data/example/README.rdoc +28 -0
  17. data/example/Rakefile +6 -0
  18. data/example/app/assets/javascripts/application.js +16 -0
  19. data/example/app/assets/stylesheets/application.css +15 -0
  20. data/example/app/controllers/application_controller.rb +5 -0
  21. data/example/app/controllers/products_controller.rb +6 -0
  22. data/example/app/helpers/application_helper.rb +2 -0
  23. data/example/app/models/product.rb +2 -0
  24. data/example/app/views/layouts/application.html.erb +23 -0
  25. data/example/app/views/products/index.haml +18 -0
  26. data/example/bin/bundle +3 -0
  27. data/example/bin/rails +9 -0
  28. data/example/bin/rake +9 -0
  29. data/example/bin/setup +29 -0
  30. data/example/bin/spring +15 -0
  31. data/example/config.ru +4 -0
  32. data/example/config/application.rb +26 -0
  33. data/example/config/boot.rb +3 -0
  34. data/example/config/database.yml +25 -0
  35. data/example/config/environment.rb +5 -0
  36. data/example/config/environments/development.rb +41 -0
  37. data/example/config/environments/production.rb +79 -0
  38. data/example/config/environments/test.rb +42 -0
  39. data/example/config/initializers/assets.rb +11 -0
  40. data/example/config/initializers/backtrace_silencers.rb +7 -0
  41. data/example/config/initializers/cookies_serializer.rb +3 -0
  42. data/example/config/initializers/filter_parameter_logging.rb +4 -0
  43. data/example/config/initializers/inflections.rb +16 -0
  44. data/example/config/initializers/mime_types.rb +4 -0
  45. data/example/config/initializers/session_store.rb +3 -0
  46. data/example/config/initializers/surface.rb +74 -0
  47. data/example/config/initializers/wrap_parameters.rb +14 -0
  48. data/example/config/locales/en.yml +23 -0
  49. data/example/config/routes.rb +58 -0
  50. data/example/config/secrets.yml +22 -0
  51. data/example/db/migrate/20160501023444_create_products.rb +9 -0
  52. data/example/db/schema.rb +22 -0
  53. data/example/db/seeds.rb +7 -0
  54. data/example/lib/tasks/seed.rake +9 -0
  55. data/example/public/404.html +67 -0
  56. data/example/public/422.html +67 -0
  57. data/example/public/500.html +66 -0
  58. data/example/public/favicon.ico +0 -0
  59. data/example/public/robots.txt +5 -0
  60. data/example/test/fixtures/products.yml +7 -0
  61. data/example/test/models/product_test.rb +7 -0
  62. data/example/test/test_helper.rb +10 -0
  63. data/kaminari-surface.gemspec +51 -0
  64. data/lib/kaminari/surface.rb +15 -0
  65. data/lib/kaminari/surface/data_mapper_extension.rb +15 -0
  66. data/lib/kaminari/surface/page_scope_methods.rb +78 -0
  67. data/lib/kaminari/surface/paginatable_array_extension.rb +27 -0
  68. data/lib/kaminari/surface/version.rb +5 -0
  69. data/spec/acceptance/active_record_spec.rb +83 -0
  70. data/spec/acceptance/data_mapper_spec.rb +83 -0
  71. data/spec/acceptance/mongo_mapper_spec.rb +84 -0
  72. data/spec/acceptance/mongoid_spec.rb +84 -0
  73. data/spec/spec_helper.rb +112 -0
  74. data/spec/surface/page_scope_methods_spec.rb +129 -0
  75. data/spec/surface_spec.rb +8 -0
  76. 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,5 @@
1
+ # See http://www.robotstxt.org/robotstxt.html for documentation on how to use the robots.txt file
2
+ #
3
+ # To ban all spiders from the entire site uncomment the next two lines:
4
+ # User-agent: *
5
+ # Disallow: /
@@ -0,0 +1,7 @@
1
+ # Read about fixtures at http://api.rubyonrails.org/classes/ActiveRecord/FixtureSet.html
2
+
3
+ one:
4
+ name: MyString
5
+
6
+ two:
7
+ name: MyString
@@ -0,0 +1,7 @@
1
+ require 'test_helper'
2
+
3
+ class ProductTest < ActiveSupport::TestCase
4
+ # test "the truth" do
5
+ # assert true
6
+ # end
7
+ end
@@ -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,15 @@
1
+ module Kaminari
2
+ module Surface
3
+ module DataMapperExtension
4
+ module Paginating
5
+ def all(options={})
6
+ super.extend Paginating
7
+ end
8
+
9
+ def per(num)
10
+ super.extend Paginating
11
+ end
12
+ end
13
+ end
14
+ end
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,5 @@
1
+ module Kaminari
2
+ module Surface
3
+ VERSION = "0.1.0"
4
+ end
5
+ 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