sequel 0.3.1 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +28 -0
- data/Rakefile +1 -1
- data/lib/sequel/ado.rb +12 -5
- data/lib/sequel/array_keys.rb +61 -2
- data/lib/sequel/connection_pool.rb +6 -0
- data/lib/sequel/core_ext.rb +18 -14
- data/lib/sequel/database.rb +40 -4
- data/lib/sequel/dataset.rb +9 -3
- data/lib/sequel/dataset/convenience.rb +66 -19
- data/lib/sequel/dataset/sequelizer.rb +4 -4
- data/lib/sequel/dataset/sql.rb +41 -42
- data/lib/sequel/db2.rb +160 -0
- data/lib/sequel/dbi.rb +1 -1
- data/lib/sequel/error.rb +11 -0
- data/lib/sequel/model.rb +28 -22
- data/lib/sequel/model/record.rb +3 -1
- data/lib/sequel/mysql.rb +9 -9
- data/lib/sequel/odbc.rb +1 -1
- data/lib/sequel/oracle.rb +2 -2
- data/lib/sequel/postgres.rb +18 -20
- data/lib/sequel/pretty_table.rb +2 -2
- data/lib/sequel/schema/schema_sql.rb +3 -3
- data/lib/sequel/sqlite.rb +4 -0
- data/spec/adapters/mysql_spec.rb +1 -1
- data/spec/adapters/sqlite_spec.rb +8 -0
- data/spec/core_ext_spec.rb +23 -23
- data/spec/dataset_spec.rb +204 -19
- data/spec/model_spec.rb +71 -1
- data/spec/schema_generator_spec.rb +3 -3
- metadata +4 -3
- data/lib/sequel/expressions.rb +0 -76
data/lib/sequel/dbi.rb
CHANGED
data/lib/sequel/error.rb
CHANGED
@@ -1,10 +1,21 @@
|
|
1
|
+
# Represents an error raised in Sequel code.
|
1
2
|
class SequelError < StandardError
|
2
3
|
end
|
3
4
|
|
5
|
+
# SequelRollbackError is a special error used to rollback a transactions.
|
6
|
+
# A transaction block will catch this error and wont pass further up the stack.
|
4
7
|
class SequelRollbackError < StandardError
|
5
8
|
end
|
6
9
|
|
10
|
+
# Object extensions
|
7
11
|
class Object
|
12
|
+
# Cancels the current transaction without an error:
|
13
|
+
#
|
14
|
+
# DB.tranaction do
|
15
|
+
# ...
|
16
|
+
# rollback! if failed_to_contact_client
|
17
|
+
# ...
|
18
|
+
# end
|
8
19
|
def rollback!
|
9
20
|
raise SequelRollbackError
|
10
21
|
end
|
data/lib/sequel/model.rb
CHANGED
@@ -79,12 +79,21 @@ module Sequel
|
|
79
79
|
#
|
80
80
|
# post = Post.create(:title => 'hello world')
|
81
81
|
#
|
82
|
-
#
|
82
|
+
# Another way is to construct a new instance and save it:
|
83
83
|
#
|
84
84
|
# post = Post.new
|
85
85
|
# post.title = 'hello world'
|
86
86
|
# post.save
|
87
87
|
#
|
88
|
+
# You can also supply a block to Model.new and Model.create:
|
89
|
+
#
|
90
|
+
# post = Post.create {|p| p.title = 'hello world'}
|
91
|
+
#
|
92
|
+
# post = Post.new do |p|
|
93
|
+
# p.title = 'hello world'
|
94
|
+
# p.save
|
95
|
+
# end
|
96
|
+
#
|
88
97
|
# === Hooks
|
89
98
|
#
|
90
99
|
# You can execute custom code when creating, updating, or deleting records by using hooks. The before_create and after_create hooks wrap record creation. The before_update and after_update wrap record updating. The before_save and after_save wrap record creation and updating. The before_destroy and after_destroy wrap destruction.
|
@@ -127,11 +136,14 @@ module Sequel
|
|
127
136
|
# def posts; Post.filter(:author_id => pk); end
|
128
137
|
# end
|
129
138
|
#
|
130
|
-
# Sequel also provides two macros to assist with common types of associations. The one_to_one macro is roughly equivalent to ActiveRecord's belongs_to macro:
|
139
|
+
# Sequel also provides two macros to assist with common types of associations. The one_to_one macro is roughly equivalent to ActiveRecord?'s belongs_to macro. It defines both getter and setter methods for the association:
|
131
140
|
#
|
132
141
|
# class Post < Sequel::Model(:posts)
|
133
142
|
# one_to_one :author, :from => Author
|
134
143
|
# end
|
144
|
+
#
|
145
|
+
# post = Post.create(:name => 'hi!')
|
146
|
+
# post.author = Author[:name => 'Sharon']
|
135
147
|
#
|
136
148
|
# The one_to_many macro is roughly equivalent to ActiveRecord's has_many macro:
|
137
149
|
#
|
@@ -270,24 +282,18 @@ module Sequel
|
|
270
282
|
def self.delete_all
|
271
283
|
dataset.delete
|
272
284
|
end
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
285
|
+
|
286
|
+
def self.is_dataset_magic_method?(m)
|
287
|
+
method_name = m.to_s
|
288
|
+
Sequel::Dataset::MAGIC_METHODS.each_key do |r|
|
289
|
+
return true if method_name =~ r
|
290
|
+
end
|
291
|
+
false
|
292
|
+
end
|
293
|
+
|
278
294
|
def self.method_missing(m, *args, &block) #:nodoc:
|
279
295
|
Thread.exclusive do
|
280
|
-
|
281
|
-
if method_name =~ FIND_BY_REGEXP
|
282
|
-
c = $1.to_sym
|
283
|
-
meta_def(method_name) {|arg| find(c => arg)}
|
284
|
-
elsif method_name =~ FILTER_BY_REGEXP
|
285
|
-
c = $1.to_sym
|
286
|
-
meta_def(method_name) {|arg| filter(c => arg)}
|
287
|
-
elsif method_name =~ ALL_BY_REGEXP
|
288
|
-
c = $1.to_sym
|
289
|
-
meta_def(method_name) {|arg| filter(c => arg).all}
|
290
|
-
elsif dataset.respond_to?(m)
|
296
|
+
if dataset.respond_to?(m) || is_dataset_magic_method?(m)
|
291
297
|
instance_eval("def #{m}(*args, &block); dataset.#{m}(*args, &block); end")
|
292
298
|
end
|
293
299
|
end
|
@@ -301,12 +307,12 @@ module Sequel
|
|
301
307
|
end
|
302
308
|
|
303
309
|
# Returns value of attribute.
|
304
|
-
def [](
|
305
|
-
@values[
|
310
|
+
def [](column)
|
311
|
+
@values[column]
|
306
312
|
end
|
307
313
|
# Sets value of attribute.
|
308
|
-
def []=(
|
309
|
-
@values[
|
314
|
+
def []=(column, value)
|
315
|
+
@values[column] = value
|
310
316
|
end
|
311
317
|
|
312
318
|
# Enumerates through all attributes.
|
data/lib/sequel/model/record.rb
CHANGED
@@ -133,7 +133,7 @@ module Sequel
|
|
133
133
|
#
|
134
134
|
# This method guesses whether the record exists when
|
135
135
|
# <tt>new_record</tt> is set to false.
|
136
|
-
def initialize(values = {}, new_record = false)
|
136
|
+
def initialize(values = {}, new_record = false, &block)
|
137
137
|
@values = values
|
138
138
|
|
139
139
|
@new = new_record
|
@@ -144,6 +144,8 @@ module Sequel
|
|
144
144
|
# we regard this instance as new.
|
145
145
|
@new = (k == nil) || (!(Array === k) && !@values[k])
|
146
146
|
end
|
147
|
+
|
148
|
+
block[self] if block
|
147
149
|
end
|
148
150
|
|
149
151
|
# Returns true if the current instance represents a new record.
|
data/lib/sequel/mysql.rb
CHANGED
@@ -61,7 +61,7 @@ class Mysql::Result
|
|
61
61
|
row[i] = v.send(t)
|
62
62
|
end
|
63
63
|
end
|
64
|
-
row.
|
64
|
+
row.keys = c
|
65
65
|
yield row
|
66
66
|
end
|
67
67
|
end
|
@@ -173,21 +173,21 @@ module Sequel
|
|
173
173
|
|
174
174
|
class Dataset < Sequel::Dataset
|
175
175
|
UNQUOTABLE_FIELD_RE = /^(`(.+)`)|\*$/.freeze
|
176
|
-
def
|
176
|
+
def quote_column(f)
|
177
177
|
(f.nil? || f.empty? || f =~ UNQUOTABLE_FIELD_RE) ? f : "`#{f}`"
|
178
178
|
end
|
179
179
|
|
180
180
|
FIELD_EXPR_RE = /^([^\(]+\()?([^\.]+\.)?([^\s\)]+)?(\))?(\sAS\s(.+))?$/i.freeze
|
181
181
|
FIELD_ORDER_RE = /^(.*) (DESC|ASC)$/i.freeze
|
182
|
-
def
|
182
|
+
def quoted_column_name(name)
|
183
183
|
case name
|
184
184
|
when FIELD_EXPR_RE:
|
185
185
|
$6 ? \
|
186
|
-
"#{$1}#{$2}#{
|
187
|
-
"#{$1}#{$2}#{
|
188
|
-
when FIELD_ORDER_RE: "#{
|
186
|
+
"#{$1}#{$2}#{quote_column($3)}#{$4} AS #{quote_column($6)}" : \
|
187
|
+
"#{$1}#{$2}#{quote_column($3)}#{$4}"
|
188
|
+
when FIELD_ORDER_RE: "#{quote_column($1)} #{$2}"
|
189
189
|
else
|
190
|
-
|
190
|
+
quote_column(name)
|
191
191
|
end
|
192
192
|
end
|
193
193
|
|
@@ -196,7 +196,7 @@ module Sequel
|
|
196
196
|
|
197
197
|
def literal(v)
|
198
198
|
case v
|
199
|
-
when LiteralString:
|
199
|
+
when LiteralString: quoted_column_name(v)
|
200
200
|
when String: "'#{v.gsub(/'|\\/, '\&\&')}'"
|
201
201
|
when true: TRUE
|
202
202
|
when false: FALSE
|
@@ -223,7 +223,7 @@ module Sequel
|
|
223
223
|
opts = opts ? @opts.merge(opts) : @opts
|
224
224
|
|
225
225
|
if order = opts[:order]
|
226
|
-
sql << " ORDER BY #{
|
226
|
+
sql << " ORDER BY #{column_list(order)}"
|
227
227
|
end
|
228
228
|
|
229
229
|
if limit = opts[:limit]
|
data/lib/sequel/odbc.rb
CHANGED
@@ -105,7 +105,7 @@ module Sequel
|
|
105
105
|
end
|
106
106
|
|
107
107
|
def array_tuples_make_row(row)
|
108
|
-
row.
|
108
|
+
row.keys = @columns
|
109
109
|
row.each_with_index do |v, idx|
|
110
110
|
# When fetching a result set, the Ruby ODBC driver converts all ODBC
|
111
111
|
# SQL types to an equivalent Ruby type; with the exception of
|
data/lib/sequel/oracle.rb
CHANGED
@@ -14,7 +14,7 @@ module Sequel
|
|
14
14
|
# def auto_increment_sql
|
15
15
|
# AUTO_INCREMENT
|
16
16
|
# end
|
17
|
-
|
17
|
+
|
18
18
|
def connect
|
19
19
|
if @opts[:database]
|
20
20
|
dbname = @opts[:host] ? \
|
@@ -38,7 +38,7 @@ module Sequel
|
|
38
38
|
|
39
39
|
def execute(sql)
|
40
40
|
@logger.info(sql) if @logger
|
41
|
-
@pool.hold {|conn| conn.
|
41
|
+
@pool.hold {|conn| conn.exec(sql)}
|
42
42
|
end
|
43
43
|
|
44
44
|
alias_method :do, :execute
|
data/lib/sequel/postgres.rb
CHANGED
@@ -419,31 +419,30 @@ module Sequel
|
|
419
419
|
@@converters = {}
|
420
420
|
|
421
421
|
def row_converter(result)
|
422
|
-
|
422
|
+
@columns = []; translators = []
|
423
423
|
result.fields.each_with_index do |f, idx|
|
424
|
-
|
424
|
+
@columns << f.to_sym
|
425
425
|
translators << PG_TYPES[result.type(idx)]
|
426
426
|
end
|
427
|
-
@columns = fields
|
428
427
|
|
429
428
|
# create result signature and memoize the converter
|
430
|
-
sig = [
|
429
|
+
sig = [@columns, translators].hash
|
431
430
|
@@converters_mutex.synchronize do
|
432
|
-
@@converters[sig] ||= compile_converter(
|
431
|
+
@@converters[sig] ||= compile_converter(@columns, translators)
|
433
432
|
end
|
434
433
|
end
|
435
434
|
|
436
|
-
def compile_converter(
|
437
|
-
|
435
|
+
def compile_converter(columns, translators)
|
436
|
+
used_columns = []
|
438
437
|
kvs = []
|
439
|
-
|
440
|
-
next if
|
441
|
-
|
438
|
+
columns.each_with_index do |column, idx|
|
439
|
+
next if used_columns.include?(column)
|
440
|
+
used_columns << column
|
442
441
|
|
443
442
|
if !AUTO_TRANSLATE and translator = translators[idx]
|
444
|
-
kvs << ":\"#{
|
443
|
+
kvs << ":\"#{column}\" => ((t = r[#{idx}]) ? t.#{translator} : nil)"
|
445
444
|
else
|
446
|
-
kvs << ":\"#{
|
445
|
+
kvs << ":\"#{column}\" => r[#{idx}]"
|
447
446
|
end
|
448
447
|
end
|
449
448
|
eval("lambda {|r| {#{kvs.join(COMMA_SEPARATOR)}}}")
|
@@ -465,28 +464,27 @@ module Sequel
|
|
465
464
|
@@array_tuples_converters = {}
|
466
465
|
|
467
466
|
def array_tuples_row_converter(result)
|
468
|
-
|
467
|
+
@columns = []; translators = []
|
469
468
|
result.fields.each_with_index do |f, idx|
|
470
|
-
|
469
|
+
@columns << f.to_sym
|
471
470
|
translators << PG_TYPES[result.type(idx)]
|
472
471
|
end
|
473
|
-
@columns = fields
|
474
472
|
|
475
473
|
# create result signature and memoize the converter
|
476
|
-
sig = [
|
474
|
+
sig = [@columns, translators].hash
|
477
475
|
@@array_tuples_converters_mutex.synchronize do
|
478
|
-
@@array_tuples_converters[sig] ||= array_tuples_compile_converter(
|
476
|
+
@@array_tuples_converters[sig] ||= array_tuples_compile_converter(@columns, translators)
|
479
477
|
end
|
480
478
|
end
|
481
479
|
|
482
|
-
def array_tuples_compile_converter(
|
480
|
+
def array_tuples_compile_converter(columns, translators)
|
483
481
|
tr = []
|
484
|
-
|
482
|
+
columns.each_with_index do |column, idx|
|
485
483
|
if !AUTO_TRANSLATE and t = translators[idx]
|
486
484
|
tr << "if (v = r[#{idx}]); r[#{idx}] = v.#{t}; end"
|
487
485
|
end
|
488
486
|
end
|
489
|
-
eval("lambda {|r| r.
|
487
|
+
eval("lambda {|r| r.keys = columns; #{tr.join(';')}; r}")
|
490
488
|
end
|
491
489
|
end
|
492
490
|
end
|
data/lib/sequel/pretty_table.rb
CHANGED
@@ -10,8 +10,8 @@ module Sequel
|
|
10
10
|
def self.records_columns(records)
|
11
11
|
columns = []
|
12
12
|
records.each do |r|
|
13
|
-
if Array === r && (
|
14
|
-
return
|
13
|
+
if Array === r && (k = r.keys)
|
14
|
+
return k
|
15
15
|
elsif Hash === r
|
16
16
|
r.keys.each {|k| columns << k unless columns.include?(k)}
|
17
17
|
end
|
@@ -60,12 +60,12 @@ module Sequel
|
|
60
60
|
end
|
61
61
|
|
62
62
|
def index_definition_sql(table_name, index)
|
63
|
-
|
63
|
+
columns = index[:columns].join(COMMA_SEPARATOR)
|
64
64
|
index_name = index[:name] || default_index_name(table_name, index[:columns])
|
65
65
|
if index[:unique]
|
66
|
-
"CREATE UNIQUE INDEX #{index_name} ON #{table_name} (#{
|
66
|
+
"CREATE UNIQUE INDEX #{index_name} ON #{table_name} (#{columns});"
|
67
67
|
else
|
68
|
-
"CREATE INDEX #{index_name} ON #{table_name} (#{
|
68
|
+
"CREATE INDEX #{index_name} ON #{table_name} (#{columns});"
|
69
69
|
end
|
70
70
|
end
|
71
71
|
|
data/lib/sequel/sqlite.rb
CHANGED
@@ -19,6 +19,10 @@ module Sequel
|
|
19
19
|
end
|
20
20
|
db = ::SQLite3::Database.new(@opts[:database])
|
21
21
|
db.type_translation = true
|
22
|
+
# fix for timestamp translation
|
23
|
+
db.translator.add_translator("timestamp") do |t, v|
|
24
|
+
v =~ /^\d+$/ ? Time.at(v.to_i) : Time.parse(v)
|
25
|
+
end
|
22
26
|
db
|
23
27
|
end
|
24
28
|
|
data/spec/adapters/mysql_spec.rb
CHANGED
@@ -78,7 +78,7 @@ context "A MySQL dataset" do
|
|
78
78
|
proc {@d.literal(false)}.should_not raise_error
|
79
79
|
end
|
80
80
|
|
81
|
-
specify "should quote
|
81
|
+
specify "should quote columns using back-ticks" do
|
82
82
|
@d.select(:name).sql.should == \
|
83
83
|
'SELECT `name` FROM items'
|
84
84
|
|
@@ -6,6 +6,7 @@ SQLITE_DB.create_table :items do
|
|
6
6
|
text :name
|
7
7
|
float :value
|
8
8
|
end
|
9
|
+
SQLITE_DB.create_table(:time) {timestamp :t}
|
9
10
|
|
10
11
|
context "An SQLite database" do
|
11
12
|
setup do
|
@@ -120,6 +121,13 @@ context "An SQLite database" do
|
|
120
121
|
@db.disconnect
|
121
122
|
@db.pool.size.should == 0
|
122
123
|
end
|
124
|
+
|
125
|
+
specify "should support timestamps" do
|
126
|
+
t1 = Time.at(Time.now.to_i) #normalize time
|
127
|
+
|
128
|
+
SQLITE_DB[:time] << {:t => t1}
|
129
|
+
SQLITE_DB[:time].first[:t].should == t1
|
130
|
+
end
|
123
131
|
end
|
124
132
|
|
125
133
|
context "An SQLite dataset" do
|
data/spec/core_ext_spec.rb
CHANGED
@@ -127,47 +127,47 @@ context "Symbol#ALL" do
|
|
127
127
|
end
|
128
128
|
end
|
129
129
|
|
130
|
-
context "Symbol#
|
130
|
+
context "Symbol#to_column_name" do
|
131
131
|
specify "should convert qualified symbol notation into dot notation" do
|
132
|
-
:abc__def.
|
132
|
+
:abc__def.to_column_name.should == 'abc.def'
|
133
133
|
end
|
134
134
|
|
135
135
|
specify "should convert AS symbol notation into SQL AS notation" do
|
136
|
-
:xyz___x.
|
137
|
-
:abc__def___x.
|
136
|
+
:xyz___x.to_column_name.should == 'xyz AS x'
|
137
|
+
:abc__def___x.to_column_name.should == 'abc.def AS x'
|
138
138
|
end
|
139
139
|
|
140
140
|
specify "should support names with digits" do
|
141
|
-
:abc2.
|
142
|
-
:xx__yy3.
|
143
|
-
:ab34__temp3_4ax.
|
144
|
-
:x1___y2.
|
145
|
-
:abc2__def3___ggg4.
|
141
|
+
:abc2.to_column_name.should == 'abc2'
|
142
|
+
:xx__yy3.to_column_name.should == 'xx.yy3'
|
143
|
+
:ab34__temp3_4ax.to_column_name.should == 'ab34.temp3_4ax'
|
144
|
+
:x1___y2.to_column_name.should == 'x1 AS y2'
|
145
|
+
:abc2__def3___ggg4.to_column_name.should == 'abc2.def3 AS ggg4'
|
146
146
|
end
|
147
147
|
|
148
148
|
specify "should support upper case and lower case" do
|
149
|
-
:ABC.
|
150
|
-
:Zvashtoy__aBcD.
|
149
|
+
:ABC.to_column_name.should == 'ABC'
|
150
|
+
:Zvashtoy__aBcD.to_column_name.should == 'Zvashtoy.aBcD'
|
151
151
|
|
152
152
|
end
|
153
153
|
end
|
154
154
|
|
155
|
-
context "FieldCompositionMethods#
|
156
|
-
specify "should return the
|
157
|
-
:xyz.
|
158
|
-
:abc__xyz.
|
155
|
+
context "FieldCompositionMethods#column_title" do
|
156
|
+
specify "should return the column name for non aliased columns" do
|
157
|
+
:xyz.column_title.should == 'xyz'
|
158
|
+
:abc__xyz.column_title.should == 'xyz'
|
159
159
|
|
160
|
-
'abc'.
|
161
|
-
'abc.def'.
|
160
|
+
'abc'.column_title.should == 'abc'
|
161
|
+
'abc.def'.column_title.should == 'def'
|
162
162
|
end
|
163
163
|
|
164
|
-
specify "should return the
|
165
|
-
:xyz___x.
|
166
|
-
:abc__xyz___y.
|
164
|
+
specify "should return the column alias for aliased columns" do
|
165
|
+
:xyz___x.column_title.should == 'x'
|
166
|
+
:abc__xyz___y.column_title.should == 'y'
|
167
167
|
|
168
|
-
'abc AS x'.
|
169
|
-
'abc as y'.
|
170
|
-
'abc.def AS d'.
|
168
|
+
'abc AS x'.column_title.should == 'x'
|
169
|
+
'abc as y'.column_title.should == 'y'
|
170
|
+
'abc.def AS d'.column_title.should == 'd'
|
171
171
|
end
|
172
172
|
end
|
173
173
|
|