sequel_core 1.5.1 → 2.0.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.
- data/CHANGELOG +116 -0
- data/COPYING +19 -19
- data/README +83 -32
- data/Rakefile +9 -20
- data/bin/sequel +43 -112
- data/doc/cheat_sheet.rdoc +225 -0
- data/doc/dataset_filtering.rdoc +257 -0
- data/lib/sequel_core/adapters/adapter_skeleton.rb +4 -2
- data/lib/sequel_core/adapters/ado.rb +3 -1
- data/lib/sequel_core/adapters/db2.rb +4 -2
- data/lib/sequel_core/adapters/dbi.rb +127 -113
- data/lib/sequel_core/adapters/informix.rb +4 -2
- data/lib/sequel_core/adapters/jdbc.rb +5 -3
- data/lib/sequel_core/adapters/mysql.rb +112 -46
- data/lib/sequel_core/adapters/odbc.rb +5 -7
- data/lib/sequel_core/adapters/odbc_mssql.rb +12 -3
- data/lib/sequel_core/adapters/openbase.rb +3 -1
- data/lib/sequel_core/adapters/oracle.rb +11 -9
- data/lib/sequel_core/adapters/postgres.rb +261 -262
- data/lib/sequel_core/adapters/sqlite.rb +72 -22
- data/lib/sequel_core/connection_pool.rb +140 -73
- data/lib/sequel_core/core_ext.rb +201 -66
- data/lib/sequel_core/core_sql.rb +123 -153
- data/lib/sequel_core/database/schema.rb +156 -0
- data/lib/sequel_core/database.rb +321 -338
- data/lib/sequel_core/dataset/callback.rb +11 -12
- data/lib/sequel_core/dataset/convenience.rb +213 -240
- data/lib/sequel_core/dataset/pagination.rb +58 -43
- data/lib/sequel_core/dataset/parse_tree_sequelizer.rb +331 -0
- data/lib/sequel_core/dataset/query.rb +41 -0
- data/lib/sequel_core/dataset/schema.rb +15 -0
- data/lib/sequel_core/dataset/sequelizer.rb +41 -373
- data/lib/sequel_core/dataset/sql.rb +741 -632
- data/lib/sequel_core/dataset.rb +183 -168
- data/lib/sequel_core/deprecated.rb +1 -169
- data/lib/sequel_core/exceptions.rb +24 -19
- data/lib/sequel_core/migration.rb +44 -52
- data/lib/sequel_core/object_graph.rb +43 -42
- data/lib/sequel_core/pretty_table.rb +71 -76
- data/lib/sequel_core/schema/generator.rb +163 -105
- data/lib/sequel_core/schema/sql.rb +250 -93
- data/lib/sequel_core/schema.rb +2 -8
- data/lib/sequel_core/sql.rb +394 -0
- data/lib/sequel_core/worker.rb +37 -27
- data/lib/sequel_core.rb +99 -45
- data/spec/adapters/informix_spec.rb +0 -1
- data/spec/adapters/mysql_spec.rb +177 -124
- data/spec/adapters/oracle_spec.rb +0 -1
- data/spec/adapters/postgres_spec.rb +98 -58
- data/spec/adapters/sqlite_spec.rb +45 -4
- data/spec/blockless_filters_spec.rb +269 -0
- data/spec/connection_pool_spec.rb +21 -18
- data/spec/core_ext_spec.rb +169 -19
- data/spec/core_sql_spec.rb +56 -49
- data/spec/database_spec.rb +78 -17
- data/spec/dataset_spec.rb +300 -428
- data/spec/migration_spec.rb +1 -1
- data/spec/object_graph_spec.rb +5 -11
- data/spec/rcov.opts +1 -1
- data/spec/schema_generator_spec.rb +16 -4
- data/spec/schema_spec.rb +89 -10
- data/spec/sequelizer_spec.rb +56 -56
- data/spec/spec.opts +0 -5
- data/spec/spec_config.rb +7 -0
- data/spec/spec_config.rb.example +5 -5
- data/spec/spec_helper.rb +6 -0
- data/spec/worker_spec.rb +1 -1
- metadata +78 -63
@@ -1,17 +1,16 @@
|
|
1
|
+
# This file holds empty methods that can be
|
2
|
+
# overridden to provide callback behavior.
|
3
|
+
|
1
4
|
module Sequel
|
2
5
|
class Dataset
|
3
|
-
|
4
|
-
#
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
# a single argument, an array of all
|
12
|
-
# returned records.
|
13
|
-
def post_load(all_records)
|
14
|
-
end
|
6
|
+
private
|
7
|
+
# This is run inside .all, after all
|
8
|
+
# of the records have been loaded
|
9
|
+
# via .each, but before any block passed
|
10
|
+
# to all is called. It is called with
|
11
|
+
# a single argument, an array of all
|
12
|
+
# returned records.
|
13
|
+
def post_load(all_records)
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
@@ -1,270 +1,243 @@
|
|
1
|
-
require 'enumerator'
|
2
|
-
|
3
1
|
module Sequel
|
4
2
|
class Dataset
|
5
|
-
|
6
|
-
|
7
|
-
def empty?
|
8
|
-
db.dataset.where(exists).get(1) == nil
|
9
|
-
end
|
10
|
-
|
11
|
-
# Returns the first record in the dataset.
|
12
|
-
def single_record(opts = nil)
|
13
|
-
each(opts) {|r| return r}
|
14
|
-
nil
|
15
|
-
end
|
16
|
-
|
17
|
-
NAKED_HASH = {:naked => true}.freeze
|
18
|
-
|
19
|
-
# Returns the first value of the first reecord in the dataset.
|
20
|
-
# Returns nil if dataset is empty.
|
21
|
-
def single_value(opts = nil)
|
22
|
-
opts = opts ? NAKED_HASH.merge(opts) : NAKED_HASH
|
23
|
-
# don't cache the columns
|
24
|
-
each(opts) {|r| @columns = nil; return r.values.first}
|
25
|
-
nil
|
26
|
-
end
|
27
|
-
|
28
|
-
def get(column)
|
29
|
-
select(column).single_value
|
30
|
-
end
|
3
|
+
COMMA_SEPARATOR = ', '.freeze
|
4
|
+
COUNT_OF_ALL_AS_COUNT = :count['*'.lit].as(:count)
|
31
5
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
return filter(&block).single_record(:limit => 1)
|
37
|
-
end
|
38
|
-
args = args.empty? ? 1 : (args.size == 1) ? args.first : args
|
39
|
-
case args
|
40
|
-
when 1
|
41
|
-
single_record(:limit => 1)
|
42
|
-
when Fixnum
|
43
|
-
limit(args).all
|
44
|
-
else
|
45
|
-
filter(args, &block).single_record(:limit => 1)
|
46
|
-
end
|
47
|
-
end
|
48
|
-
|
49
|
-
# Returns the first record matching the condition.
|
50
|
-
def [](*conditions)
|
51
|
-
first(*conditions)
|
52
|
-
end
|
53
|
-
|
54
|
-
def []=(conditions, values)
|
55
|
-
filter(conditions).update(values)
|
56
|
-
end
|
6
|
+
# Returns the first record matching the conditions.
|
7
|
+
def [](*conditions)
|
8
|
+
first(*conditions)
|
9
|
+
end
|
57
10
|
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
raise Error, 'No order specified' unless
|
64
|
-
@opts[:order] || (opts && opts[:order])
|
11
|
+
# Update all records matching the conditions
|
12
|
+
# with the values specified.
|
13
|
+
def []=(conditions, values)
|
14
|
+
filter(conditions).update(values)
|
15
|
+
end
|
65
16
|
|
66
|
-
|
17
|
+
# Returns the average value for the given column.
|
18
|
+
def avg(column)
|
19
|
+
get(:avg[column])
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns true if no records exists in the dataset
|
23
|
+
def empty?
|
24
|
+
db.dataset.where(exists).get(1) == nil
|
25
|
+
end
|
26
|
+
|
27
|
+
# Returns the first record in the dataset. If a numeric argument is
|
28
|
+
# given, it is interpreted as a limit, and then returns all
|
29
|
+
# matching records up to that limit. If no argument is passed,
|
30
|
+
# it returns the first matching record. If any other type of
|
31
|
+
# argument(s) is passed, it is given to filter and the
|
32
|
+
# first matching record is returned. If a block is given, it is used
|
33
|
+
# to filter the dataset before returning anything.
|
34
|
+
#
|
35
|
+
# Examples:
|
36
|
+
#
|
37
|
+
# ds.first => {:id=>7}
|
38
|
+
# ds.first(2) => [{:id=>6}, {:id=>4}]
|
39
|
+
# ds.order(:id).first(2) => [{:id=>1}, {:id=>2}]
|
40
|
+
# ds.first(:id=>2) => {:id=>2}
|
41
|
+
# ds.first("id = 3") => {:id=>3}
|
42
|
+
# ds.first("id = ?", 4) => {:id=>4}
|
43
|
+
# ds.first{:id > 2} => {:id=>5}
|
44
|
+
# ds.order(:id).first{:id > 2} => {:id=>3}
|
45
|
+
# ds.first{:id > 2} => {:id=>5}
|
46
|
+
# ds.first("id > ?", 4){:id < 6) => {:id=>5}
|
47
|
+
# ds.order(:id).first(2){:id < 2} => [{:id=>1}]
|
48
|
+
def first(*args, &block)
|
49
|
+
ds = block ? filter(&block) : self
|
67
50
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
single_record(opts)
|
75
|
-
else
|
76
|
-
clone(opts).all
|
77
|
-
end
|
51
|
+
if args.empty?
|
52
|
+
ds.single_record
|
53
|
+
else
|
54
|
+
args = (args.size == 1) ? args.first : args
|
55
|
+
if Integer === args
|
56
|
+
ds.limit(args).all
|
78
57
|
else
|
79
|
-
filter(args).
|
80
|
-
end
|
81
|
-
end
|
82
|
-
|
83
|
-
# Maps column values for each record in the dataset (if a column name is
|
84
|
-
# given), or performs the stock mapping functionality of Enumerable.
|
85
|
-
def map(column_name = nil, &block)
|
86
|
-
if column_name
|
87
|
-
super() {|r| r[column_name]}
|
88
|
-
else
|
89
|
-
super(&block)
|
58
|
+
ds.filter(args).single_record
|
90
59
|
end
|
91
60
|
end
|
61
|
+
end
|
92
62
|
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
m
|
98
|
-
end
|
99
|
-
end
|
63
|
+
# Return the column value for the first matching record in the dataset.
|
64
|
+
def get(column)
|
65
|
+
select(column).single_value
|
66
|
+
end
|
100
67
|
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
68
|
+
# Returns a dataset grouped by the given column with count by group.
|
69
|
+
def group_and_count(*columns)
|
70
|
+
group(*columns).select(*(columns + [COUNT_OF_ALL_AS_COUNT])).order(:count)
|
71
|
+
end
|
72
|
+
|
73
|
+
# Returns the interval between minimum and maximum values for the given
|
74
|
+
# column.
|
75
|
+
def interval(column)
|
76
|
+
get("(max(#{literal(column)}) - min(#{literal(column)}))".lit)
|
77
|
+
end
|
105
78
|
|
106
|
-
|
107
|
-
|
108
|
-
|
79
|
+
# Reverses the order and then runs first. Note that this
|
80
|
+
# will not necessarily give you the last record in the dataset,
|
81
|
+
# unless you have an unambiguous order. If there is not
|
82
|
+
# currently an order for this dataset, raises an Error.
|
83
|
+
def last(*args, &block)
|
84
|
+
raise(Error, 'No order specified') unless @opts[:order]
|
85
|
+
reverse.first(*args, &block)
|
86
|
+
end
|
87
|
+
|
88
|
+
# Maps column values for each record in the dataset (if a column name is
|
89
|
+
# given), or performs the stock mapping functionality of Enumerable.
|
90
|
+
def map(column_name = nil, &block)
|
91
|
+
if column_name
|
92
|
+
super() {|r| r[column_name]}
|
93
|
+
else
|
94
|
+
super(&block)
|
109
95
|
end
|
96
|
+
end
|
110
97
|
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
98
|
+
# Returns the maximum value for the given column.
|
99
|
+
def max(column)
|
100
|
+
get(:max[column])
|
101
|
+
end
|
115
102
|
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
COUNT_OF_ALL_AS_COUNT = :count['*'.lit].as(:count)
|
122
|
-
|
123
|
-
# Returns a dataset grouped by the given column with count by group.
|
124
|
-
def group_and_count(*columns)
|
125
|
-
group(*columns).select(columns + [COUNT_OF_ALL_AS_COUNT]).order(:count)
|
126
|
-
end
|
127
|
-
|
128
|
-
# Returns a Range object made from the minimum and maximum values for the
|
129
|
-
# given column.
|
130
|
-
def range(column)
|
131
|
-
if r = select(:min[column].as(:v1), :max[column].as(:v2)).first
|
132
|
-
(r[:v1]..r[:v2])
|
133
|
-
end
|
134
|
-
end
|
135
|
-
|
136
|
-
# Returns the interval between minimum and maximum values for the given
|
137
|
-
# column.
|
138
|
-
def interval(column)
|
139
|
-
if r = select("(max(#{literal(column)}) - min(#{literal(column)})) AS v".lit).first
|
140
|
-
r[:v]
|
141
|
-
end
|
142
|
-
end
|
103
|
+
# Returns the minimum value for the given column.
|
104
|
+
def min(column)
|
105
|
+
get(:min[column])
|
106
|
+
end
|
143
107
|
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
else
|
193
|
-
# we assume that an array of hashes is given
|
194
|
-
hashes, opts = *args
|
195
|
-
return if hashes.empty?
|
196
|
-
columns = hashes.first.keys
|
197
|
-
# convert the hashes into arrays
|
198
|
-
values = hashes.map {|h| columns.map {|c| h[c]}}
|
199
|
-
end
|
200
|
-
# make sure there's work to do
|
201
|
-
return if columns.empty? || values.empty?
|
202
|
-
|
203
|
-
slice_size = opts && (opts[:commit_every] || opts[:slice])
|
204
|
-
|
205
|
-
if slice_size
|
206
|
-
values.each_slice(slice_size) do |slice|
|
207
|
-
statements = multi_insert_sql(columns, slice)
|
208
|
-
@db.transaction {statements.each {|st| @db.execute(st)}}
|
209
|
-
end
|
210
|
-
else
|
211
|
-
statements = multi_insert_sql(columns, values)
|
108
|
+
# Inserts multiple records into the associated table. This method can be
|
109
|
+
# to efficiently insert a large amounts of records into a table. Inserts
|
110
|
+
# are automatically wrapped in a transaction.
|
111
|
+
#
|
112
|
+
# This method should be called with a columns array and an array of value arrays:
|
113
|
+
#
|
114
|
+
# dataset.multi_insert([:x, :y], [[1, 2], [3, 4]])
|
115
|
+
#
|
116
|
+
# This method can also be called with an array of hashes:
|
117
|
+
#
|
118
|
+
# dataset.multi_insert({:x => 1}, {:x => 2})
|
119
|
+
#
|
120
|
+
# Be aware that all hashes should have the same keys if you use this calling method,
|
121
|
+
# otherwise some columns could be missed or set to null instead of to default
|
122
|
+
# values.
|
123
|
+
#
|
124
|
+
# The method also accepts a :slice or :commit_every option that specifies
|
125
|
+
# the number of records to insert per transaction. This is useful especially
|
126
|
+
# when inserting a large number of records, e.g.:
|
127
|
+
#
|
128
|
+
# # this will commit every 50 records
|
129
|
+
# dataset.multi_insert(lots_of_records, :slice => 50)
|
130
|
+
def multi_insert(*args)
|
131
|
+
if args.empty?
|
132
|
+
return
|
133
|
+
elsif args[0].is_a?(Array) && args[1].is_a?(Array)
|
134
|
+
columns, values, opts = *args
|
135
|
+
elsif args[0].is_a?(Array) && args[1].is_a?(Dataset)
|
136
|
+
table = @opts[:from].first
|
137
|
+
columns, dataset = *args
|
138
|
+
sql = "INSERT INTO #{quote_identifier(table)} #{literal(columns)} VALUES #{literal(dataset)}"
|
139
|
+
return @db.transaction {@db.execute sql}
|
140
|
+
else
|
141
|
+
# we assume that an array of hashes is given
|
142
|
+
hashes, opts = *args
|
143
|
+
return if hashes.empty?
|
144
|
+
columns = hashes.first.keys
|
145
|
+
# convert the hashes into arrays
|
146
|
+
values = hashes.map {|h| columns.map {|c| h[c]}}
|
147
|
+
end
|
148
|
+
# make sure there's work to do
|
149
|
+
return if columns.empty? || values.empty?
|
150
|
+
|
151
|
+
slice_size = opts && (opts[:commit_every] || opts[:slice])
|
152
|
+
|
153
|
+
if slice_size
|
154
|
+
values.each_slice(slice_size) do |slice|
|
155
|
+
statements = multi_insert_sql(columns, slice)
|
212
156
|
@db.transaction {statements.each {|st| @db.execute(st)}}
|
213
157
|
end
|
158
|
+
else
|
159
|
+
statements = multi_insert_sql(columns, values)
|
160
|
+
@db.transaction {statements.each {|st| @db.execute(st)}}
|
214
161
|
end
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
162
|
+
end
|
163
|
+
alias_method :import, :multi_insert
|
164
|
+
|
165
|
+
# Pretty prints the records in the dataset as plain-text table.
|
166
|
+
def print(*cols)
|
167
|
+
Sequel::PrettyTable.print(naked.all, cols.empty? ? columns : cols)
|
168
|
+
end
|
169
|
+
|
170
|
+
# Returns a Range object made from the minimum and maximum values for the
|
171
|
+
# given column.
|
172
|
+
def range(column)
|
173
|
+
if r = select(:min[column].as(:v1), :max[column].as(:v2)).first
|
174
|
+
(r[:v1]..r[:v2])
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
# Returns the first record in the dataset.
|
179
|
+
def single_record(opts = nil)
|
180
|
+
each((opts||{}).merge(:limit=>1)){|r| return r}
|
181
|
+
nil
|
182
|
+
end
|
222
183
|
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
184
|
+
# Returns the first value of the first record in the dataset.
|
185
|
+
# Returns nil if dataset is empty.
|
186
|
+
def single_value(opts = nil)
|
187
|
+
if r = naked.single_record(opts)
|
188
|
+
r.values.first
|
227
189
|
end
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
copy.instance_eval(&block)
|
242
|
-
clone(copy.opts)
|
190
|
+
end
|
191
|
+
|
192
|
+
# Returns the sum for the given column.
|
193
|
+
def sum(column)
|
194
|
+
get(:sum[column])
|
195
|
+
end
|
196
|
+
|
197
|
+
# Returns true if the table exists. Will raise an error
|
198
|
+
# if the dataset has fixed SQL or selects from another dataset
|
199
|
+
# or more than one table.
|
200
|
+
def table_exists?
|
201
|
+
if @opts[:sql]
|
202
|
+
raise Sequel::Error, "this dataset has fixed SQL"
|
243
203
|
end
|
244
204
|
|
245
|
-
|
246
|
-
|
205
|
+
if @opts[:from].size != 1
|
206
|
+
raise Sequel::Error, "this dataset selects from multiple sources"
|
247
207
|
end
|
248
208
|
|
249
|
-
|
250
|
-
|
209
|
+
t = @opts[:from].first
|
210
|
+
if t.is_a?(Dataset)
|
211
|
+
raise Sequel::Error, "this dataset selects from a sub query"
|
251
212
|
end
|
252
213
|
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
214
|
+
@db.table_exists?(t.to_sym)
|
215
|
+
end
|
216
|
+
|
217
|
+
# Returns a string in CSV format containing the dataset records. By
|
218
|
+
# default the CSV representation includes the column titles in the
|
219
|
+
# first line. You can turn that off by passing false as the
|
220
|
+
# include_column_titles argument.
|
221
|
+
#
|
222
|
+
# This does not use a CSV library or handle quoting of values in
|
223
|
+
# any way. If any values in any of the rows could include commas or line
|
224
|
+
# endings, you probably shouldn't use this.
|
225
|
+
def to_csv(include_column_titles = true)
|
226
|
+
n = naked
|
227
|
+
cols = n.columns
|
228
|
+
csv = ''
|
229
|
+
csv << "#{cols.join(COMMA_SEPARATOR)}\r\n" if include_column_titles
|
230
|
+
n.each{|r| csv << "#{cols.collect{|c| r[c]}.join(COMMA_SEPARATOR)}\r\n"}
|
231
|
+
csv
|
232
|
+
end
|
233
|
+
|
234
|
+
# Returns a hash with one column used as key and another used as value.
|
235
|
+
# If rows have duplicate values for the key column, the latter row(s)
|
236
|
+
# will overwrite the value of the previous row(s).
|
237
|
+
def to_hash(key_column, value_column)
|
238
|
+
inject({}) do |m, r|
|
239
|
+
m[r[key_column]] = r[value_column]
|
240
|
+
m
|
268
241
|
end
|
269
242
|
end
|
270
243
|
end
|
@@ -1,62 +1,43 @@
|
|
1
|
-
require 'enumerator'
|
2
|
-
|
3
1
|
module Sequel
|
4
2
|
class Dataset
|
5
|
-
# Returns a paginated dataset. The
|
6
|
-
#
|
7
|
-
#
|
8
|
-
# for
|
9
|
-
def paginate(page_no, page_size)
|
3
|
+
# Returns a paginated dataset. The returned dataset is limited to
|
4
|
+
# the page size at the correct offset, and extended with the Pagination
|
5
|
+
# module. If a record count is not provided, does a count of total
|
6
|
+
# number of records for this dataset.
|
7
|
+
def paginate(page_no, page_size, record_count=nil)
|
10
8
|
raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
|
11
|
-
record_count = count
|
12
|
-
total_pages = (record_count / page_size.to_f).ceil
|
13
9
|
paginated = limit(page_size, (page_no - 1) * page_size)
|
14
10
|
paginated.extend(Pagination)
|
15
|
-
paginated.set_pagination_info(page_no, page_size, record_count)
|
16
|
-
paginated
|
11
|
+
paginated.set_pagination_info(page_no, page_size, record_count || count)
|
17
12
|
end
|
18
13
|
|
19
|
-
|
14
|
+
# Yields a paginated dataset for each page and returns the receiver. Does
|
15
|
+
# a count to find the total number of records for this dataset.
|
16
|
+
def each_page(page_size, &block)
|
20
17
|
raise(Error, "You cannot paginate a dataset that already has a limit") if @opts[:limit]
|
21
18
|
record_count = count
|
22
19
|
total_pages = (record_count / page_size.to_f).ceil
|
23
|
-
|
24
|
-
(1..total_pages).each do |page_no|
|
25
|
-
paginated = limit(page_size, (page_no - 1) * page_size)
|
26
|
-
paginated.extend(Pagination)
|
27
|
-
paginated.set_pagination_info(page_no, page_size, record_count)
|
28
|
-
yield paginated
|
29
|
-
end
|
30
|
-
|
20
|
+
(1..total_pages).each{|page_no| yield paginate(page_no, page_size, record_count)}
|
31
21
|
self
|
32
22
|
end
|
33
23
|
|
24
|
+
# Holds methods that only relate to paginated datasets. Paginated dataset
|
25
|
+
# have pages starting at 1 (page 1 is offset 0, page 1 is offset page_size).
|
34
26
|
module Pagination
|
35
|
-
|
27
|
+
# The number of records per page (the final page may have fewer than
|
28
|
+
# this number of records).
|
29
|
+
attr_accessor :page_size
|
36
30
|
|
37
|
-
#
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
def prev_page
|
47
|
-
current_page > 1 ? (current_page - 1) : nil
|
48
|
-
end
|
31
|
+
# The number of pages in the dataset before pagination, of which
|
32
|
+
# this paginated dataset is one.
|
33
|
+
attr_accessor :page_count
|
34
|
+
|
35
|
+
# The current page of the dataset, starting at 1 and not 0.
|
36
|
+
attr_accessor :current_page
|
37
|
+
|
38
|
+
# The total number of records in the dataset before pagination.
|
39
|
+
attr_accessor :pagination_record_count
|
49
40
|
|
50
|
-
# Returns the next page number or nil if the current page is the last page
|
51
|
-
def next_page
|
52
|
-
current_page < page_count ? (current_page + 1) : nil
|
53
|
-
end
|
54
|
-
|
55
|
-
# Returns the page range
|
56
|
-
def page_range
|
57
|
-
1..page_count
|
58
|
-
end
|
59
|
-
|
60
41
|
# Returns the record range for the current page
|
61
42
|
def current_page_record_range
|
62
43
|
return (0..0) if @current_page > @page_count
|
@@ -76,6 +57,40 @@ module Sequel
|
|
76
57
|
b = @pagination_record_count if b > @pagination_record_count
|
77
58
|
b - a + 1
|
78
59
|
end
|
60
|
+
|
61
|
+
# Returns true if the current page is the first page
|
62
|
+
def first_page?
|
63
|
+
@current_page == 1
|
64
|
+
end
|
65
|
+
|
66
|
+
# Returns true if the current page is the last page
|
67
|
+
def last_page?
|
68
|
+
@current_page == @page_count
|
69
|
+
end
|
70
|
+
|
71
|
+
# Returns the next page number or nil if the current page is the last page
|
72
|
+
def next_page
|
73
|
+
current_page < page_count ? (current_page + 1) : nil
|
74
|
+
end
|
75
|
+
|
76
|
+
# Returns the page range
|
77
|
+
def page_range
|
78
|
+
1..page_count
|
79
|
+
end
|
80
|
+
|
81
|
+
# Returns the previous page number or nil if the current page is the first
|
82
|
+
def prev_page
|
83
|
+
current_page > 1 ? (current_page - 1) : nil
|
84
|
+
end
|
85
|
+
|
86
|
+
# Sets the pagination info for this paginated dataset, and returns self.
|
87
|
+
def set_pagination_info(page_no, page_size, record_count)
|
88
|
+
@current_page = page_no
|
89
|
+
@page_size = page_size
|
90
|
+
@pagination_record_count = record_count
|
91
|
+
@page_count = (record_count / page_size.to_f).ceil
|
92
|
+
self
|
93
|
+
end
|
79
94
|
end
|
80
95
|
end
|
81
96
|
end
|