sequel 0.1.1 → 0.1.2

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.2*
2
+
3
+ * The first opened database is automatically assigned to to Model.db.
4
+
5
+ * Removed SequelConnectionError. Exception class errors are converted to RuntimeError.
6
+
7
+ * Added support for UNION, INTERSECT and EXCEPT set operations.
8
+
9
+ * Fixed Dataset#single_record to return nil if no record is found.
10
+
11
+ * Updated specs to conform to RSpec 1.0.
12
+
13
+ * Added Model#find_or_create method.
14
+
15
+ * Fixed MySQL::Dataset#query_single (thanks Dries Harnie.)
16
+
17
+ * Added Model.subset method. Fixed Model.filter and Model.exclude to accept blocks.
18
+
19
+ * Added Database#uri method.
20
+
21
+ * Refactored and removed deprecated code in postgres adapter.
22
+
1
23
  *0.1.1*
2
24
 
3
25
  * More documentation for Dataset.
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
6
6
  include FileUtils
7
7
 
8
8
  NAME = "sequel"
9
- VERS = "0.1.1"
9
+ VERS = "0.1.2"
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",
@@ -50,8 +50,6 @@ module Sequel
50
50
  #
51
51
  # If no connection is available, Pool#hold will block until a connection
52
52
  # is available.
53
- #
54
- # Errors raised inside a hold call are wrapped in a SequelConnectionError.
55
53
  def hold
56
54
  t = Thread.current
57
55
  if (conn = owned_connection(t))
@@ -65,10 +63,9 @@ module Sequel
65
63
  ensure
66
64
  release(t)
67
65
  end
68
- rescue SequelConnectionError => e
69
- raise e
70
66
  rescue Exception => e
71
- raise SequelConnectionError.new(e)
67
+ # if the error is not a StandardError it is converted into RuntimeError.
68
+ raise e.is_a?(StandardError) ? e : e.message
72
69
  end
73
70
 
74
71
  private
@@ -2,6 +2,7 @@ require 'uri'
2
2
 
3
3
  require File.join(File.dirname(__FILE__), 'schema')
4
4
  require File.join(File.dirname(__FILE__), 'dataset')
5
+ require File.join(File.dirname(__FILE__), 'model')
5
6
 
6
7
  module Sequel
7
8
  # A Database object represents a virtual connection to a database.
@@ -15,11 +16,30 @@ module Sequel
15
16
  #
16
17
  # Sequel::Database is an abstract class that is not useful by itself.
17
18
  def initialize(opts = {}, &block)
19
+ Model.database_opened(self)
18
20
  @opts = opts
19
21
  @pool = ConnectionPool.new(@opts[:max_connections] || 4, &block)
20
22
  @logger = opts[:logger]
21
23
  end
22
24
 
25
+ def uri
26
+ uri = URI::Generic.new(
27
+ self.class.adapter_scheme.to_s,
28
+ nil,
29
+ @opts[:host],
30
+ @opts[:port],
31
+ nil,
32
+ "/#{@opts[:database]}",
33
+ nil,
34
+ nil,
35
+ nil
36
+ )
37
+ uri.user = @opts[:user]
38
+ uri.password = @opts[:password]
39
+ uri.to_s
40
+ end
41
+ alias url uri # Because I don't care much for the semantic difference.
42
+
23
43
  # Returns a blank dataset
24
44
  def dataset
25
45
  Dataset.new(self)
@@ -114,7 +134,7 @@ module Sequel
114
134
 
115
135
  @@adapters = Hash.new
116
136
 
117
- # Sets the adapter scheme for the database class. Call this method in
137
+ # Sets the adapter scheme for the Database class. Call this method in
118
138
  # descendnants of Database to allow connection using a URL. For example the
119
139
  # following:
120
140
  # class DB2::Database < Sequel::Database
@@ -124,9 +144,15 @@ module Sequel
124
144
  # would allow connection using:
125
145
  # Sequel.open('db2://user:password@dbserver/mydb')
126
146
  def self.set_adapter_scheme(scheme)
147
+ @scheme = scheme
127
148
  @@adapters[scheme.to_sym] = self
128
149
  end
129
150
 
151
+ # Returns the scheme for the Database class.
152
+ def self.adapter_scheme
153
+ @scheme
154
+ end
155
+
130
156
  # Converts a uri to an options hash. These options are then passed
131
157
  # to a newly created database object.
132
158
  def self.uri_to_options(uri)
@@ -343,8 +343,20 @@ module Sequel
343
343
  raise SequelError, "Can only specify a HAVING clause on a grouped dataset"
