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 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