-
Notifications
You must be signed in to change notification settings - Fork 3
/
model_extension.rb
117 lines (100 loc) · 3.2 KB
/
model_extension.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
require 'activerecord/cursor/params'
module ActiveRecord
module Cursor
module ModelExtension
extend ActiveSupport::Concern
module ClassMethods
def cursor(options = {})
@options = default_options.merge!(options).symbolize_keys!
@options[:direction] =
if @options.key?(:start) || @options.key?(:stop)
@options.key?(:start) ? :start : :stop
end
@cursor = Params.decode(@options[@options[:direction]]).value
@records = on_cursor.in_order.limit(@options[:size] + 1)
set_cursor
@records
rescue ActiveRecord::StatementInvalid
raise Cursor::InvalidCursor
end
def next_cursor
@next
end
def prev_cursor
@prev
end
def on_cursor
if @cursor.nil?
where(nil)
else
where(
"(#{column} = ? AND #{table_name}.id #{sign_of_inequality} ?) OR (#{column} #{sign_of_inequality} ?)",
@cursor[:key],
@cursor[:id],
@cursor[:key]
)
end
end
def in_order
order("#{column} #{by}", "#{table_name}.id #{by}")
end
private
def default_options
{ key: 'id', reverse: false, size: 1 }
end
def column
"#{table_name}.#{@options[:key]}"
end
def sign_of_inequality
case @options[:reverse]
when true
@options[:direction] == :start ? '<' : '>'
when false
@options[:direction] == :start ? '>' : '<'
end
end
def by
direction = @options[:direction]
case @options[:reverse]
when true
direction == :start || direction.nil? ? 'desc' : 'asc'
when false
direction == :start || direction.nil? ? 'asc' : 'desc'
end
end
def set_cursor
@next = nil
@prev = nil
if @options[:direction] == :start
set_cursor_on_start
elsif @options[:direction] == :stop
set_cursor_on_stop
elsif @records.size == @options[:size] + 1
@records = @records.limit(@options[:size])
@next = generate_cursor(@records[@records.size - 1])
end
end
def set_cursor_on_start
record = @records[0]
@prev = generate_cursor(record) if record
size = @records.size
@records = @records.limit(@options[:size])
return unless size == @options[:size] + 1
@next = generate_cursor(@records[@records.size - 1])
end
def set_cursor_on_stop
record = @records[0]
@next = generate_cursor(record) if record
size = @records.size
reverse_by = by == 'asc' ? 'desc' : 'asc'
@records = @records.reorder("#{column} #{reverse_by}").limit(@options[:size])
return unless size == @options[:size] + 1
@prev = generate_cursor(record)
end
def generate_cursor(record)
Params.new(id: record.id, key: record.public_send(@options[:key])).encoded
end
end
end
end
end