sequel 0.1.6 → 0.1.7
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 +22 -0
- data/Rakefile +1 -1
- data/lib/sequel.rb +6 -0
- data/lib/sequel/core_ext.rb +32 -0
- data/lib/sequel/dataset.rb +55 -44
- data/lib/sequel/model.rb +7 -12
- data/lib/sequel/postgres.rb +65 -7
- data/lib/sequel/sqlite.rb +3 -9
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
*0.1.7*
|
2
|
+
|
3
|
+
* Removed db.synchronize wrapping calls in sqlite adapter.
|
4
|
+
|
5
|
+
* Implemented Model.join method to restrict returned columns to the model table (thanks Pedro Gutierrez).
|
6
|
+
|
7
|
+
* Implemented Dataset#paginate method.
|
8
|
+
|
9
|
+
* Fixed after_destroy hook.
|
10
|
+
|
11
|
+
* Improved Dataset#first and #last to accept a filter hash.
|
12
|
+
|
13
|
+
* Added Dataset#[]= method.
|
14
|
+
|
15
|
+
* Added Sequel() convenience method.
|
16
|
+
|
17
|
+
* Fixed Dataset#first to include a LIMIT clause for a single record.
|
18
|
+
|
19
|
+
* Small fix to Postgres driver to return a primary_key value for the inserted record if it is specified in the insertion values (thanks Florian Aßmann and Pedro Gutierrez).
|
20
|
+
|
21
|
+
* Fixed Symbol#DESC to support qualified notation (thanks Pedro Gutierrez).
|
22
|
+
|
1
23
|
*0.1.6*
|
2
24
|
|
3
25
|
* Fixed Model#method_missing to raise for an invalid attribute.
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
|
|
6
6
|
include FileUtils
|
7
7
|
|
8
8
|
NAME = "sequel"
|
9
|
-
VERS = "0.1.
|
9
|
+
VERS = "0.1.7"
|
10
10
|
CLEAN.include ['**/.*.sw?', 'pkg/*', '.config', 'doc/*', 'coverage/*']
|
11
11
|
RDOC_OPTS = ['--quiet', '--title', "Sequel: Concise ORM for Ruby",
|
12
12
|
"--opname", "index.html",
|
data/lib/sequel.rb
CHANGED
data/lib/sequel/core_ext.rb
CHANGED
@@ -39,3 +39,35 @@ class String
|
|
39
39
|
Sequel::ExpressionString.new(self)
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
# Symbol extensions
|
44
|
+
class Symbol
|
45
|
+
def DESC
|
46
|
+
"#{to_field_name} DESC"
|
47
|
+
end
|
48
|
+
|
49
|
+
def AS(target)
|
50
|
+
"#{to_field_name} AS #{target}"
|
51
|
+
end
|
52
|
+
|
53
|
+
AS_REGEXP = /(.*)___(.*)/.freeze
|
54
|
+
DOUBLE_UNDERSCORE = '__'.freeze
|
55
|
+
PERIOD = '.'.freeze
|
56
|
+
|
57
|
+
def to_field_name
|
58
|
+
s = to_s
|
59
|
+
if s =~ AS_REGEXP
|
60
|
+
s = "#{$1} AS #{$2}"
|
61
|
+
end
|
62
|
+
s.split(DOUBLE_UNDERSCORE).join(PERIOD)
|
63
|
+
end
|
64
|
+
|
65
|
+
def ALL
|
66
|
+
"#{to_s}.*"
|
67
|
+
end
|
68
|
+
|
69
|
+
def MIN; "min(#{to_field_name})"; end
|
70
|
+
def MAX; "max(#{to_field_name})"; end
|
71
|
+
def SUM; "sum(#{to_field_name})"; end
|
72
|
+
def AVG; "avg(#{to_field_name})"; end
|
73
|
+
end
|
data/lib/sequel/dataset.rb
CHANGED
@@ -294,12 +294,17 @@ module Sequel
|
|
294
294
|
clause = (@opts[:group] ? :having : :where)
|
295
295
|
cond = cond.first if cond.size == 1
|
296
296
|
parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
|
297
|
+
filter = cond.is_a?(Hash) && cond
|
297
298
|
if @opts[clause]
|
299
|
+
if filter && cond.is_a?(Hash)
|
300
|
+
filter
|
301
|
+
end
|
302
|
+
filter =
|
298
303
|
l = expression_list(@opts[clause])
|
299
304
|
r = expression_list(block || cond, parenthesize)
|
300
305
|
dup_merge(clause => "#{l} AND #{r}")
|
301
306
|
else
|
302
|
-
dup_merge(clause => expression_list(block || cond))
|
307
|
+
dup_merge(:filter => cond, clause => expression_list(block || cond))
|
303
308
|
end
|
304
309
|
end
|
305
310
|
|
@@ -606,6 +611,30 @@ module Sequel
|
|
606
611
|
end
|
607
612
|
alias size count
|
608
613
|
|
614
|
+
# returns a paginated dataset. The resulting dataset also provides the
|
615
|
+
# total number of pages (Dataset#page_count) and the current page number
|
616
|
+
# (Dataset#current_page), as well as Dataset#prev_page and Dataset#next_page
|
617
|
+
# for implementing pagination controls.
|
618
|
+
def paginate(page_no, page_size)
|
619
|
+
total_pages = (count / page_size.to_f).ceil
|
620
|
+
paginated = limit(page_size, (page_no - 1) * page_size)
|
621
|
+
paginated.current_page = page_no
|
622
|
+
paginated.page_count = total_pages
|
623
|
+
paginated
|
624
|
+
end
|
625
|
+
|
626
|
+
attr_accessor :page_count, :current_page
|
627
|
+
|
628
|
+
# Returns the previous page number or nil if the current page is the first
|
629
|
+
def prev_page
|
630
|
+
current_page > 1 ? (current_page - 1) : nil
|
631
|
+
end
|
632
|
+
|
633
|
+
# Returns the next page number or nil if the current page is the last page
|
634
|
+
def next_page
|
635
|
+
current_page < page_count ? (current_page + 1) : nil
|
636
|
+
end
|
637
|
+
|
609
638
|
# Returns a table reference for use in the FROM clause. If the dataset has
|
610
639
|
# only a :from option refering to a single table, only the table name is
|
611
640
|
# returned. Otherwise a subquery is returned.
|
@@ -660,35 +689,47 @@ module Sequel
|
|
660
689
|
|
661
690
|
# Returns the first record in the dataset. If the num argument is specified,
|
662
691
|
# an array is returned with the first <i>num</i> records.
|
663
|
-
def first(
|
664
|
-
|
665
|
-
|
692
|
+
def first(*args)
|
693
|
+
args = args.empty? ? 1 : (args.size == 1) ? args.first : args
|
694
|
+
case args
|
695
|
+
when 1: single_record(:limit => 1)
|
696
|
+
when Fixnum: limit(args).all
|
666
697
|
else
|
667
|
-
|
698
|
+
filter(args).single_record(:limit => 1)
|
668
699
|
end
|
669
700
|
end
|
670
701
|
|
671
702
|
# Returns the first record matching the condition.
|
672
703
|
def [](*conditions)
|
673
|
-
|
704
|
+
first(*conditions)
|
705
|
+
end
|
706
|
+
|
707
|
+
def []=(conditions, values)
|
708
|
+
filter(conditions).update(values)
|
674
709
|
end
|
675
710
|
|
676
711
|
# Returns the last records in the dataset by inverting the order. If no
|
677
712
|
# order is given, an exception is raised. If num is not given, the last
|
678
713
|
# record is returned. Otherwise an array is returned with the last
|
679
714
|
# <i>num</i> records.
|
680
|
-
def last(
|
715
|
+
def last(*args)
|
681
716
|
raise SequelError, 'No order specified' unless
|
682
717
|
@opts[:order] || (opts && opts[:order])
|
683
718
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
719
|
+
args = args.empty? ? 1 : (args.size == 1) ? args.first : args
|
720
|
+
|
721
|
+
case args
|
722
|
+
when Fixnum:
|
723
|
+
l = {:limit => args}
|
724
|
+
opts = {:order => invert_order(@opts[:order])}. \
|
725
|
+
merge(opts ? opts.merge(l) : l)
|
726
|
+
if args == 1
|
727
|
+
single_record(opts)
|
728
|
+
else
|
729
|
+
dup_merge(opts).all
|
730
|
+
end
|
690
731
|
else
|
691
|
-
|
732
|
+
filter(args).last(1)
|
692
733
|
end
|
693
734
|
end
|
694
735
|
|
@@ -709,33 +750,3 @@ module Sequel
|
|
709
750
|
end
|
710
751
|
end
|
711
752
|
|
712
|
-
class Symbol
|
713
|
-
def DESC
|
714
|
-
"#{to_s} DESC"
|
715
|
-
end
|
716
|
-
|
717
|
-
def AS(target)
|
718
|
-
"#{to_field_name} AS #{target}"
|
719
|
-
end
|
720
|
-
|
721
|
-
def MIN; "min(#{to_field_name})"; end
|
722
|
-
def MAX; "max(#{to_field_name})"; end
|
723
|
-
def SUM; "sum(#{to_field_name})"; end
|
724
|
-
def AVG; "avg(#{to_field_name})"; end
|
725
|
-
|
726
|
-
AS_REGEXP = /(.*)___(.*)/.freeze
|
727
|
-
DOUBLE_UNDERSCORE = '__'.freeze
|
728
|
-
PERIOD = '.'.freeze
|
729
|
-
|
730
|
-
def to_field_name
|
731
|
-
s = to_s
|
732
|
-
if s =~ AS_REGEXP
|
733
|
-
s = "#{$1} AS #{$2}"
|
734
|
-
end
|
735
|
-
s.split(DOUBLE_UNDERSCORE).join(PERIOD)
|
736
|
-
end
|
737
|
-
|
738
|
-
def ALL
|
739
|
-
"#{to_s}.*"
|
740
|
-
end
|
741
|
-
end
|
data/lib/sequel/model.rb
CHANGED
@@ -154,7 +154,7 @@ module Sequel
|
|
154
154
|
end
|
155
155
|
|
156
156
|
def self.after_destroy(&block)
|
157
|
-
get_hooks(:after_destroy)
|
157
|
+
get_hooks(:after_destroy) << block
|
158
158
|
end
|
159
159
|
|
160
160
|
def self.find(cond)
|
@@ -194,17 +194,6 @@ module Sequel
|
|
194
194
|
self
|
195
195
|
end
|
196
196
|
|
197
|
-
# def self.each(&block); dataset.each(&block); end
|
198
|
-
# def self.all; dataset.all; end
|
199
|
-
# def self.filter(*arg, &block); dataset.filter(*arg, &block); end
|
200
|
-
# def self.exclude(*arg, &block); dataset.exclude(*arg, &block); end
|
201
|
-
# def self.order(*arg); dataset.order(*arg); end
|
202
|
-
# def self.first(*arg); dataset.first(*arg); end
|
203
|
-
# def self.count; dataset.count; end
|
204
|
-
# def self.map(*arg, &block); dataset.map(*arg, &block); end
|
205
|
-
# def self.hash_column(column); dataset.hash_column(primary_key, column); end
|
206
|
-
# def self.join(*args); dataset.join(*args); end
|
207
|
-
# def self.lock(mode, &block); dataset.lock(mode, &block); end
|
208
197
|
def self.destroy_all
|
209
198
|
has_hooks?(:before_destroy) ? dataset.destroy : dataset.delete
|
210
199
|
end
|
@@ -222,6 +211,7 @@ module Sequel
|
|
222
211
|
db.transaction do
|
223
212
|
run_hooks(:before_destroy)
|
224
213
|
delete
|
214
|
+
run_hooks(:after_destroy)
|
225
215
|
end
|
226
216
|
end
|
227
217
|
|
@@ -252,6 +242,11 @@ module Sequel
|
|
252
242
|
respond_to?(m) ? send(m, *args, &block) : super(m, *args)
|
253
243
|
end
|
254
244
|
|
245
|
+
def self.join(*args)
|
246
|
+
table_name = dataset.opts[:from].first
|
247
|
+
dataset.join(*args).select(table_name.to_sym.ALL)
|
248
|
+
end
|
249
|
+
|
255
250
|
def db; self.class.db; end
|
256
251
|
|
257
252
|
def [](field); @values[field]; end
|
data/lib/sequel/postgres.rb
CHANGED
@@ -41,9 +41,17 @@ class PGconn
|
|
41
41
|
|
42
42
|
def last_insert_id(table)
|
43
43
|
@table_sequences ||= {}
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
if !@table_sequences.include?(table)
|
45
|
+
pkey_and_seq = pkey_and_sequence(table)
|
46
|
+
if pkey_and_seq
|
47
|
+
@table_sequences[table] = pkey_and_seq[1]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
if seq = @table_sequences[table]
|
51
|
+
r = async_query(SELECT_CURRVAL % seq)
|
52
|
+
return r[0][0].to_i unless r.nil? || r.empty?
|
53
|
+
end
|
54
|
+
nil # primary key sequence not found
|
47
55
|
end
|
48
56
|
|
49
57
|
# Shamelessly appropriated from ActiveRecord's Postgresql adapter.
|
@@ -74,13 +82,31 @@ class PGconn
|
|
74
82
|
AND cons.contype = 'p'
|
75
83
|
AND def.adsrc ~* 'nextval'
|
76
84
|
end_sql
|
85
|
+
|
86
|
+
SELECT_PK = <<-end_sql
|
87
|
+
SELECT pg_attribute.attname
|
88
|
+
FROM pg_class, pg_attribute, pg_index
|
89
|
+
WHERE pg_class.oid = pg_attribute.attrelid AND
|
90
|
+
pg_class.oid = pg_index.indrelid AND
|
91
|
+
pg_index.indkey[0] = pg_attribute.attnum AND
|
92
|
+
pg_index.indisprimary = 't' AND
|
93
|
+
pg_class.relname = '%s'
|
94
|
+
end_sql
|
77
95
|
|
78
96
|
def pkey_and_sequence(table)
|
79
97
|
r = async_query(SELECT_PK_AND_SERIAL_SEQUENCE % table)
|
80
98
|
return [r[0].first, r[0].last] unless r.nil? or r.empty?
|
81
99
|
|
82
100
|
r = async_query(SELECT_PK_AND_CUSTOM_SEQUENCE % table)
|
83
|
-
return [r.first, r.last] unless r.nil? or r.empty?
|
101
|
+
return [r[0].first, r[0].last] unless r.nil? or r.empty?
|
102
|
+
rescue
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def primary_key(table)
|
107
|
+
r = async_query(SELECT_PK % table)
|
108
|
+
pkey = r[0].first unless r.nil? or r.empty?
|
109
|
+
return pkey.to_sym if pkey
|
84
110
|
rescue
|
85
111
|
nil
|
86
112
|
end
|
@@ -164,11 +190,42 @@ module Sequel
|
|
164
190
|
raise e
|
165
191
|
end
|
166
192
|
|
167
|
-
def
|
193
|
+
def primary_key_for_table(conn, table)
|
194
|
+
@primary_keys ||= {}
|
195
|
+
@primary_keys[table] ||= conn.primary_key(table)
|
196
|
+
end
|
197
|
+
|
198
|
+
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session/.freeze
|
199
|
+
|
200
|
+
def insert_result(conn, table, values)
|
201
|
+
begin
|
202
|
+
result = conn.last_insert_id(table)
|
203
|
+
return result if result
|
204
|
+
rescue PGError => e
|
205
|
+
# An error could occur if the inserted values include a primary key
|
206
|
+
# value, while the primary key is serial.
|
207
|
+
if e.message =~ RE_CURRVAL_ERROR
|
208
|
+
raise SequelError, "Could not return primary key value for the inserted record. Are you specifying a primary key value for a serial primary key?"
|
209
|
+
else
|
210
|
+
raise e
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
case values
|
215
|
+
when Hash:
|
216
|
+
values[primary_key_for_table(conn, table)]
|
217
|
+
when Array:
|
218
|
+
values.first
|
219
|
+
else
|
220
|
+
nil
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def execute_insert(sql, table, values)
|
168
225
|
@logger.info(sql) if @logger
|
169
226
|
@pool.hold do |conn|
|
170
227
|
conn.execute(sql).clear
|
171
|
-
conn
|
228
|
+
insert_result(conn, table, values)
|
172
229
|
end
|
173
230
|
rescue => e
|
174
231
|
@logger.error(e.message) if @logger
|
@@ -314,7 +371,8 @@ module Sequel
|
|
314
371
|
end
|
315
372
|
|
316
373
|
def insert(*values)
|
317
|
-
@db.execute_insert(insert_sql(*values), @opts[:from]
|
374
|
+
@db.execute_insert(insert_sql(*values), @opts[:from],
|
375
|
+
values.size == 1 ? values.first : values)
|
318
376
|
end
|
319
377
|
|
320
378
|
def update(values, opts = nil)
|
data/lib/sequel/sqlite.rb
CHANGED
@@ -75,22 +75,16 @@ module Sequel
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def insert(*values)
|
78
|
-
@db.
|
79
|
-
@db.execute_insert insert_sql(*values)
|
80
|
-
end
|
78
|
+
@db.execute_insert insert_sql(*values)
|
81
79
|
end
|
82
80
|
|
83
81
|
def update(values, opts = nil)
|
84
|
-
@db.
|
85
|
-
@db.execute update_sql(values, opts)
|
86
|
-
end
|
82
|
+
@db.execute update_sql(values, opts)
|
87
83
|
self
|
88
84
|
end
|
89
85
|
|
90
86
|
def delete(opts = nil)
|
91
|
-
@db.
|
92
|
-
@db.execute delete_sql(opts)
|
93
|
-
end
|
87
|
+
@db.execute delete_sql(opts)
|
94
88
|
self
|
95
89
|
end
|
96
90
|
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: sequel
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2007-06-
|
6
|
+
version: 0.1.7
|
7
|
+
date: 2007-06-30 00:00:00 +03:00
|
8
8
|
summary: Concise ORM for Ruby.
|
9
9
|
require_paths:
|
10
10
|
- lib
|