cursor_pagination 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 160e8afd510f2789a414f3b860ae6d59c5c626c4
4
- data.tar.gz: b49030bc3ec98ec9091fcfcabe9184d31d68aec7
3
+ metadata.gz: e801f9543b56fe4801268a2bf320334b9170af5c
4
+ data.tar.gz: 32b9c82901596fee3cfe428aa048bffb79ec148c
5
5
  SHA512:
6
- metadata.gz: 97ce4b2fb4ecdfb51bb9c097fcbcdc6db5b6bd5e4f62619d2046349a9e803aa3a9586d9bf4ac0bd14ae438a7ba8b0ad41d4b6c5dab60f9ad50fac47b63014531
7
- data.tar.gz: db5fe954866b988670f3bfdb2a075e6fe656f674edd652315f7c4fb25768f17079d4e189690a4ba9e23f90a3189e308aff5ab67fc91fb800d1a950056dc31561
6
+ metadata.gz: 924930a39b2eb74ea406572a533db6522d2f9adaf6568bb5872dd96bc260e982ebc9264fd2530c00bde1cd763e5daf4466c43a585e8d7b9c8de4303765814f22
7
+ data.tar.gz: 8d5ee3dbb3abc1faa7382de0d0da077ec3f700ac494921b8b0bb6bf7e69811832ab4a3735e778d110b631a1d7f637cedfba80f3b11be662e258af88c86cf36bc
data/README.md CHANGED
@@ -34,6 +34,10 @@ You can pass options as second argument for `cursor` scope
34
34
 
35
35
  User.order('id DESC').cursor(params[:cursor], reverse: true).per(20)
36
36
 
37
+ You can also use multi columns for your entities
38
+
39
+ News.order('published_at DESC, id DESC').cursor(params[:cursor], columns: { published_at: { reverse: true }, id: { reverse: true } }).per(20)
40
+
37
41
  ## How it works?
38
42
 
39
43
  Actually, cursor is column value of specific entity, it uses to get all entities, later than specific. For example, if you have the Users set with IDs from 1 to 5,
@@ -50,6 +54,8 @@ At this point, `cursor` scope accepts these options:
50
54
 
51
55
  * `reverse`: Set it to true, if your set are ordered descendingly (_DESC_). Default: _false_
52
56
  * `column`: column value of cursor. For example, if you order your data set by *updated_at*, set *updated_at* column for cursor. Default: _id_
57
+ * `columns`: hash with columns information, where key is the column name and the value is column options. The available options is
58
+ * reverse: The same as global `reverse` option, but related to current column. Default: _false_
53
59
 
54
60
  ## Scope methods
55
61
 
@@ -14,7 +14,10 @@ Gem::Specification.new do |spec|
14
14
  spec.license = "MIT"
15
15
 
16
16
  spec.add_dependency "activerecord", ['>= 3.1']
17
+ spec.add_dependency "actionpack", ['>= 3.1']
17
18
  spec.add_development_dependency "rspec-rails"
19
+ spec.add_development_dependency "railties"
20
+ spec.add_development_dependency "capybara"
18
21
  spec.add_development_dependency "sqlite3-ruby"
19
22
  spec.add_development_dependency "database_cleaner", ['< 1.1.0']
20
23
 
@@ -1,7 +1,64 @@
1
+ require 'action_view'
2
+
1
3
  require "cursor_pagination/version"
2
- require "cursor_pagination/active_record_extension"
3
- require 'cursor_pagination/action_view_helper'
4
4
 
5
5
  module CursorPagination
