sequel 0.1.5 → 0.1.6

Sign up to get free protection for your applications and to get access to all the features.
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.5"
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",
@@ -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
@@ -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 || :left_outer]
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 RuntimeError, "Table name not specified for class #{self}."
24
+ raise SequelError, "Table name not specified for #{self}."
25
25
  elsif !db
26
- raise RuntimeError, "No database connected."
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 RuntimeError, "Record not found")
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
- if m.to_s =~ WRITE_ATTR_REGEXP
263
- self[$1.to_sym] = value
264
- else
265
- self[m]
266
- end
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
- Time.gm(v.year, v.month, v.day, v.hour, v.minute, v.second)
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)
@@ -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
- r.each do |k, v|
27
- s = v.to_s.size
28
- sizes[k.to_sym] = s if s > sizes[k.to_sym]
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
- db = SQLite3::Database.new(@opts[:database])
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.5
7
- date: 2007-05-26 00:00:00 +03:00
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