cursed 0.1.0 → 0.2.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 +4 -4
- data/.rubocop.yml +3 -0
- data/README.md +11 -4
- data/lib/cursed.rb +1 -0
- data/lib/cursed/adapter/base.rb +11 -0
- data/lib/cursed/collection.rb +28 -49
- data/lib/cursed/page.rb +104 -0
- data/lib/cursed/version.rb +1 -1
- metadata +3 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: ddec2faf831b8c8a33d1aa2e9bef2f15fee19dae
|
4
|
+
data.tar.gz: 0e54d9462488a3db7f7b9558bd137125c1eba0d9
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: cefdc4be655a383b79111be16db1906d5091d0ec87ef97399493456168e82519ca778d9c340e2d4f6699bf5b094cb13e99c7826fff59bd7ba0736f9d946a3985
|
7
|
+
data.tar.gz: 577e3192cd34290aabe16cc8e5f2450099030c82eae83691f63fb7a08add56080be3fffa61de7f5a3a15c6583cc38c6117a979318d4eb2aa4bc91a9a35995b33
|
data/.rubocop.yml
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,6 @@
|
|
1
|
-
|
1
|
+
|
2
|
+
|
3
|
+
# Cursed [](https://circleci.com/gh/influitive/cursed)
|
2
4
|
|
3
5
|
Cursed is a gem that implements the cursoring pattern in Postgres with the
|
4
6
|
ActiveRecord and Sequel gems. The cursoring pattern is an alternative to
|
@@ -52,11 +54,16 @@ The `Collection` is enumerable so you can use it as you would us any array
|
|
52
54
|
```
|
53
55
|
|
54
56
|
To generate your next link and previous link merge in the values of `#next_page_params`
|
55
|
-
and `#prev_page_params` into your URL generator
|
57
|
+
and `#prev_page_params` into your URL generator. `#next_page?` and `#prev_page?` can
|
58
|
+
be used to determine if there are next or previous pages of records currently.
|
56
59
|
|
57
60
|
```erb
|
58
|
-
|
59
|
-
|
61
|
+
<% if collection.prev_page? %>
|
62
|
+
<%= link_to 'Previous Page', widgets_path(collection.prev_page_params) %>
|
63
|
+
<% end %>
|
64
|
+
<% if collection.next_page? %>
|
65
|
+
<%= link_to 'Next Page', widgets_path(collection.next_page_params) %>
|
66
|
+
<% end %>
|
60
67
|
```
|
61
68
|
|
62
69
|
## Development
|
data/lib/cursed.rb
CHANGED
data/lib/cursed/adapter/base.rb
CHANGED
@@ -28,4 +28,15 @@ module Cursed
|
|
28
28
|
end
|
29
29
|
end
|
30
30
|
end
|
31
|
+
|
32
|
+
module_function
|
33
|
+
|
34
|
+
def Adapter(value)
|
35
|
+
case value
|
36
|
+
when -> (x) { x.is_a?(Class) && x.ancestors.include?(Adapter::Base) } then value
|
37
|
+
when Sequel::Dataset then Adapter::Sequel
|
38
|
+
when ActiveRecord::Base, ActiveRecord::Relation then Adapter::ActiveRecord
|
39
|
+
else raise ArgumentError, "unable to cast #{value.inspect} to Adapter"
|
40
|
+
end
|
41
|
+
end
|
31
42
|
end
|
data/lib/cursed/collection.rb
CHANGED
@@ -1,9 +1,14 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
|
+
require 'forwardable'
|
4
|
+
|
3
5
|
module Cursed
|
4
6
|
class Collection
|
7
|
+
extend Forwardable
|
5
8
|
include Enumerable
|
6
9
|
|
10
|
+
def_delegators :current_page, :each, :maximum_id, :minimum_id, :next_page_params, :prev_page_params
|
11
|
+
|
7
12
|
attr_reader :relation, :cursor, :adapter
|
8
13
|
|
9
14
|
# @param relation [ActiveRecord::Relation or Sequel::Dataset] the relation to cursor on
|
@@ -12,72 +17,46 @@ module Cursed
|
|
12
17
|
def initialize(relation:, cursor:, adapter: nil)
|
13
18
|
@relation = relation
|
14
19
|
@cursor = cursor
|
15
|
-
@adapter = adapter ||
|
20
|
+
@adapter = Cursed::Adapter(adapter || relation)
|
16
21
|
end
|
17
22
|
|
18
|
-
#
|
19
|
-
|
20
|
-
collection.each(*args, &block)
|
21
|
-
end
|
22
|
-
|
23
|
-
# Invalidates the local cache of the current page - the next call to {#each}
|
24
|
-
# (or any Enumerable method that calls it) will fetch a fresh page.
|
23
|
+
# invalidates the {#current_page}, {#next_page} and {#prev_page}
|
24
|
+
# @see Page#invalidate!
|
25
25
|
def invalidate!
|
26
|
-
|
26
|
+
[prev_page, next_page, current_page].each(&:invalidate!)
|
27
27
|
end
|
28
28
|
|
29
|
-
#
|
30
|
-
def
|
31
|
-
|
29
|
+
# @return [Page] the current page
|
30
|
+
def current_page
|
31
|
+
@current_page ||= build_page(cursor)
|
32
32
|
end
|
33
33
|
|
34
|
-
#
|
35
|
-
def
|
36
|
-
|
34
|
+
# @return [Page] the page following this one
|
35
|
+
def next_page
|
36
|
+
@next_page ||= build_page(current_page.next_page_cursor)
|
37
37
|
end
|
38
38
|
|
39
|
-
#
|
40
|
-
|
41
|
-
|
42
|
-
def next_page_params
|
43
|
-
if cursor.forward?
|
44
|
-
after_maximum_params
|
45
|
-
else
|
46
|
-
before_minimum_params
|
47
|
-
end
|
39
|
+
# @return [Page] the page previous to this one
|
40
|
+
def prev_page
|
41
|
+
@prev_page ||= build_page(current_page.prev_page_cursor)
|
48
42
|
end
|
49
43
|
|
50
|
-
#
|
51
|
-
|
52
|
-
|
53
|
-
def prev_page_params
|
54
|
-
if cursor.forward?
|
55
|
-
before_minimum_params
|
56
|
-
else
|
57
|
-
after_maximum_params
|
58
|
-
end
|
44
|
+
# @return [Boolean] true if there are records that follow records in the current page
|
45
|
+
def next_page?
|
46
|
+
next_page.any?
|
59
47
|
end
|
60
48
|
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
@collection ||= adapter.new(relation).apply_to(cursor).to_a
|
49
|
+
# @return [Boolean] true if there are records that preceede records in the current page
|
50
|
+
def prev_page?
|
51
|
+
prev_page.any?
|
65
52
|
end
|
66
53
|
|
67
|
-
|
68
|
-
case relation
|
69
|
-
when Sequel::Dataset then Adapter::Sequel
|
70
|
-
when ActiveRecord::Base, ActiveRecord::Relation then Adapter::ActiveRecord
|
71
|
-
else raise ArgumentError, "unable to determine adapter for #{relation.inspect}"
|
72
|
-
end
|
73
|
-
end
|
54
|
+
private
|
74
55
|
|
75
|
-
|
76
|
-
{ after: maximum_id, limit: cursor.clamped_limit }
|
77
|
-
end
|
56
|
+
attr_reader :relation, :cursor, :adapter
|
78
57
|
|
79
|
-
def
|
80
|
-
|
58
|
+
def build_page(cursor)
|
59
|
+
Page.new(relation: adapter.new(relation.dup).apply_to(cursor), cursor: cursor)
|
81
60
|
end
|
82
61
|
end
|
83
62
|
end
|
data/lib/cursed/page.rb
ADDED
@@ -0,0 +1,104 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Cursed
|
4
|
+
class Page
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
def initialize(relation:, cursor:)
|
8
|
+
@relation = relation
|
9
|
+
@cursor = cursor
|
10
|
+
end
|
11
|
+
|
12
|
+
# Iterates through each element in the current page
|
13
|
+
def each(*args, &block)
|
14
|
+
collection.each(*args, &block)
|
15
|
+
end
|
16
|
+
|
17
|
+
# Invalidates the local cache of the current page - the next call to {#each}
|
18
|
+
# (or any Enumerable method that calls it) will fetch a fresh page.
|
19
|
+
def invalidate!
|
20
|
+
@collection = nil
|
21
|
+
@count = nil
|
22
|
+
end
|
23
|
+
|
24
|
+
# @return [Integer] the number of records in this page
|
25
|
+
def count
|
26
|
+
@count ||= @collection.try(:length) || relation.count
|
27
|
+
end
|
28
|
+
|
29
|
+
# @return [Boolean] if there are records on this page
|
30
|
+
def any?
|
31
|
+
count > 0
|
32
|
+
end
|
33
|
+
|
34
|
+
# @return [Integer] the maximum cursor index in the current page
|
35
|
+
def maximum_id
|
36
|
+
collection.map(&cursor.attribute).max
|
37
|
+
end
|
38
|
+
|
39
|
+
# @return [Integer] the minimum cursor index in the current page
|
40
|
+
def minimum_id
|
41
|
+
collection.map(&cursor.attribute).min
|
42
|
+
end
|
43
|
+
|
44
|
+
# Returns a hash of parameters which should be used for generating a next
|
45
|
+
# page link.
|
46
|
+
# @return [Hash] a hash containing any combination of :before, :after, :limit
|
47
|
+
def next_page_params
|
48
|
+
if cursor.forward?
|
49
|
+
after_maximum_params
|
50
|
+
else
|
51
|
+
before_minimum_params
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns a hash of parameters which should be used for generating a previous
|
56
|
+
# page link.
|
57
|
+
# @return [Hash] a hash containing any combination of :before, :after, :limit
|
58
|
+
def prev_page_params
|
59
|
+
if cursor.forward?
|
60
|
+
before_minimum_params
|
61
|
+
else
|
62
|
+
after_maximum_params
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
# @return [Cursor] with the values to fetch the next page
|
67
|
+
def next_page_cursor
|
68
|
+
params = next_page_params.merge(
|
69
|
+
maximum: cursor.maximum,
|
70
|
+
direction: cursor.direction,
|
71
|
+
attribute: cursor.attribute
|
72
|
+
)
|
73
|
+
|
74
|
+
Cursor.new(params)
|
75
|
+
end
|
76
|
+
|
77
|
+
# @return [Cursor] with the values to fetch the previous page
|
78
|
+
def prev_page_cursor
|
79
|
+
params = prev_page_params.merge(
|
80
|
+
maximum: cursor.maximum,
|
81
|
+
direction: cursor.direction,
|
82
|
+
attribute: cursor.attribute
|
83
|
+
)
|
84
|
+
|
85
|
+
Cursor.new(params)
|
86
|
+
end
|
87
|
+
|
88
|
+
private
|
89
|
+
|
90
|
+
attr_reader :relation, :cursor
|
91
|
+
|
92
|
+
def collection
|
93
|
+
@collection ||= relation.to_a
|
94
|
+
end
|
95
|
+
|
96
|
+
def after_maximum_params
|
97
|
+
{ after: maximum_id || cursor.after, limit: cursor.clamped_limit }
|
98
|
+
end
|
99
|
+
|
100
|
+
def before_minimum_params
|
101
|
+
{ before: minimum_id || cursor.before, limit: cursor.clamped_limit }
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
data/lib/cursed/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: cursed
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Will Howard
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-
|
11
|
+
date: 2016-07-04 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description:
|
14
14
|
email:
|
@@ -37,6 +37,7 @@ files:
|
|
37
37
|
- lib/cursed/adapter/sequel.rb
|
38
38
|
- lib/cursed/collection.rb
|
39
39
|
- lib/cursed/cursor.rb
|
40
|
+
- lib/cursed/page.rb
|
40
41
|
- lib/cursed/version.rb
|
41
42
|
homepage: https://www.github.com/influitive/cursed
|
42
43
|
licenses:
|