6
- ::ActiveRecord::Base.send(:include, CursorPagination::ActiveRecordExtension)
6
+
7
+ class Cursor
8
+ attr_reader :cursor
9
+
10
+ def initialize(cursor)
11
+ @cursor = cursor
12
+ end
13
+
14
+ def self.decode(cursor)
15
+ unless cursor.nil?
16
+ new YAML.load(Base64.strict_decode64(cursor))
17
+ else
18
+ new nil
19
+ end
20
+ end
21
+
22
+ def self.value_from_entity(entity, columns)
23
+ value = []
24
+ columns.each_key do |column|
25
+ value << entity.send(column)
26
+ end
27
+ value.size == 1 ? value.first : value
28
+ end
29
+
30
+ def self.from_entity(entity, columns)
31
+ new value_from_entity(entity, columns)
32
+ end
33
+
34
+ def encoded
35
+ Base64.strict_encode64 cursor.to_yaml
36
+ end
37
+
38
+ def empty?
39
+ cursor.nil? || invalid?
40
+ end
41
+
42
+ def invalid?
43
+ cursor == -1
44
+ end
45
+
46
+ def value
47
+ @cursor
48
+ end
49
+
50
+ def to_s
51
+ cursor.nil? ? nil : encoded
52
+ end
53
+ end
54
+ end
55
+
56
+ #Include ActiveRecord extension
57
+ require "cursor_pagination/active_record_extension"
58
+ ::ActiveRecord::Base.send :include, CursorPagination::ActiveRecordExtension
59
+
60
+ #Include ActionView Helper
61
+ require 'cursor_pagination/action_view_helper'
62
+ ActiveSupport.on_load(:action_view) do
63
+ ::ActionView::Base.send :include, ::CursorPagination::ActionViewHelper
7
64
  end
@@ -18,18 +18,45 @@ module CursorPagination
18
18
  end
19
19
 
20
20
  def self.cursor(cursor, options = {})
21
- options.reverse_merge! column: :id, reverse: false
21
+ cursor = Cursor.decode(cursor) unless cursor.is_a? Cursor
22
+
23
+ options.reverse_merge! column: :id, reverse: false, columns: {}
24
+ if options[:columns].empty?
25
+ options[:columns][options[:column]] = { reverse: options[:reverse] }
26
+ end
27
+
22
28
  scoped_method = ActiveRecord::VERSION::STRING < '4.0' ? :scoped : :all
23
29
  @current_cursor = cursor
24
30
  @origin_scope = self.send scoped_method
25
31
  @cursor_options = options
26
32
 
27
33
  scope = @origin_scope
28
- scope = scope.where("#{options[:column]} #{options[:reverse] ? '<' : '>'} ?", cursor) if cursor
34
+ unless cursor.empty?
35
+ cursor_value = [*cursor.value]
36
+ scope = scope.where _cursor_to_where(options[:columns], cursor_value)
37
+ end
29
38
  scope = scope.limit(25)
30
39
 
31
40
  scope.extending(CursorPagination::PageScopeMethods)
32
41
  end
42
+
43
+ private
44
+ def self._cursor_to_where(columns, cursor, reverse = false)
45
+ _cursor_to_where_recursion(0, arel_table, columns.to_a, cursor, reverse)
46
+ end
47
+
48
+ def self._cursor_to_where_recursion(i, t, columns, cursor, reverse = false)
49
+ column = columns[i]
50
+ method = column.last[:reverse] ? :lt : :gt
51
+ method = (method == :lt ? :gt : :lt) if reverse
52
+ if (columns.size - i) == 1 #last column
53
+ method = (method == :lt ? :lteq : :gteq) if reverse
54
+ t[column.first].send method, cursor[i]
55
+ else
56
+ t[column.first].send(method, cursor[i]).or(t[column.first].eq(cursor[i]).and(_cursor_to_where_recursion(i+1, t, columns, cursor, reverse)))
57
+ end
58
+ end
59
+
33
60
  end
34
61
  end
35
62
  end
@@ -5,32 +5,43 @@ module CursorPagination
5
5
  end
6
6
 
7
7
  def first_page?
8
- previous_cursor == -1
8
+ previous_cursor.value == -1
9
9
  end
10
10
 
11
11
  def last_page?
12
- next_cursor == -1
12
+ next_cursor.value == -1
13
13
  end
14
14
 
15
15
  def previous_cursor
16
- options = cursor_options
17
- result = _origin_scope.where("#{options[:column]} #{options[:reverse] ? '>' : '<'}= ?", current_cursor).limit(limit_value+1).to_a
18
- case result.size
19
- when limit_value+1
20
- result.first.send(options[:column])
21
- when 0
22
- -1 #no previous page
16
+ Cursor.new(if current_cursor.empty?
17
+ -1
23
18
  else
24
- nil #first page, incomplete
25
- end
19
+ scope = _origin_scope.limit(limit_value+1).reverse_order
20
+ columns = cursor_options[:columns]
26
21
 
22
+ cursor_value = [*current_cursor.value]
23
+ scope = scope.where _cursor_to_where(columns, cursor_value, true)
24
+ result = scope.to_a
25
+
26
+ case result.size
27
+ when limit_value+1
28
+ Cursor.value_from_entity result.last, columns
29
+ when 0
30
+ -1 #no previous page
31
+ else
32
+ nil #first page, incomplete
33
+ end
34
+ end)
27
35
  end
