sequel 0.0.20 → 0.1.0

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.0*
2
+
3
+ * Changed Database#create_table to only accept a block. Nobody's gonna use the other way.
4
+
5
+ * Removed Dataset#[]= method. Too confusing and not really useful.
6
+
7
+ * Fixed ConnectionPool#hold to wrap exceptions only once.
8
+
9
+ * Dataset#where_list Renamed Dataset#expression_list.
10
+
11
+ * Added support for qualified fields in Proc expressions (e.g. filter {items.id == 1}.)
12
+
13
+ * Added like? and in? Proc expression operators.
14
+
15
+ * Added require 'date' in dataset.rb. Is this a 1.8.5 thing?
16
+
17
+ * Refactored Dataset to use literal strings instead of format strings (slight performance improvement and better readability.)
18
+
19
+ * Added support for literalizing Date objects.
20
+
21
+ * Refactored literalization of Time objects.
22
+
1
23
  *0.0.20*
2
24
 
3
25
  * Refactored Dataset where clause construction to use expressions.
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
6
6
  include FileUtils
7
7
 
8
8
  NAME = "sequel"
9
- VERS = "0.0.20"
9
+ VERS = "0.1.0"
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.rb CHANGED
@@ -11,6 +11,15 @@ require File.join(dir, 'model')
11
11
 
12
12
  module Sequel #:nodoc:
13
13
  class << self
14
+ # call-seq:
15
+ # Sequel::Database.connect(conn_string)
16
+ # Sequel.connect(conn_string)
17
+ # Sequel.open(conn_string)
18
+ #
19
+ # Creates a new database object based on the supplied connection string.
20
+ # The specified scheme determines the database class used, and the rest
21
+ # of the string specifies the connection options. For example:
22
+ # DB = Sequel.open 'sqlite:///blog.db'
14
23
  def connect(url)
15
24
  Database.connect(url)
16
25
  end
@@ -65,6 +65,8 @@ module Sequel
65
65
  ensure
66
66
  release(t)
67
67
  end
68
+ rescue SequelConnectionError => e
69
+ raise e
68
70
  rescue Exception => e
69
71
  raise SequelConnectionError.new(e)
70
72
  end
@@ -1,13 +1,3 @@
1
- # Time extensions.
2
- class Time
3
- SQL_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S'".freeze
4
-
5
- # Formats the Time object as an SQL TIMESTAMP.
6
- def to_sql_timestamp
7
- strftime(SQL_FORMAT)
8
- end
9
- end
10
-
11
1
  # Enumerable extensions.
12
2
  module Enumerable
13
3
  def send_each(sym, *args)
@@ -57,14 +57,10 @@ module Sequel
57
57
  # column :content, :text
58
58
  # index :title
59
59
  # end
60
- def create_table(name, columns = nil, indexes = nil, &block)
61
- if block
62
- schema = Schema.new
63
- schema.create_table(name, &block)
64
- schema.create(self)
65
- else
66
- execute Schema.create_table_sql(name, columns, indexes)
67
- end
60
+ def create_table(name, &block)
61
+ schema = Schema.new
62
+ schema.create_table(name, &block)
63
+ schema.create(self)
68
64
  end
69
65
 
70
66
  # Drops a table.
@@ -150,11 +146,11 @@ module Sequel
150
146
  # Creates a new database object based on the supplied connection string.
151
147
  # The specified scheme determines the database class used, and the rest
152
148
  # of the string specifies the connection options. For example:
153
- # DB = Sequel.connect('sqlite:///blog.db')
149
+ # DB = Sequel.open 'sqlite:///blog.db'
154
150
  def self.connect(conn_string)
155
151
  uri = URI.parse(conn_string)
156
152
  c = @@adapters[uri.scheme.to_sym]
157
- raise "Invalid database scheme" unless c
153
+ raise SequelError, "Invalid database scheme" unless c
158
154
  c.new(c.uri_to_options(uri))
159
155
  end
160
156
  end
@@ -1,4 +1,5 @@
1
1
  require 'time'
2
+ require 'date'
2
3
 
3
4
  module Sequel
4
5
  # A Dataset represents a view of a the data in a database, constrained by
@@ -67,13 +68,12 @@ module Sequel
67
68
  end
68
69
 
69
70
  QUALIFIED_REGEXP = /(.*)\.(.*)/.freeze
70
- QUALIFIED_FORMAT = "%s.%s".freeze
71
71
 
72
72
  # Returns a qualified field name (including a table name) if the field
