sequel 0.1.5 → 0.1.6
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/core_ext.rb +10 -0
- data/lib/sequel/dataset.rb +39 -4
- data/lib/sequel/model.rb +27 -21
- data/lib/sequel/odbc.rb +1 -1
- data/lib/sequel/pretty_table.rb +3 -3
- data/lib/sequel/sqlite.rb +4 -1
- metadata +2 -3
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
*0.1.6*
|
2
|
+
|
3
|
+
* Fixed Model#method_missing to raise for an invalid attribute.
|
4
|
+
|
5
|
+
* Fixed PrettyTable to print model objects (thanks snok.)
|
6
|
+
|
7
|
+
* Fixed ODBC timestamp conversion to return DateTime rather than Time object (thanks snok.)
|
8
|
+
|
9
|
+
* Fixed Model.method_missing (thanks snok.)
|
10
|
+
|
11
|
+
* Model.method_missing now creates stubs for calling Model.dataset methods. Methods like Model.each etc are removed.
|
12
|
+
|
13
|
+
* Changed default join type to INNER JOIN (thanks snok.)
|
14
|
+
|
15
|
+
* Added support for literal expressions, e.g. DB[:items].filter(:col1 => 'col2 - 10'.expr).
|
16
|
+
|
17
|
+
* Added Dataset#and.
|
18
|
+
|
19
|
+
* SQLite adapter opens a memory DB if no database is specified, e.g. Sequel.open 'sqlite:/'.
|
20
|
+
|
21
|
+
* Added Dataset#or, pretty nifty.
|
22
|
+
|
1
23
|
*0.1.5*
|
2
24
|
|
3
25
|
* Fixed Dataset#join to support multiple joins. Added #left_outer_join, #right_outer_join, #full_outer_join, #inner_join methods.
|
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.6"
|
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/core_ext.rb
CHANGED
@@ -15,6 +15,11 @@ class Array
|
|
15
15
|
end
|
16
16
|
end
|
17
17
|
|
18
|
+
module Sequel
|
19
|
+
class ExpressionString < ::String
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
18
23
|
# String extensions
|
19
24
|
class String
|
20
25
|
# Converts a string into an SQL string by removing comments.
|
@@ -28,4 +33,9 @@ class String
|
|
28
33
|
def split_sql
|
29
34
|
to_sql.split(';').map {|s| s.strip}
|
30
35
|
end
|
36
|
+
|
37
|
+
# Convert a string into an Expression String
|
38
|
+
def expr
|
39
|
+
Sequel::ExpressionString.new(self)
|
40
|
+
end
|
31
41
|
end
|
data/lib/sequel/dataset.rb
CHANGED
@@ -112,6 +112,7 @@ module Sequel
|
|
112
112
|
# If an unsupported object is given, an exception is raised.
|
113
113
|
def literal(v)
|
114
114
|
case v
|
115
|
+
when ExpressionString: v
|
115
116
|
when String: "'#{v.gsub(/'/, "''")}'"
|
116
117
|
when Integer, Float: v.to_s
|
117
118
|
when NilClass: NULL
|
@@ -208,6 +209,9 @@ module Sequel
|
|
208
209
|
fmt = where.to_expressions.map {|e| format_expression(e.left, e.op, e.right)}.
|
209
210
|
join(AND_SEPARATOR)
|
210
211
|
else
|
212
|
+
# if the expression is compound, it should be parenthesized in order for
|
213
|
+
# things to be predictable (when using #or and #and.)
|
214
|
+
parenthesize |= where =~ /\).+\(/
|
211
215
|
fmt = where
|
212
216
|
end
|
213
217
|
parenthesize ? "(#{fmt})" : fmt
|
@@ -298,6 +302,32 @@ module Sequel
|
|
298
302
|
dup_merge(clause => expression_list(block || cond))
|
299
303
|
end
|
300
304
|
end
|
305
|
+
|
306
|
+
# Adds an alternate filter to an existing filter using OR. If no filter
|
307
|
+
# exists an error is raised.
|
308
|
+
def or(*cond, &block)
|
309
|
+
clause = (@opts[:group] ? :having : :where)
|
310
|
+
cond = cond.first if cond.size == 1
|
311
|
+
parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
|
312
|
+
if @opts[clause]
|
313
|
+
l = expression_list(@opts[clause])
|
314
|
+
r = expression_list(block || cond, parenthesize)
|
315
|
+
dup_merge(clause => "#{l} OR #{r}")
|
316
|
+
else
|
317
|
+
raise SequelError, "No existing filter found."
|
318
|
+
end
|
319
|
+
end
|
320
|
+
|
321
|
+
# Adds an further filter to an existing filter using AND. If no filter
|
322
|
+
# exists an error is raised. This method is identical to #filter except
|
323
|
+
# it expects an existing filter.
|
324
|
+
def and(*cond, &block)
|
325
|
+
clause = (@opts[:group] ? :having : :where)
|
326
|
+
unless @opts[clause]
|
327
|
+
raise SequelError, "No existing filter found."
|
328
|
+
end
|
329
|
+
filter(*cond, &block)
|
330
|
+
end
|
301
331
|
|
302
332
|
# Performs the inverse of Dataset#filter.
|
303
333
|
#
|
@@ -337,14 +367,20 @@ module Sequel
|
|
337
367
|
end
|
338
368
|
end
|
339
369
|
|
370
|
+
# Adds a UNION clause using a second dataset object. If all is true the
|
371
|
+
# clause used is UNION ALL, which may return duplicate rows.
|
340
372
|
def union(dataset, all = false)
|
341
373
|
dup_merge(:union => dataset, :union_all => all)
|
342
374
|
end
|
343
375
|
|
376
|
+
# Adds an INTERSECT clause using a second dataset object. If all is true
|
377
|
+
# the clause used is INTERSECT ALL, which may return duplicate rows.
|
344
378
|
def intersect(dataset, all = false)
|
345
379
|
dup_merge(:intersect => dataset, :intersect_all => all)
|
346
380
|
end
|
347
381
|
|
382
|
+
# Adds an EXCEPT clause using a second dataset object. If all is true the
|
383
|
+
# clause used is EXCEPT ALL, which may return duplicate rows.
|
348
384
|
def except(dataset, all = false)
|
349
385
|
dup_merge(:except => dataset, :except_all => all)
|
350
386
|
end
|
@@ -357,7 +393,7 @@ module Sequel
|
|
357
393
|
}
|
358
394
|
|
359
395
|
def join_expr(type, table, expr)
|
360
|
-
join_type = JOIN_TYPES[type || :
|
396
|
+
join_type = JOIN_TYPES[type || :inner]
|
361
397
|
unless join_type
|
362
398
|
raise SequelError, "Invalid join type: #{type}"
|
363
399
|
end
|
@@ -382,13 +418,12 @@ module Sequel
|
|
382
418
|
end
|
383
419
|
|
384
420
|
def left_outer_join(table, expr); join_table(:left_outer, table, expr); end
|
385
|
-
alias_method :join, :left_outer_join
|
386
421
|
def right_outer_join(table, expr); join_table(:right_outer, table, expr); end
|
387
422
|
def full_outer_join(table, expr); join_table(:full_outer, table, expr); end
|
388
423
|
def inner_join(table, expr); join_table(:inner, table, expr); end
|
424
|
+
alias_method :join, :inner_join
|
389
425
|
|
390
|
-
|
391
|
-
alias all to_a
|
426
|
+
alias_method :all, :to_a
|
392
427
|
|
393
428
|
# Maps field values for each record in the dataset (if a field name is
|
394
429
|
# given), or performs the stock mapping functionality of Enumerable.
|
data/lib/sequel/model.rb
CHANGED
@@ -21,9 +21,9 @@ module Sequel
|
|
21
21
|
def self.dataset
|
22
22
|
return @dataset if @dataset
|
23
23
|
if !table_name
|
24
|
-
raise
|
24
|
+
raise SequelError, "Table name not specified for #{self}."
|
25
25
|
elsif !db
|
26
|
-
raise
|
26
|
+
raise SequelError, "Database not specified for #{self}."
|
27
27
|
end
|
28
28
|
@dataset = db[table_name]
|
29
29
|
@dataset.model_class = self
|
@@ -190,21 +190,21 @@ module Sequel
|
|
190
190
|
|
191
191
|
def refresh
|
192
192
|
@values = self.class.dataset.naked[primary_key => @pkey] ||
|
193
|
-
(raise
|
193
|
+
(raise SequelError, "Record not found")
|
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
|
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
208
|
def self.destroy_all
|
209
209
|
has_hooks?(:before_destroy) ? dataset.destroy : dataset.delete
|
210
210
|
end
|
@@ -233,7 +233,7 @@ module Sequel
|
|
233
233
|
FILTER_BY_REGEXP = /^filter_by_(.*)/.freeze
|
234
234
|
ALL_BY_REGEXP = /^all_by_(.*)/.freeze
|
235
235
|
|
236
|
-
def self.method_missing(m, *args)
|
236
|
+
def self.method_missing(m, *args, &block)
|
237
237
|
Thread.exclusive do
|
238
238
|
method_name = m.to_s
|
239
239
|
if method_name =~ FIND_BY_REGEXP
|
@@ -245,9 +245,11 @@ module Sequel
|
|
245
245
|
elsif method_name =~ ALL_BY_REGEXP
|
246
246
|
c = $1
|
247
247
|
meta_def(method_name) {|arg| filter(c => arg).all}
|
248
|
+
elsif dataset.respond_to?(m)
|
249
|
+
instance_eval("def #{m}(*args, &block); dataset.#{m}(*args, &block); end")
|
248
250
|
end
|
249
251
|
end
|
250
|
-
respond_to?(m) ? send(m, *args) : super(m, *args)
|
252
|
+
respond_to?(m) ? send(m, *args, &block) : super(m, *args)
|
251
253
|
end
|
252
254
|
|
253
255
|
def db; self.class.db; end
|
@@ -259,13 +261,17 @@ module Sequel
|
|
259
261
|
WRITE_ATTR_REGEXP = /(.*)=$/.freeze
|
260
262
|
|
261
263
|
def method_missing(m, value = nil)
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
264
|
+
write = m.to_s =~ WRITE_ATTR_REGEXP
|
265
|
+
att = write ? $1.to_sym : m
|
266
|
+
# raise unless the att is recognized or this is a new unaved record
|
267
|
+
super unless @values.include?(att) || !@pkey
|
268
|
+
|
269
|
+
write ? (self[att] = value) : self[att]
|
267
270
|
end
|
268
271
|
|
272
|
+
def each(&block); @values.each(&block); end
|
273
|
+
def keys; @values.keys; end
|
274
|
+
|
269
275
|
def id; @values[:id]; end
|
270
276
|
|
271
277
|
def save
|
data/lib/sequel/odbc.rb
CHANGED
@@ -80,7 +80,7 @@ module Sequel
|
|
80
80
|
# ODBCColumn#mapSqlTypeToGenericType and Column#klass.
|
81
81
|
case v
|
82
82
|
when ::ODBC::TimeStamp
|
83
|
-
|
83
|
+
DateTime.new(v.year, v.month, v.day, v.hour, v.minute, v.second)
|
84
84
|
when ::ODBC::Time
|
85
85
|
DateTime.now
|
86
86
|
Time.gm(now.year, now.month, now.day, v.hour, v.minute, v.second)
|
data/lib/sequel/pretty_table.rb
CHANGED
@@ -23,9 +23,9 @@ module Sequel
|
|
23
23
|
sizes[c.to_sym] = s if s > sizes[c.to_sym]
|
24
24
|
end
|
25
25
|
records.each do |r|
|
26
|
-
|
27
|
-
s =
|
28
|
-
sizes[
|
26
|
+
columns.each do |c|
|
27
|
+
s = r[c].to_s.size
|
28
|
+
sizes[c.to_sym] = s if s > sizes[c.to_sym]
|
29
29
|
end
|
30
30
|
end
|
31
31
|
sizes
|
data/lib/sequel/sqlite.rb
CHANGED
@@ -11,7 +11,10 @@ module Sequel
|
|
11
11
|
set_adapter_scheme :sqlite
|
12
12
|
|
13
13
|
def connect
|
14
|
-
|
14
|
+
if @opts[:database].empty?
|
15
|
+
@opts[:database] = ':memory:'
|
16
|
+
end
|
17
|
+
db = ::SQLite3::Database.new(@opts[:database])
|
15
18
|
db.type_translation = true
|
16
19
|
db
|
17
20
|
end
|
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-
|
6
|
+
version: 0.1.6
|
7
|
+
date: 2007-06-01 00:00:00 +03:00
|
8
8
|
summary: Concise ORM for Ruby.
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -33,7 +33,6 @@ files:
|
|
33
33
|
- README
|
34
34
|
- Rakefile
|
35
35
|
- bin/sequel
|
36
|
-
- doc/rdoc
|
37
36
|
- lib/sequel
|
38
37
|
- lib/sequel.rb
|
39
38
|
- lib/sequel/connection_pool.rb
|