28
36
 
29
37
  def next_cursor
30
- return -1 if last.nil?
31
- # try to get something after last cursor
32
- cursor = last.send(cursor_options[:column])
33
- _origin_scope.cursor(cursor, cursor_options).per(1).count.zero? ? -1 : cursor
38
+ Cursor.new(if last.nil?
39
+ -1
40
+ else
41
+ # try to get something after last cursor
42
+ cursor = Cursor.from_entity last, cursor_options[:columns]
43
+ _origin_scope.cursor(cursor, cursor_options).per(1).count.zero? ? -1 : cursor.value
44
+ end)
34
45
  end
35
46
  end
36
47
  end
@@ -1,3 +1,3 @@
1
1
  module CursorPagination
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  end
@@ -1,2 +1,3 @@
1
1
  ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
2
+ ActiveRecord::Base.default_timezone = :utc
2
3
  ActiveRecord::Base.establish_connection('test')
@@ -8,6 +8,7 @@ class CreateAllTables < ActiveRecord::Migration
8
8
  def self.up
9
9
  create_table :entities do |t|
10
10
  t.integer :custom
11
+ t.datetime :custom_time
11
12
  t.timestamps
12
13
  end
13
14
  end
@@ -18,6 +18,10 @@ app.initialize!
18
18
  # routes
19
19
  app.routes.draw do
20
20
  resources :entities
21
+
22
+ namespace :two_column do
23
+ resources :entities
24
+ end
21
25
  end
22
26
 
23
27
  #models
@@ -25,6 +29,30 @@ require 'fake_app/active_record/models' if defined? ActiveRecord
25
29
 
26
30
  # controllers
27
31
  class ApplicationController < ActionController::Base; end
32
+ class EntitiesController < ApplicationController
33
+
34
+ def index
35
+ @entities = Entity.order('custom ASC').cursor(params[:cursor], column: :custom).per(1)
36
+ render :inline => %q/
37
+ <%= previous_cursor_link(@entities, "Previous Page") %>
38
+ <%= @entities.map { |n| "Custom #{n.custom}" }.join("\n") %>
39
+ <%= next_cursor_link(@entities, "Next Page") %>/
40
+ end
41
+
42
+ end
43
+
44
+ module TwoColumn
45
+ class EntitiesController < ApplicationController
46
+ def index
47
+ @entities = Entity.order('custom_time DESC, id DESC').cursor(params[:cursor], columns: { custom_time: { reverse: true }, id: { reverse: true } }).per(1)
48
+ render :inline => %q/
49
+ <%= previous_cursor_link(@entities, "Previous Page") %>
50
+ <%= @entities.map { |n| "Custom #{n.custom}" }.join("\n") %>
51
+ <%= next_cursor_link(@entities, "Next Page") %>/
52
+ end
53
+
54
+ end
55
+ end
28
56
 
29
57
  # helpers
30
58
  Object.const_set(:ApplicationHelper, Module.new)