73
73
  # name isn't already qualified.
74
74
  def qualified_field_name(field, table)
75
75
  fn = field_name(field)
76
- fn =~ QUALIFIED_REGEXP ? fn : QUALIFIED_FORMAT % [table, fn]
76
+ fn =~ QUALIFIED_REGEXP ? fn : "#{table}.#{fn}"
77
77
  end
78
78
 
79
79
  WILDCARD = '*'.freeze
@@ -96,7 +96,8 @@ module Sequel
96
96
  end
97
97
 
98
98
  NULL = "NULL".freeze
99
- SUBQUERY = "(%s)".freeze
99
+ TIMESTAMP_FORMAT = "TIMESTAMP '%Y-%m-%d %H:%M:%S'".freeze
100
+ DATE_FORMAT = "DATE '%Y-%m-%d'".freeze
100
101
 
101
102
  # Returns a literal representation of a value to be used as part
102
103
  # of an SQL expression. This method is overriden in descendants.
@@ -107,32 +108,30 @@ module Sequel
107
108
  when NilClass: NULL
108
109
  when Symbol: v.to_field_name
109
110
  when Array: v.empty? ? NULL : v.map {|i| literal(i)}.join(COMMA_SEPARATOR)
110
- when Dataset: SUBQUERY % v.sql
111
+ when Time: v.strftime(TIMESTAMP_FORMAT)
112
+ when Date: v.strftime(DATE_FORMAT)
113
+ when Dataset: "(#{v.sql})"
111
114
  else
112
115
  raise SequelError, "can't express #{v.inspect}:#{v.class} as a SQL literal"
113
116
  end
114
117
  end
115
118
 
116
119
  AND_SEPARATOR = " AND ".freeze
117
- EQUAL_EXPR = "(%s = %s)".freeze
118
- IN_EXPR = "(%s IN (%s))".freeze
119
- INCLUSIVE_RANGE_EXPR = "(%s >= %s AND %s <= %s)".freeze
120
- EXCLUSIVE_RANGE_EXPR = "(%s >= %s AND %s < %s)".freeze
121
- NULL_EXPR = "(%s IS NULL)".freeze
122
120
 
123
121
  def format_eq_expression(left, right)
124
122
  case right
125
123
  when Range:
126
- (right.exclude_end? ? EXCLUSIVE_RANGE_EXPR : INCLUSIVE_RANGE_EXPR) %
127
- [left, literal(right.begin), left, literal(right.end)]
124
+ right.exclude_end? ? \
125
+ "(#{left} >= #{right.begin} AND #{left} < #{right.end})" : \
126
+ "(#{left} >= #{right.begin} AND #{left} <= #{right.end})"
128
127
  when Array:
129
- IN_EXPR % [left, literal(right)]
130
- when NilClass:
131
- NULL_EXPR % left
128
+ "(#{left} IN (#{literal(right)}))"
132
129
  when Dataset:
133
- IN_EXPR % [left, right.sql]
130
+ "(#{left} IN #{literal(right)})"
131
+ when NilClass:
132
+ "(#{left} IS NULL)"
134
133
  else
135
- EQUAL_EXPR % [left, literal(right)]
134
+ "(#{left} = #{literal(right)})"
136
135
  end
137
136
  end
138
137
 
@@ -151,12 +150,16 @@ module Sequel
151
150
  "(#{left} > #{literal(right)})"
152
151
  when :gte:
153
152
  "(#{left} >= #{literal(right)})"
153
+ when :like:
154
+ "(#{left} LIKE #{literal(right)})"
155
+ else
156
+ raise SequelError, "Invalid operator specified: #{op}"
154
157
  end
155
158
  end
156
159
 
157
160
  # Formats a where clause. If parenthesize is true, then the whole
158
161
  # generated clause will be enclosed in a set of parentheses.
159
- def where_list(where, parenthesize = false)
162
+ def expression_list(where, parenthesize = false)
160
163
  case where
161
164
  when Hash:
162
165
  parenthesize = false if where.size == 1
@@ -177,9 +180,9 @@ module Sequel
177
180
  # Formats a join condition.
178
181
  def join_cond_list(cond, join_table)
179
182
  cond.map do |kv|
180
- EQUAL_EXPR % [
181
- qualified_field_name(kv[0], join_table),
182
- qualified_field_name(kv[1], @opts[:from])]
183
+ l = qualified_field_name(kv[0], join_table)
184
+ r = qualified_field_name(kv[1], @opts[:from])
185
+ "(#{l} = #{r})"
183
186
  end.join(AND_SEPARATOR)
