pageturner 3.1.0 → 4.0.0

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: 24794d49a1531ed3ab3a00c71c6b9a963e42bb46
4
- data.tar.gz: 23cc7603801d8948df99667797b60949a8d601da
3
+ metadata.gz: 9ec92e81b1919467490096e12c1bb8413a028262
4
+ data.tar.gz: 2215eda44407930869479a61c51f40c631d971b8
5
5
  SHA512:
6
- metadata.gz: 64f858bce99a796dc7ab0885b12c0027542bed4dff38120d41c244c98143931869605683c41d72cab9b504a4e53a2c46229678951913ef989216c5877aa6bf3a
7
- data.tar.gz: e9b2883c66a01680d3d6432ba4edd64ceb64c9e64ecc9031ade63ba79ed29150e0391f23588eb0c4febaebe1eb20b2fe69a50ab041f669ed9c19d8a33d63fb59
6
+ metadata.gz: 9c8ada0c35616416882e52147758e98dfb86222ac440f17c194f27eebc057da2013ae4331742b04356223d59b95881e936284a13e621168b5ce9b7ae68e96df4
7
+ data.tar.gz: 6de96592c15e9cd52ef14b08c09d1f378107877c93df23b2bdec14817f5fb9a8115c924da77e785f6633c966e1080cb19e37064dc9f539551f617f693d88c63c
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  # Pageturner
2
2
 