@@ -0,0 +1,95 @@
1
+ require 'spec_helper'
2
+
3
+ describe EntitiesController do
4
+
5
+ subject { page }
6
+
7
+ shared_examples_for "first page" do
8
+ it { should have_content "Custom 1" }
9
+ it { should_not have_link "Previous Page" }
10
+ it { should have_link "Next Page" }
11
+ end
12
+ shared_examples_for "second page" do
13
+ it { should have_content "Custom 2" }
14
+ it { should have_link "Previous Page" }
15
+ it { should have_link "Next Page" }
16
+ end
17
+ shared_examples_for "third page" do
18
+ it { should have_content "Custom 3" }
19
+ it { should have_link "Previous Page" }
20
+ it { should have_link "Next Page" }
21
+ end
22
+ shared_examples_for "last page" do
23
+ it { should have_content "Custom 4" }
24
+ it { should have_link "Previous Page" }
25
+ it { should_not have_link "Next Page" }
26
+ end
27
+ shared_examples_for "previous first page" do
28
+ describe "previous page" do
29
+ before { click_link "Previous Page" }
30
+ it_should_behave_like "first page"
31
+ end
32
+ end
33
+ shared_examples_for "previous second page" do
34
+ describe "previous page" do
35
+ before { click_link "Previous Page" }
36
+ it_should_behave_like "second page"
37
+
38
+ it_should_behave_like "previous first page"
39
+ end
40
+ end
41
+
42
+
43
+ shared_examples_for "cursor pagination" do
44
+ it_should_behave_like "first page"
45
+
46
+ describe "second page" do
47
+ before { click_link "Next Page" }
48
+ it_should_behave_like "second page"
49
+
50
+ describe "third page" do
51
+ before { click_link "Next Page" }
52
+ it_should_behave_like "third page"
53
+
54
+ describe "last page" do
55
+ before { click_link "Next Page" }
56
+ it_should_behave_like "last page"
57
+
58
+ describe "previous page" do
59
+ before { click_link "Previous Page" }
60
+ it_should_behave_like "third page"
61
+
62
+ it_should_behave_like "previous second page"
63
+ end
64
+ end
65
+
66
+ it_should_behave_like "previous second page"
67
+ end
68
+
69
+ it_should_behave_like "previous first page"
70
+ end
71
+ end
72
+
73
+ context "by custom" do
74
+ include_context "entities"
75
+
76
+ before { visit entities_path }
77
+
78
+ it_should_behave_like "cursor pagination"
79
+ end
80
+
81
+ context "by two-columns" do
82
+ before do
83
+ two_minutes_ago = 2.minutes.ago
84
+ Entity.create! custom: 1, custom_time: 1.minute.ago
85
+ Entity.create! custom: 3, custom_time: two_minutes_ago
86
+ Entity.create! custom: 2, custom_time: two_minutes_ago
87
+ Entity.create! custom: 4, custom_time: 3.minutes.ago
88
+
89
+ visit two_column_entities_path
90
+ end
91
+
92
+ it_should_behave_like "cursor pagination"
93
+ end
94
+
95
+ end
@@ -16,7 +16,7 @@ describe CursorPagination::ActionViewHelper do
16
16
  subject { helper.previous_cursor_link third_page, 'Previous', {:controller => 'entities', :action => 'index'} }
17
17
  it { should be_a String }
18
18
  it { should match(/rel="previous"/) }