184
187
  end
185
188
 
@@ -229,8 +232,6 @@ module Sequel
229
232
  dup_merge(:group => fields)
230
233
  end
231
234
 
232
- AND_WHERE = "%s AND %s".freeze
233
-
234
235
  # Returns a copy of the dataset with the given conditions imposed upon it.
235
236
  # If the query has been grouped, then the conditions are imposed in the
236
237
  # HAVING clause. If not, then they are imposed in the WHERE clause.
@@ -239,24 +240,24 @@ module Sequel
239
240
  cond = cond.first if cond.size == 1
240
241
  parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
241
242
  if @opts[clause]
242
- cond = AND_WHERE % [where_list(@opts[clause]), where_list(block || cond, parenthesize)]
243
- dup_merge(clause => cond)
243
+ l = expression_list(@opts[clause])
244
+ r = expression_list(block || cond, parenthesize)
245
+ dup_merge(clause => "#{l} AND #{r}")
244
246
  else
245
- dup_merge(clause => where_list(block || cond))
247
+ dup_merge(clause => expression_list(block || cond))
246
248
  end
247
249
  end
248
250
 
249
- NOT_WHERE = "NOT %s".freeze
250
- AND_NOT_WHERE = "%s AND NOT %s".freeze
251
-
252
251
  def exclude(*cond, &block)
253
252
  clause = (@opts[:group] ? :having : :where)
254
253
  cond = cond.first if cond.size == 1
255
254
  parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
256
255
  if @opts[clause]
257
- cond = AND_NOT_WHERE % [where_list(@opts[clause]), where_list(block || cond, parenthesize)]
256
+ l = expression_list(@opts[clause])
257
+ r = expression_list(block || cond, parenthesize)
258
+ cond = "#{l} AND NOT #{r}"
258
259
  else
259
- cond = NOT_WHERE % where_list(block || cond, true)
260
+ cond = "NOT #{expression_list(block || cond, true)}"
260
261
  end
261
262
  dup_merge(clause => cond)
262
263
  end
@@ -286,9 +287,10 @@ module Sequel
286
287
  RIGHT_OUTER_JOIN = 'RIGHT OUTER JOIN'.freeze
287
288
  FULL_OUTER_JOIN = 'FULL OUTER JOIN'.freeze
288
289
 
289
- def join(table, cond)
290
+ def join(table, expr)
291
+ expr = {expr => :id} unless expr.is_a?(Hash)
290
292
  dup_merge(:join_type => LEFT_OUTER_JOIN, :join_table => table,
291
- :join_cond => cond)
293
+ :join_cond => expr)
292
294
  end
293
295
 
294
296
  alias_method :all, :to_a
@@ -323,16 +325,6 @@ module Sequel
323
325
  end
324
326
  end
325
327
 
326
- SELECT = "SELECT %s FROM %s".freeze
327
- SELECT_DISTINCT = "SELECT DISTINCT %s FROM %s".freeze
328
- LIMIT = " LIMIT %s".freeze
329
- OFFSET = " OFFSET %s".freeze
330
- ORDER = " ORDER BY %s".freeze
331
- WHERE = " WHERE %s".freeze
332
- GROUP = " GROUP BY %s".freeze
333
- HAVING = " HAVING %s".freeze
334
- JOIN_CLAUSE = " %s %s ON %s".freeze
335
-
336
328
  EMPTY = ''.freeze
337
329
 
338
330
  SPACE = ' '.freeze
@@ -343,34 +335,36 @@ module Sequel
343
335
  fields = opts[:select]
344
336
  select_fields = fields ? field_list(fields) : WILDCARD
345
337
  select_source = source_list(opts[:from])
346
- sql = (opts[:distinct] ? SELECT_DISTINCT : SELECT) % [select_fields, select_source]
338
+ sql = opts[:distinct] ? \
339
+ "SELECT DISTINCT #{select_fields} FROM #{select_source}" : \
340
+ "SELECT #{select_fields} FROM #{select_source}"
347
341
 
348
342
  if join_type = opts[:join_type]
349
343
  join_table = opts[:join_table]
350
344
  join_cond = join_cond_list(opts[:join_cond], join_table)
351
- sql << (JOIN_CLAUSE % [join_type, join_table, join_cond])
345
+ sql << " #{join_type} #{join_table} ON #{join_cond}"
352
346
  end
353
347
 
354
348
  if where = opts[:where]