3
- Cursor based pagination for activerecord queries.
3
+ Cursor based pagination for activerecord queries as described in [Pagination Done the Right Way](http://use-the-index-luke.com/blog/2013-07/pagination-done-the-postgresql-way).
4
+
5
+ Pageturner allows you to paginate over a collection's attribute (`anchor_column`) starting from a given record as noted by its id and value (`anchor_id`, `anchor_value`). The `anchor_value` is the main value that divides your collection's pages. When the `anchor_value` is `nil`, we can't perform comparisons so we augment it with `anchor_id` to decide ambiguities about ordering.
4
6
 
5
7
  ## Usage
6
8
 
@@ -39,6 +41,19 @@ json.pagination do
39
41
  end
40
42
  ```
41
43
 
44
+ If the column that you want to paginate on is referenced differently in your database schema than in your application, then you can pass the translations into Pageturner. This may happen when you want to paginate over an associated model's column and the association is aliased in your model (e.g. `has_one :subordinate, class_name: "Employee"`).
45
+
46
+ ```ruby
47
+ @pagination = Pageturner.new(
48
+ anchor_column: "subordinate",
49
+ anchor_column_active_record_identifier: "subordinate.id",
50
+ anchor_column_sql_identifier: "employees.id",
51
+ ...
52
+ )
53
+ ```
54
+
55
+ Pageturner will send the `anchor_column_active_record_identifier` as a method chain to an instance of the model you're paginating when calculating the next cursor from the current page and it will interpolate the `anchor_column_sql_identifier` string when building the where and order by clauses of the query that paginates your model.
56
+
42
57
  ## API
43
58
 
44
59
  ### `#next_cursor`
@@ -6,16 +6,17 @@ class Pageturner
6
6
  LESS_THAN_OPERATOR = "<"
7
7
 
8
8
  # @param [String] anchor_column - Field to paginate on.
9
- # @param [String|Number|nil] anchor_value - Value of the anchor_column for the record to paginate from.
9
+ # @param [Anything, nil] anchor_value - Value of the anchor_column for the record to paginate from.
10
10
  # @param [ActiveRecord::Relation] ar_relation - Resource collection to paginate.
11
11
  # @param [String] sort_direction - Order of the pagination. Valid values: Pageturner::ASC, Pageturner::DESC.
12
12
  # @param [Number] anchor_id - ID of the record to paginate from.
13
- def initialize(anchor_column:, anchor_value:, ar_relation:, sort_direction:, page_size:, anchor_id:)
14
- # To be used outside dynamic SQL statements.
13
+ # @param [String, nil] anchor_column_active_record_identifier - Method chain that references the anchor column in the model you are paginating.
14
+ # @param [String, nil] anchor_column_sql_identifier - SQL identifier that references the anchor column in your database schema.
15
+ def initialize(anchor_column:, anchor_value:, ar_relation:, sort_direction:, page_size:, anchor_id:, anchor_column_active_record_identifier: nil, anchor_column_sql_identifier: nil)
15
16
  @anchor_column = anchor_column
16
17
 
17
- # To be used in dynamic SQL queries.
18
- @sql_anchor_column = ActiveRecord::Base.connection.quote_column_name(anchor_column)
18
+ @anchor_column_active_record_identifier = anchor_column_active_record_identifier || @anchor_column
19
+ @anchor_column_sql_identifier = anchor_column_sql_identifier || @anchor_column
19
20
 
20
21
  @anchor_value = anchor_value
21
22
 
@@ -43,17 +44,10 @@ class Pageturner
43
44
  end
44
45
 
45
46
  def next_cursor
46
- anchor_value =
47
- if external_anchor_column?
48
- public_send_chain_from_sql(self.resources.last, @anchor_column)
49
- else
50
- self.resources.last&.public_send(@anchor_column)
51
- end
52
-
53
47
  {
54
48
  anchor_column: @anchor_column,
55
49
  anchor_id: self.resources.last&.id,
56
- anchor_value: anchor_value,
50
+ anchor_value: try_chain(self.resources.last, @anchor_column_active_record_identifier),
57
51
  page_size: @page_size,
58
52
  sort_direction: @sort_direction
59
53
  }
@@ -91,12 +85,13 @@ class Pageturner
91
85
  end
92
86
 
93
87
  def calculate_next_page
94
- if external_anchor_column?
95
- table, column = @anchor_column.split(".")
88
+ # Heuristic to check if the `anchor_column_sql_identifier` is a joined column.
89
+ if @anchor_column_sql_identifier.include?(".")
90
+ table, column = @anchor_column_sql_identifier.split(".")
96
91
 
97
92
  qualified_anchor_column = "`#{table}`.`#{column}`"
98
93
  else
99
- qualified_anchor_column = "#{@ar_relation.quoted_table_name}.#{@sql_anchor_column}"
94
+ qualified_anchor_column = "#{@ar_relation.quoted_table_name}.#{ActiveRecord::Base.connection.quote_column_name(@anchor_column)}"
100
95
  end
101
96
 
102
97
  qualified_anchor_pk_column = "#{@ar_relation.quoted_table_name}.#{@ar_relation.quoted_primary_key}"
@@ -117,7 +112,7 @@ class Pageturner
117
112
 
118
113
  def apply_sort_direction(ar_relation)
119
114
  ar_relation
120
- .order("#{@anchor_column} #{@sort_direction}", @ar_relation.primary_key => @sort_direction)
115
+ .order("#{@anchor_column_sql_identifier} #{@sort_direction}", @ar_relation.primary_key => @sort_direction)
121
116
  .limit(@page_size)
122
117
  end
123
118
 
@@ -137,24 +132,9 @@ class Pageturner
137
132
  ar_relation.select_values.empty? || ar_relation.select_values.include?(@ar_relation.primary_key.to_sym)
138
133
  end
139
134
 
140
- # Gets an ActiveRecord instance's associated attribute via its qualified column sql identifier.
141
- #
142
- # Given qualified_column_sql_identifier = "joined_tables.column", it will call `.joined_table.column` on `object`.
143
- # `object` must be an ActiveRecord instance.
144
- def public_send_chain_from_sql(object, qualified_column_sql_identifier)
145
- # We limit `split` to 2 because we are assuming that the SQL identifier is in table.column format.
146
- association, attribute = qualified_column_sql_identifier.split(".", 2).each_with_index.map do |string, index|
147
- # The first element should be the joined table's sql identifier.
148
- # These are always pluralized, so we must singularize it in order to use it as a method call.
149
- index == 0 ? string.singularize : string
150
- end
151
-
152
- object.try!(association).try!(attribute)
153
- end
154
-
155
- # Heuristic to check if the `anchor_column` is a joined column.
156
- def external_anchor_column?
157
- @anchor_column.include?(".")
135
+ # Given attribute_chain = "association.attribute", it will call `.association.attribute` on `object`.
136
+ def try_chain(object, attribute_chain)
137
+ attribute_chain.split(".").reduce(object) { |o, command| o.try!(command) }
158
138
  end
159
139
 
160
140
  class Exception
@@ -4,7 +4,7 @@ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
4
 
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = "pageturner"
7
- spec.version = "3.1.0"
7
+ spec.version = "4.0.0"
8
8
  spec.authors = ["crzrcn"]
9
9
  spec.email = ["fernanlink@gmail.com"]
10
10
 
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pageturner
3
3
  version: !ruby/object:Gem::Version
4
- version: 3.1.0
4
+ version: 4.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - crzrcn
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2018-01-31 00:00:00.000000000 Z
11
+ date: 2018-02-23 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activerecord