344
344
  else
345
345
  filter(*cond, &block)
346
- end
347
- end
346
+ end
347
+ end
348
+
349
+ def union(dataset, all = false)
350
+ dup_merge(:union => dataset, :union_all => all)
351
+ end
352
+
353
+ def intersect(dataset, all = false)
354
+ dup_merge(:intersect => dataset, :intersect_all => all)
355
+ end
356
+
357
+ def except(dataset, all = false)
358
+ dup_merge(:except => dataset, :except_all => all)
359
+ end
348
360
 
349
361
  LEFT_OUTER_JOIN = 'LEFT OUTER JOIN'.freeze
350
362
  INNER_JOIN = 'INNER JOIN'.freeze
@@ -437,6 +449,17 @@ module Sequel
437
449
  end
438
450
  end
439
451
 
452
+ if union = opts[:union]
453
+ sql << (opts[:union_all] ? \
454
+ " UNION ALL #{union.sql}" : " UNION #{union.sql}")
455
+ elsif intersect = opts[:intersect]
456
+ sql << (opts[:intersect_all] ? \
457
+ " INTERSECT ALL #{intersect.sql}" : " INTERSECT #{intersect.sql}")
458
+ elsif except = opts[:except]
459
+ sql << (opts[:except_all] ? \
460
+ " EXCEPT ALL #{except.sql}" : " EXCEPT #{except.sql}")
461
+ end
462
+
440
463
  sql
441
464
  end
442
465
  alias sql select_sql
@@ -516,6 +539,7 @@ module Sequel
516
539
  # Returns the first record in the dataset.
517
540
  def single_record(opts = nil)
518
541
  each(opts) {|r| return r}
542
+ nil
519
543
  end
520
544
 
521
545
  # Returns the first value of the first reecord in the dataset.
data/lib/sequel/error.rb CHANGED
@@ -1,20 +1,2 @@
1
1
  class SequelError < StandardError
2
- end
3
-
4
- # This error class is used to wrap exceptions occuring inside calls to
5
- # ConnectionPool#hold. Sequel wraps any exception raised by the database
6
- # connection and provides it as a SequelConnectionError. The original
7
- # exception is provided through SequelConnectionError#original_exception.
8
- class SequelConnectionError < SequelError
9
- attr_reader :original_error
10
-
11
- def initialize(original_error)
12
- @original_error = original_error
13
- end
14
-
15
- def message
16
- "#{@original_error.class}: #{@original_error.message}"
17
- end
18
-
19
- alias_method :to_s, :message
20
- end
2
+ end
data/lib/sequel/model.rb CHANGED
@@ -9,6 +9,9 @@ module Sequel
9
9
  @db ||= ((superclass != Object) && (superclass.db)) || nil
10
10
  end
11
11
  def self.db=(db); @db = db; end
12
+ def self.database_opened(db)
13
+ @db = db if self == Model && !@db
14
+ end
12
15
 
13
16
  def self.table_name
14
17
  @table_name ||= ((superclass != Model) && (superclass.table_name)) || nil
@@ -90,6 +93,10 @@ module Sequel
90
93
  create_table
91
94
  end
92
95
 
96
+ def self.subset(name, *args, &block)
97
+ meta_def(name) {filter(*args, &block)}
98
+ end
99
+
93
100
  ONE_TO_ONE_PROC = "proc {i = @values[:%s]; %s[i] if i}".freeze
94
101
  ID_POSTFIX = "_id".freeze
95
102
  FROM_DATASET = "db[%s]".freeze
@@ -126,6 +133,10 @@ module Sequel
126
133
  self.class.get_hooks(key).each {|h| instance_eval(&h)}
127
134
  end
128
135
 
136
+ def self.before_save(&block)
137
+ get_hooks(:before_save).unshift(block)
138
+ end
139
+
129
140
  def self.before_create(&block)
130
141
  get_hooks(:before_create).unshift(block)
131
142
  end
@@ -134,17 +145,25 @@ module Sequel
134
145
  get_hooks(:before_destroy).unshift(block)
135
146
  end
136
147
 
137
- def self.after_destroy(&block)
138
- get_hooks(:after_destroy).unshift(block)
148
+ def self.after_save(&block)
149
+ get_hooks(:after_save) << block
139
150
  end
140
151
 
141
152
  def self.after_create(&block)
142
153
  get_hooks(:after_create) << block
143
154
  end
144
155
 
156
+ def self.after_destroy(&block)
157
+ get_hooks(:after_destroy).unshift(block)
158
+ end
159
+
145
160
  def self.find(cond)