355
- sql << (WHERE % where)
349
+ sql << " WHERE #{where}"
356
350
  end
357
351
 
358
352
  if group = opts[:group]
359
- sql << (GROUP % field_list(group))
353
+ sql << " GROUP BY #{field_list(group)}"
360
354
  end
361
355
 
362
356
  if order = opts[:order]
363
- sql << (ORDER % field_list(order))
357
+ sql << " ORDER BY #{field_list(order)}"
364
358
  end
365
359
 
366
360
  if having = opts[:having]
367
- sql << (HAVING % having)
361
+ sql << " HAVING #{having}"
368
362
  end
369
363
 
370
364
  if limit = opts[:limit]
371
- sql << (LIMIT % limit)
365
+ sql << " LIMIT #{limit}"
372
366
  if offset = opts[:offset]
373
- sql << (OFFSET % offset)
367
+ sql << " OFFSET #{offset}"
374
368
  end
375
369
  end
376
370
 
@@ -379,13 +373,9 @@ module Sequel
379
373
 
380
374
  alias_method :sql, :select_sql
381
375
 
382
- INSERT = "INSERT INTO %s (%s) VALUES (%s)".freeze
383
- INSERT_VALUES = "INSERT INTO %s VALUES (%s)".freeze
384
- INSERT_EMPTY = "INSERT INTO %s DEFAULT VALUES".freeze
385
-
386
376
  def insert_sql(*values)
387
377
  if values.empty?
388
- INSERT_EMPTY % @opts[:from]
378
+ "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
389
379
  elsif (values.size == 1) && values[0].is_a?(Hash)
390
380
  field_list = []
391
381
  value_list = []
@@ -393,18 +383,14 @@ module Sequel
393
383
  field_list << k
394
384
  value_list << literal(v)
395
385
  end
396
- INSERT % [
397
- @opts[:from],
398
- field_list.join(COMMA_SEPARATOR),
399
- value_list.join(COMMA_SEPARATOR)]
386
+ fl = field_list.join(COMMA_SEPARATOR)
387
+ vl = value_list.join(COMMA_SEPARATOR)
388
+ "INSERT INTO #{@opts[:from]} (#{fl}) VALUES (#{vl})"
400
389
  else
401
- INSERT_VALUES % [@opts[:from], literal(values)]
390
+ "INSERT INTO #{@opts[:from]} VALUES (#{literal(values)})"
402
391
  end
403
392
  end
404
393
 
405
- UPDATE = "UPDATE %s SET %s".freeze
406
- SET_FORMAT = "%s = %s".freeze
407
-
408
394
  def update_sql(values, opts = nil)
409
395
  opts = opts ? @opts.merge(opts) : @opts
410
396
 
@@ -414,19 +400,17 @@ module Sequel
414
400
  raise SequelError, "Can't update a joined dataset"
415
401
  end
416
402
 
417
- set_list = values.map {|kv| SET_FORMAT % [kv[0], literal(kv[1])]}.
403
+ set_list = values.map {|kv| "#{kv[0]} = #{literal(kv[1])}"}.
418
404
  join(COMMA_SEPARATOR)
419
- sql = UPDATE % [opts[:from], set_list]
405
+ sql = "UPDATE #{@opts[:from]} SET #{set_list}"
420
406
 
421
407
  if where = opts[:where]
422
- sql << WHERE % where_list(where)
408
+ sql << " WHERE #{where}"
423
409
  end
424
410
 
425
411
  sql
426
412
  end
427
413
 
428
- DELETE = "DELETE FROM %s".freeze
429
-
430
414
  def delete_sql(opts = nil)
431
415
  opts = opts ? @opts.merge(opts) : @opts
432
416
 
@@ -436,10 +420,10 @@ module Sequel
436
420
  raise SequelError, "Can't delete from a joined dataset"
437
421
  end
438
422
 
439
- sql = DELETE % opts[:from]
423
+ sql = "DELETE FROM #{opts[:from]}"
440
424
 
441
425
  if where = opts[:where]
442
- sql << WHERE % where_list(where)
426
+ sql << " WHERE #{where}"
443
427
  end
444
428
 
445
429
  sql
@@ -456,7 +440,7 @@ module Sequel
456
440
  if opts.keys == [:from] && opts[:from].size == 1
457
441
  opts[:from].first.to_s
458
442
  else
459
- SUBQUERY % sql
443
+ "(#{sql})"
460
444
  end
461
445
  end
462
446
 
