sequel 0.0.20 → 0.1.0

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