146
161
  dataset[cond.is_a?(Hash) ? cond : {primary_key => cond}]
147
162
  end
163
+
164
+ def self.find_or_create(cond)
165
+ find(cond) || create(cond)
166
+ end
148
167
 
149
168
  class << self; alias_method :[], :find; end
150
169
 
@@ -177,8 +196,8 @@ module Sequel
177
196
 
178
197
  def self.each(&block); dataset.each(&block); end
179
198
  def self.all; dataset.all; end
180
- def self.filter(*arg); dataset.filter(*arg); end
181
- def self.exclude(*arg); dataset.exclude(*arg); end
199
+ def self.filter(*arg, &block); dataset.filter(*arg, &block); end
200
+ def self.exclude(*arg, &block); dataset.exclude(*arg, &block); end
182
201
  def self.order(*arg); dataset.order(*arg); end
183
202
  def self.first(*arg); dataset.first(*arg); end
184
203
  def self.count; dataset.count; end
@@ -252,14 +271,14 @@ module Sequel
252
271
  def save
253
272
  run_hooks(:before_save)
254
273
  if @pkey
255
- # run_hooks(:before_update)
274
+ run_hooks(:before_update)
256
275
  model.dataset.filter(primary_key => @pkey).update(@values)
257
- # run_hooks(:after_update)
276
+ run_hooks(:after_update)
258
277
  else
259
- # run_hooks(:before_create)
278
+ run_hooks(:before_create)
260
279
  @pkey = model.dataset.insert(@values)
261
280
  refresh
262
- # run_hooks(:after_create)
281
+ run_hooks(:after_create)
263
282
  end
264
283
  run_hooks(:after_save)
265
284
  end
data/lib/sequel/mysql.rb CHANGED
@@ -2,7 +2,7 @@ if !Object.const_defined?('Sequel')
2
2
  require File.join(File.dirname(__FILE__), '../sequel')
3
3
  end
4
4
 
5
- require 'mysql'
5
+ #require 'mysql'
6
6
 
7
7
  module Sequel
8
8
  module MySQL
@@ -101,9 +101,9 @@ module Sequel
101
101
  result = @db.execute(sql)
102
102
  begin
103
103
  if use_model_class && @model_class
104
- @model_class.new(result.fetch_hash)
104
+ row = @model_class.new(result.fetch_hash)
105
105
  else
106
- result.fetch_hash
106
+ row = result.fetch_hash
107
107
  end
108
108
  ensure
109
109
  result.free
@@ -24,16 +24,12 @@ class PGconn
24
24
 
25
25
  def execute(sql)
26
26
  begin
27
- # ServerSide.info(sql)
28
27
  async_exec(sql)
29
28
  rescue PGError => e
30
29
  unless connected?
31
- # ServerSide.warn('Reconnecting to Postgres server')
32
30
  reset
33
31
  async_exec(sql)
34
32
  else
35
- p sql
36
- p e
37
33
  raise e
38
34
  end
39
35
  end
@@ -49,16 +45,13 @@ class PGconn
49
45
  if @transaction_in_progress
50
46
  return yield
51
47
  end
52
- # ServerSide.info('BEGIN')
53
48
  async_exec(SQL_BEGIN)
54
49
  begin
55
50
  @transaction_in_progress = true
56
51
  result = yield
57
- # ServerSide.info('COMMIT')
58
52
  async_exec(SQL_COMMIT)
59
53
  result
60
54
  rescue => e
61
- # ServerSide.info('ROLLBACK')
62
55
  async_exec(SQL_ROLLBACK)
63
56
  raise e
64
57
  ensure
@@ -126,15 +119,8 @@ class String
126
119
  end
127
120
  end
128
121
 
129
- TIME_REGEXP = /(\d{4})-(\d{2})-(\d{2})\s(\d{2}):(\d{2}):(\d{2})/
130
-
131
122
  def postgres_to_time
132
123
  Time.parse(self)
133
- # if self =~ TIME_REGEXP
134
- # Time.local($1.to_i, $2.to_i, $3.to_i, $4.to_i, $5.to_i, $6.to_i)
135
- # else
136
- # nil
137
- # end
138
124
  end
139
125
  end
140
126
 
@@ -161,8 +147,8 @@ module Sequel
161
147
  @opts[:host] || 'localhost',
162
148
  @opts[:port] || 5432,
163
149
  '', '',
164
- @opts[:database] || 'reality_development',
165
- @opts[:user] || 'postgres',
150
+ @opts[:database],
151
+ @opts[:user],
166
152
  @opts[:password]
167
153
  )
168
154
  end