@@ -477,10 +461,8 @@ module Sequel
477
461
  select(field.AVG).naked.first.values.first
478
462
  end
479
463
 
480
- EXISTS_EXPR = "EXISTS (%s)".freeze
481
-
482
464
  def exists(opts = nil)
483
- EXISTS_EXPR % sql({:select => 1}.merge(opts || {}))
465
+ "EXISTS (#{sql({:select => [1]}.merge(opts || {}))})"
484
466
  end
485
467
 
486
468
  LIMIT_1 = {:limit => 1}.freeze
@@ -514,11 +496,6 @@ module Sequel
514
496
  where(*conditions).first
515
497
  end
516
498
 
517
- # Updates all records matching the condition with the values specified.
518
- def []=(condition, values)
519
- where(condition).update(values)
520
- end
521
-
522
499
  def last(num = 1)
523
500
  raise SequelError, 'No order specified' unless
524
501
  @opts[:order] || (opts && opts[:order])
@@ -7,6 +7,7 @@ module Sequel
7
7
  instance_methods.each { |m| undef_method m unless m =~ /^(__|instance_eval)/ }
8
8
  end
9
9
 
10
+ # An Expression is made of a left side, an operator, and a right side.
10
11
  class Expression < BlankSlate
11
12
  attr_reader :left, :right
12
13
  attr_accessor :op
@@ -17,14 +18,15 @@ module Sequel
17
18
 
18
19
  def method_missing(sym, *right)
19
20
  @op = case sym
20
- when :==, :===, :in: :eql
21
- when :=~: :like
22
- when :"<=>": :not
23
- when :<: :lt
24
- when :<=: :lte
25
- when :>: :gt
26
- when :>=: :gte
27
- else sym
21
+ when :==, :===, :in, :in?: :eql
22
+ when :=~, :like, :like?: :like
23
+ when :"<=>", :is_not: :not
24
+ when :<: :lt
25
+ when :<=: :lte
26
+ when :>: :gt
27
+ when :>=: :gte
28
+ else
29
+ @left = "#{left}.#{sym}"
28
30
  end
29
31
  @right = right.first
30
32
  self
@@ -37,28 +39,32 @@ module Sequel
37
39
  end
38
40
  end
39
41
 
42
+ # An ExpressionCompiler takes a Proc object and compiles it
43
+ # into an array of expressions using instance_eval magic.
40
44
  class ExpressionCompiler < BlankSlate
41
- def initialize(&block)
45
+ def initialize(&block) #:nodoc:
42
46
  @block = block
43
47
  @expressions = []
44
48
  end
45
-
46
- def method_missing(sym, *args)
47
- expr = Expression.new(sym)
48
- @expressions << expr
49
- expr
50
- end
51
-
52
- def SUM(sym)
53
- expr = Expression.new(sym.SUM)
54
- @expressions << expr
55
- expr
56
- end
57
49
 
50
+ # Converts the block into an array of expressions.
58
51
  def __to_a__
59
52
  instance_eval(&@block)
60
53
  @expressions
54
+ rescue => e
55
+ raise SequelError, e.message
61
56
  end
57
+
58
+ private
59
+ def __expr(sym) #:nodoc:
60
+ expr = Expression.new(sym)
61
+ @expressions << expr
62
+ expr
63
+ end
64
+
65
+ def method_missing(sym, *args); __expr(sym); end #:nodoc:
66
+ def test; __expr(:test); end #:nodoc:
67
+ def SUM(sym); __expr(sym.SUM); end #:nodoc:
62
68
  end
63
69
  end
64
70
  end
@@ -218,7 +218,6 @@ module Sequel
218
218
  def literal(v)
219
219
  case v
220
220
  when String, Fixnum, Float, TrueClass, FalseClass: PGconn.quote(v)
221
- when Time: v.to_sql_timestamp
222
221
  else
223
222
  super
224
223
  end
@@ -237,11 +236,6 @@ module Sequel
237
236
  end
238
237
  end
239
238
 
240
- def format_expression(left, op, right)
241
- op = :eql if op == :like
242
- super
243
- end
244
-
245
239
  def each(opts = nil, &block)
246
240
  query_each(select_sql(opts), true, &block)
247
241
  self
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: sequel
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.0.20
7
- date: 2007-04-18 00:00:00 +03:00
6
+ version: 0.1.0
7
+ date: 2007-04-22 00:00:00 +03:00
8
8
  summary: Concise ORM for Ruby.
9
9
  require_paths:
10
10
  - lib