sequel 0.3.1 → 0.3.2
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 +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
|
|