@@ -368,7 +354,6 @@ module Sequel
368
354
  result = @db.execute(sql)
369
355
  begin
370
356
  row = nil
371
- # each_row(result, use_model_class) {|r| row = r}
372
357
  conv = row_converter(result, use_model_class)
373
358
  result.each {|r| row = conv.call(r)}
374
359
  ensure
@@ -393,60 +378,42 @@ module Sequel
393
378
  end
394
379
  end
395
380
 
396
- def each_row(result, use_model_class)
397
- fields = result.fields.map {|s| s.to_sym}
398
- types = (0..(result.num_fields - 1)).map {|idx| PG_TYPES[result.type(idx)]}
399
- m_klass = use_model_class && @model_class
400
- result.each do |row|
401
- hashed_row = {}
402
- row.each_index do |cel_index|
403
- column = row[cel_index]
404
- if column && types[cel_index]
405
- column = column.send(types[cel_index])
406
- end
407
- hashed_row[fields[cel_index]] = column
408
- end
409
- yield m_klass ? m_klass.new(hashed_row) : hashed_row
410
- end
411
- end
412
-
413
- COMMA = ','.freeze
414
-
415
381
  @@converters_mutex = Mutex.new
416
382
  @@converters = {}
417
383
 
418
384
  def row_converter(result, use_model_class)
419
- fields = result.fields.map {|s| s.to_sym}
420
- types = (0..(result.num_fields - 1)).map {|idx| result.type(idx)}
421
- klass = use_model_class ? @model_class : nil
385
+ fields = []; translators = []
386
+ result.fields.each_with_index do |f, idx|
387
+ fields << f.to_sym
388
+ translators << PG_TYPES[result.type(idx)]
389
+ end
390
+ klass = use_model_class && @model_class
422
391
 
423
392
  # create result signature and memoize the converter
424
- sig = fields.join(COMMA) + types.join(COMMA) + klass.to_s
393
+ sig = [fields, translators, klass].hash
425
394
  @@converters_mutex.synchronize do
426
- @@converters[sig] ||= compile_converter(fields, types, klass)
395
+ @@converters[sig] ||= compile_converter(fields, translators, klass)
427
396
  end
428
397
  end
429
398
 
430
- CONVERT = "lambda {|r| {%s}}".freeze
431
- CONVERT_MODEL_CLASS = "lambda {|r| %2$s.new(%1$s)}".freeze
432
-
433
- CONVERT_FIELD = '%s => r[%d]'.freeze
434
- CONVERT_FIELD_TRANSLATE = '%s => ((t = r[%d]) ? t.%s : nil)'.freeze
435
-
436
- def compile_converter(fields, types, klass)
399
+ def compile_converter(fields, translators, klass)
437
400
  used_fields = []
438
401
  kvs = []
439
402
  fields.each_with_index do |field, idx|
440
403
  next if used_fields.include?(field)
441
404
  used_fields << field
442
405
 
443
- translate_fn = PG_TYPES[types[idx]]
444
- kvs << (translate_fn ? CONVERT_FIELD_TRANSLATE : CONVERT_FIELD) %
445
- [field.inspect, idx, translate_fn]
406
+ if translator = translators[idx]
407
+ kvs << ":#{field} => ((t = r[#{idx}]) ? t.#{translator} : nil)"
408
+ else
409
+ kvs << ":#{field} => r[#{idx}]"
410
+ end
411
+ end
412
+ if klass
413
+ eval("lambda {|r| #{klass}.new(#{kvs.join(COMMA_SEPARATOR)})}")
414
+ else
415
+ eval("lambda {|r| {#{kvs.join(COMMA_SEPARATOR)}}}")
446
416
  end
447
- s = (klass ? CONVERT_MODEL_CLASS : CONVERT) %
448
- [kvs.join(COMMA), klass]
449
- eval(s)
450
417
  end
451
418
  end
452
419
  end
data/lib/sequel/sqlite.rb CHANGED
@@ -40,10 +40,12 @@ module Sequel
40
40
  end
41
41
 
42
42
  def single_value(sql)
43
+ @logger.info(sql) if @logger
43
44
  @pool.hold {|conn| conn.get_first_value(sql)}
44
45
  end
45
46
 
46
47
  def result_set(sql, model_class, &block)
48
+ @logger.info(sql) if @logger
47
49
  @pool.hold do |conn|
48
50
  conn.query(sql) do |result|
49
51
  columns = result.columns
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.1.1
7
- date: 2007-04-28 00:00:00 +03:00
6
+ version: 0.1.2
7
+ date: 2007-05-19 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