19
- it { should match(/cursor=#{first_entity.id}/) }
19
+ it { should match("cursor=#{CGI.escape(c(first_entity.id).to_s)}") }
20
20
  end
21
21
  context 'overriding rel=' do
22
22
  subject { helper.previous_cursor_link second_page, 'Previous', {:controller => 'entities', :action => 'index'}, {:rel => 'external'} }
@@ -35,7 +35,7 @@ describe CursorPagination::ActionViewHelper do
35
35
  subject { helper.next_cursor_link first_page, 'More', {:controller => 'entities', :action => 'index'} }
36
36
  it { should be_a String }
37
37
  it { should match(/rel="next"/) }
38
- it { should match(/cursor=#{first_entity.id}/) }
38
+ it { should match("cursor=#{CGI.escape(c(first_entity.id).to_s)}") }
39
39
  end
40
40
  context 'overriding rel=' do
41
41
  subject { helper.next_cursor_link first_page, 'More', {:controller => 'entities', :action => 'index'}, { :rel => 'external' } }
@@ -9,6 +9,24 @@ describe Entity do
9
9
  first_entity.custom.should be > second_entity.custom
10
10
  end
11
11
 
12
+ describe "cursor_options" do
13
+ it "accepts default values" do
14
+ options = Entity.cursor(nil).cursor_options
15
+ options[:columns].should eq id: { reverse: false }
16
+ end
17
+
18
+ it "accepts short column notation" do
19
+ options = Entity.cursor(nil, column: :custom, reverse: true).cursor_options
20
+ options[:columns].should eq custom: { reverse: true }
21
+ end
22
+
23
+ it "prefer full columns notation" do
24
+ full_options = { custom: { reverse: false } }
25
+ options = Entity.cursor(nil, column: :custom_time, reverse: true, columns: full_options).cursor_options
26
+ options[:columns].should eq full_options
27
+ end
28
+ end
29
+
12
30
  describe "#cursor method" do
13
31
  it "returns first entity only" do
14
32
  result = first_page.to_a
@@ -23,17 +41,37 @@ describe Entity do
23
41
  end
24
42
 
25
43
  it "support different orders" do
26
- result = Entity.order('id DESC').cursor(second_entity.id, reverse: true).per(1).to_a
44
+ result = Entity.order('id DESC').cursor(c(second_entity.id), reverse: true).per(1).to_a
27
45
  result.size.should eq 1
28
46
  result.first.should eq first_entity
29
47
  end
30
48
 
31
49
  it "support different columns" do
32
- result = Entity.cursor(second_entity.id, column: :custom).per(1).to_a
50
+ result = Entity.cursor(c(second_entity.id), column: :custom).per(1).to_a
33
51
  result.size.should eq 1
34
52
  result.first.should eq first_entity
35
53
  end
36
54
 
55
+ describe "time columns" do
56
+ let(:columns) do
57
+ { custom_time: { reverse: false }, id: { reverse: false } }
58
+ end
59
+ let(:scope) { Entity.order('custom_time ASC, id ASC') }
60
+ let(:first_page) { scope.cursor(nil, columns: columns ).per(1) }
61
+ specify { first_page.next_cursor.value.should eq [last_entity.custom_time, last_entity.id] }
62
+ let(:second_page) { scope.cursor(first_page.next_cursor, columns: columns).per(1) }
63
+ specify { second_page.next_cursor.value.should eq [third_entity.custom_time, third_entity.id] }
64
+ let(:previous_page) { scope.cursor(second_page.previous_cursor, columns: columns).per(1) }
65
+ specify { previous_page.next_cursor.value.should eq [last_entity.custom_time, last_entity.id] }
66
+ let(:third_page) { scope.cursor(second_page.next_cursor, columns: columns).per(1) }
67
+ specify { third_page.next_cursor.value.should eq [second_entity.custom_time, second_entity.id] }
68
+
69
+ specify { first_page.first.should eq last_entity }
70
+ specify { second_page.first.should eq third_entity }
71
+ specify { third_page.first.should eq second_entity }
72
+ specify { previous_page.first.should eq last_entity }
73
+ end
74
+
37
75
  context "without #per method" do
38
76
  before do
39
77
  25.times { Entity.create! }
@@ -51,43 +89,71 @@ describe Entity do
51
89
  # -1 means unavailable cursor (last or first page)
52
90
  describe "#next_cursor" do
53
91
  ##Default settings
54
- specify { Entity.cursor(nil).per(10).next_cursor.should eq -1 }
92
+ specify { Entity.cursor(nil).per(10).next_cursor.value.should eq -1 }
55
93
  specify { Entity.cursor(nil).per(10).should be_last_page }
56
- specify { first_page.next_cursor.should eq first_entity.id}
94
+ specify { first_page.next_cursor.should be_a CursorPagination::Cursor }
95
+ specify { first_page.next_cursor.value.should eq first_entity.id}
57
96
  specify { first_page.should_not be_last_page}
58
- specify { last_page.next_cursor.should eq -1 }
97
+ specify { last_page.next_cursor.value.should eq -1 }
59
98
 
60
99
  ##Reverse order
61
- specify { Entity.order('id DESC').cursor(nil, reverse: true).per(1).next_cursor.should eq last_entity.id }
62
- specify { Entity.order('id DESC').cursor(second_entity.id, reverse: true).per(1).next_cursor.should eq -1 }
100
+ specify { Entity.order('id DESC').cursor(nil, reverse: true).per(1).next_cursor.value.should eq last_entity.id }
101
+ specify { Entity.order('id DESC').cursor(c(second_entity.id), reverse: true).per(1).next_cursor.value.should eq -1 }
63
102
 
64
103
  ##With custom column
65
- specify { Entity.order('custom ASC').cursor(second_entity.custom, column: :custom).per(1).next_cursor.should eq -1 }
66
- specify { Entity.order('custom ASC').cursor(third_entity.custom, column: :custom).per(1).next_cursor.should eq second_entity.custom }
67
- specify { Entity.order('custom ASC').cursor(nil, column: :custom).per(1).next_cursor.should eq last_entity.custom }
104
+ specify { Entity.order('custom ASC').cursor(c(second_entity.custom), column: :custom).per(1).next_cursor.value.should eq -1 }
105
+ specify { Entity.order('custom ASC').cursor(c(third_entity.custom), column: :custom).per(1).next_cursor.value.should eq second_entity.custom }
106
+ specify { Entity.order('custom ASC').cursor(nil, column: :custom).per(1).next_cursor.value.should eq last_entity.custom }
68
107
  end
69
108
 
70
109
  describe "#previous_cursor" do
71
110
  ##Default settings
72
111
  #no previous page
73
- specify { Entity.cursor(nil).previous_cursor.should eq -1 }
112
+ specify { Entity.cursor(nil).previous_cursor.value.should eq -1 }
74
113
  specify { Entity.cursor(nil).should be_first_page }
75
114
  #not full previous page
76
- specify { Entity.cursor(first_entity.id).previous_cursor.should be_nil }
77
- specify { Entity.cursor(first_entity.id).should_not be_first_page }
115
+ specify { Entity.cursor(c(first_entity.id)).previous_cursor.value.should be_nil }
116
+ specify { Entity.cursor(c(first_entity.id)).should_not be_first_page }
78
117
  #full previous page
79
- specify { third_page.previous_cursor.should eq first_entity.id }
118
+ specify { third_page.previous_cursor.value.should eq first_entity.id }
80
119
  specify { third_page.should_not be_first_page }
81
120
 
82
121
  ##Reverse order
83
- specify { Entity.order('id DESC').cursor(nil, reverse: true).previous_cursor.should eq -1 }
84
- specify { Entity.order('id DESC').cursor(last_entity.id, reverse: true).previous_cursor.should be_nil }
85
- specify { Entity.order('id DESC').cursor(third_entity.id, reverse: true).per(1).previous_cursor.should eq last_entity.id }
122
+ specify { Entity.order('id DESC').cursor(nil, reverse: true).previous_cursor.value.should eq -1 }
123
+ specify { Entity.order('id DESC').cursor(c(last_entity.id), reverse: true).previous_cursor.value.should be_nil }
124
+ specify { Entity.order('id DESC').cursor(c(third_entity.id), reverse: true).per(1).previous_cursor.value.should eq last_entity.id }
86
125
 
87
126
  ##With custom column
88
- specify { Entity.order('custom ASC').cursor(nil, column: :custom).previous_cursor.should eq -1 }
89
- specify { Entity.order('custom ASC').cursor(last_entity.custom, column: :custom).previous_cursor.should be_nil }
90
- specify { Entity.order('custom ASC').cursor(third_entity.custom, column: :custom).per(1).previous_cursor.should eq last_entity.custom }
127
+ specify { Entity.order('custom ASC').cursor(nil, column: :custom).previous_cursor.value.should eq -1 }
128
+ specify { Entity.order('custom ASC').cursor(c(last_entity.custom), column: :custom).previous_cursor.value.should be_nil }
129
+ specify { Entity.order('custom ASC').cursor(c(third_entity.custom), column: :custom).per(1).previous_cursor.value.should eq last_entity.custom }
130
+ specify { Entity.order('custom ASC').cursor(c(second_entity.custom), column: :custom).per(1).previous_cursor.value.should eq third_entity.custom }
131
+ specify { Entity.order('custom ASC').cursor(c(first_entity.custom), column: :custom).per(1).previous_cursor.value.should eq second_entity.custom }
132
+ end
133
+ end
134
+
135
+ describe "._cursor_to_where" do
136
+ let(:time) { Time.at 1378132362 }
137
+ let(:columns) do
138
+ { id: { reverse: true }, custom: { reverse: false }, custom_time: { reverse: false } }
139
+ end
140
+ let(:cursor_value) { [1,2,time] }
141
+ let(:cursor) { CursorPagination::Cursor.new cursor_value }
142
+
143
+ context "with direct sql" do
144
+ specify do
145
+ target_sql = "(\"entities\".\"id\" < 1 OR \"entities\".\"id\" = 1 AND (\"entities\".\"custom\" > 2 OR \"entities\".\"custom\" = 2 AND \"entities\".\"custom_time\" > '2013-09-02 14:32:42.000000'))"
146
+ where = Entity.send(:_cursor_to_where, columns, cursor_value)
147
+ where.to_sql.should eq target_sql
148
+ end
149
+ end
150
+
151
+ context "with reversed sql" do
152
+ specify do
153
+ target_sql = "(\"entities\".\"id\" > 1 OR \"entities\".\"id\" = 1 AND (\"entities\".\"custom\" < 2 OR \"entities\".\"custom\" = 2 AND \"entities\".\"custom_time\" <= '2013-09-02 14:32:42.000000'))"
154
+ where = Entity.send(:_cursor_to_where, columns, cursor_value, true)
155
+ where.to_sql.should eq target_sql
156
+ end
91
157
  end
92
158
  end
93
159
  end
@@ -7,6 +7,7 @@ require 'cursor_pagination'
7
7
 
8
8
  require 'fake_app/rails'
9
9
  require 'rspec/rails'
10
+ require 'capybara/rspec'
10
11
 
11
12
  require 'database_cleaner'
12
13
 
@@ -1,6 +1,6 @@
1
1
  shared_context "entities" do
2
2
  before do
3
- 4.times { |n| Entity.create! custom: (4 - n)}
3
+ 4.times { |n| Entity.create! custom: (4 - n), custom_time: n.minutes.ago}
4
4
  end
5
5
 
6
6
  let(:entities) { Entity.all.to_a }
@@ -10,7 +10,11 @@ shared_context "entities" do
10
10
  let(:last_entity) { entities[3] }
11
11
 
12
12
  let(:first_page) { Entity.cursor(nil).per(1) }
13
- let(:second_page) { Entity.cursor(first_entity.id).per(1) }
14
- let(:third_page) { Entity.cursor(second_entity.id).per(1) }
15
- let(:last_page) { Entity.cursor(third_entity.id).per(1) }
13
+ let(:second_page) { Entity.cursor(c(first_entity.id)).per(1) }
14
+ let(:third_page) { Entity.cursor(c(second_entity.id)).per(1) }
15
+ let(:last_page) { Entity.cursor(c(third_entity.id)).per(1) }
16
+
17
+ def c(cursor)
18
+ CursorPagination::Cursor.new cursor
19
+ end
16
20
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: cursor_pagination
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergey Kukunin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-28 00:00:00.000000000 Z
11
+ date: 2013-09-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord
@@ -24,6 +24,20 @@ dependencies:
24
24
  - - '>='
25
25
  - !ruby/object:Gem::Version
26
26
  version: '3.1'
27
+ - !ruby/object:Gem::Dependency
28
+ name: actionpack
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - '>='
32
+ - !ruby/object:Gem::Version
33
+ version: '3.1'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - '>='
39
+ - !ruby/object:Gem::Version
40
+ version: '3.1'
27
41
  - !ruby/object:Gem::Dependency
28
42
  name: rspec-rails
29
43
  requirement: !ruby/object:Gem::Requirement
@@ -38,6 +52,34 @@ dependencies:
38
52
  - - '>='
39
53
  - !ruby/object:Gem::Version
40
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: railties
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - '>='
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: capybara
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - '>='
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: sqlite3-ruby
43
85
  requirement: !ruby/object:Gem::Requirement
@@ -111,7 +153,6 @@ files:
111
153
  - LICENSE.txt
112
154
  - README.md
113
155
  - Rakefile
114
- - app/helpers/cursor_pagination_helper.rb
115
156
  - cursor_pagination.gemspec
116
157
  - lib/cursor_pagination.rb
117
158
  - lib/cursor_pagination/action_view_helper.rb
@@ -122,6 +163,7 @@ files:
122
163
  - spec/fake_app/active_record/config.rb
123
164
  - spec/fake_app/active_record/models.rb
124
165
  - spec/fake_app/rails.rb
166
+ - spec/features/entities_feature_spec.rb
125
167
  - spec/helpers/cursor_pagination_helper_spec.rb
126
168
  - spec/models/entity_spec.rb
127
169
  - spec/spec_helper.rb
@@ -154,6 +196,7 @@ test_files:
154
196
  - spec/fake_app/active_record/config.rb
155
197
  - spec/fake_app/active_record/models.rb
156
198
  - spec/fake_app/rails.rb
199
+ - spec/features/entities_feature_spec.rb
157
200
  - spec/helpers/cursor_pagination_helper_spec.rb
158
201
  - spec/models/entity_spec.rb
159
202
  - spec/spec_helper.rb
@@ -1,3 +0,0 @@
1
- module CursorPaginationHelper
2
- include CursorPagination::ActionViewHelper
3
- end