sequel 0.3.4.1 → 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (36) hide show
  1. data/CHANGELOG +24 -0
  2. data/README +2 -0
  3. data/Rakefile +2 -2
  4. data/lib/{sequel → sequel-core}/array_keys.rb +0 -0
  5. data/lib/{sequel → sequel-core}/connection_pool.rb +0 -0
  6. data/lib/{sequel → sequel-core}/core_ext.rb +0 -0
  7. data/lib/{sequel → sequel-core}/core_sql.rb +8 -0
  8. data/lib/{sequel → sequel-core}/database.rb +4 -2
  9. data/lib/{sequel → sequel-core}/dataset.rb +0 -0
  10. data/lib/{sequel → sequel-core}/dataset/convenience.rb +0 -0
  11. data/lib/{sequel → sequel-core}/dataset/sequelizer.rb +0 -0
  12. data/lib/{sequel → sequel-core}/dataset/sql.rb +22 -21
  13. data/lib/{sequel → sequel-core}/error.rb +0 -0
  14. data/lib/{sequel → sequel-core}/migration.rb +0 -0
  15. data/lib/{sequel → sequel-core}/model.rb +5 -0
  16. data/lib/{sequel → sequel-core}/model/base.rb +0 -0
  17. data/lib/{sequel → sequel-core}/model/caching.rb +0 -0
  18. data/lib/{sequel → sequel-core}/model/hooks.rb +0 -0
  19. data/lib/{sequel → sequel-core}/model/record.rb +0 -0
  20. data/lib/{sequel → sequel-core}/model/relations.rb +0 -0
  21. data/lib/{sequel → sequel-core}/model/schema.rb +0 -0
  22. data/lib/{sequel → sequel-core}/pretty_table.rb +0 -0
  23. data/lib/{sequel → sequel-core}/schema.rb +0 -0
  24. data/lib/{sequel → sequel-core}/schema/schema_generator.rb +0 -0
  25. data/lib/{sequel → sequel-core}/schema/schema_sql.rb +0 -0
  26. data/lib/{sequel → sequel-core}/worker.rb +23 -10
  27. data/lib/sequel.rb +3 -1
  28. data/lib/sequel/dbi.rb +33 -0
  29. data/lib/sequel/informix.rb +78 -0
  30. data/lib/sequel/mysql.rb +0 -2
  31. data/lib/sequel/postgres.rb +16 -0
  32. data/spec/adapters/mysql_spec.rb +11 -0
  33. data/spec/adapters/postgres_spec.rb +5 -0
  34. data/spec/dataset_spec.rb +26 -11
  35. data/spec/worker_spec.rb +35 -2
  36. metadata +97 -88
data/CHANGELOG CHANGED
@@ -1,3 +1,27 @@
1
+ === 0.4.0 (2007-11-24)
2
+
3
+ * Reorganized lib directory structure.
4
+
5
+ * Added support for dbi-xxx URI schemes (#86).
6
+
7
+ * Fixed problem in Database#uri where setting the password would raise an error (#87).
8
+
9
+ * Improved Dataset#insert_sql to correctly handle string keys (#92).
10
+
11
+ * Improved error-handling for worker threads. Errors are saved to an array and are accessible through #errors (#91).
12
+
13
+ * Dataset#uniq/distinct can now accept a column list for DISTINCT ON clauses.
14
+
15
+ * Fixed Model.all.
16
+
17
+ * Fixed literalization of strings with escape sequences in postgres adapter (#90).
18
+
19
+ * Added support for literalizing BigDecimal values (#89).
20
+
21
+ * Fixed column qualification for joined datasets (thanks Christian).
22
+
23
+ * Implemented experimental informix adapter.
24
+
1
25
  === 0.3.4.1 (2007-11-10)
2
26
 
3
27
  * Changed Dataset#select_sql to support queries without a FROM clause.
data/README CHANGED
@@ -36,6 +36,8 @@ Sequel currently supports:
36
36
  * PostgreSQL
37
37
  * SQLite 3
38
38
 
39
+ There are also experimental adapters for DB2 and Informix.
40
+
39
41
  == The Sequel Console
40
42
 
41
43
  Sequel includes an IRB console for quick'n'dirty access to databases. You can use it like this:
data/Rakefile CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
6
6
  include FileUtils
7
7
 
8
8
  NAME = "sequel"
9
- VERS = "0.3.4.1"
9
+ VERS = "0.4.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",
@@ -25,7 +25,7 @@ Rake::RDocTask.new do |rdoc|
25
25
  rdoc.options += RDOC_OPTS
26
26
  rdoc.main = "README"
27
27
  rdoc.title = "Sequel: Lightweight ORM library for Ruby"
28
- rdoc.rdoc_files.add ['README', 'COPYING', 'lib/sequel.rb', 'lib/sequel/**/*.rb']
28
+ rdoc.rdoc_files.add ['README', 'COPYING', 'lib/sequel.rb', 'lib/**/*.rb']
29
29
  end
30
30
 
31
31
  spec = Gem::Specification.new do |s|
File without changes
File without changes
File without changes
@@ -68,6 +68,14 @@ module Sequel
68
68
  end
69
69
  end
70
70
 
71
+ class QualifiedColumnRef < Expression
72
+ def initialize(t, c); @t, @c = t, c; end
73
+
74
+ def to_s(ds)
75
+ "#{@t}.#{ds.literal(@c)}"
76
+ end
77
+ end
78
+
71
79
  class Function < Expression
72
80
  def initialize(f, *args); @f, @args = f, args; end
73
81
 
@@ -64,7 +64,7 @@ module Sequel
64
64
  nil
65
65
  )
66
66
  uri.user = @opts[:user]
67
- uri.password = @opts[:password]
67
+ uri.password = @opts[:password] if uri.user
68
68
  uri.to_s
69
69
  end
70
70
  alias url uri # Because I don't care much for the semantic difference.
@@ -285,7 +285,9 @@ module Sequel
285
285
  # DB = Sequel.open 'sqlite:///blog.db'
286
286
  def self.connect(conn_string, more_opts = nil)
287
287
  uri = URI.parse(conn_string)
288
- c = @@adapters[uri.scheme.to_sym]
288
+ scheme = uri.scheme
289
+ scheme = :dbi if scheme =~ /^dbi-(.+)/
290
+ c = @@adapters[scheme.to_sym]
289
291
  raise SequelError, "Invalid database scheme" unless c
290
292
  c.new(c.uri_to_options(uri).merge(more_opts || {}))
291
293
  end
File without changes
File without changes
File without changes
@@ -14,16 +14,15 @@ module Sequel
14
14
  # Returns a qualified column name (including a table name) if the column
15
15
  # name isn't already qualified.
16
16
  def qualified_column_name(column, table)
17
- column = literal(column)
18
- if column =~ QUALIFIED_REGEXP
19
- # column is already qualified
20
- column
21
- else
22
- # check if the table is aliased
23
- if table =~ ALIASED_REGEXP
17
+ s = literal(column)
18
+ if s =~ QUALIFIED_REGEXP
19
+ return column
20
+ else
21
+ if (table =~ ALIASED_REGEXP)
24
22
  table = $2
25
23
  end
26
- "#{table}.#{column}"
24
+ Sequel::SQL::QualifiedColumnRef.new(table, column)
25
+ # "#{table}.#{column}"
27
26
  end
28
27
  end
29
28
 
@@ -86,6 +85,7 @@ module Sequel
86
85
  when LiteralString: v
87
86
  when String: "'#{v.gsub(/'/, "''")}'"
88
87
  when Integer, Float: v.to_s
88
+ when BigDecimal: v.to_s("F")
89
89
  when NilClass: NULL
90
90
  when TrueClass: TRUE
91
91
  when FalseClass: FALSE
@@ -109,10 +109,7 @@ module Sequel
109
109
  case expr
110
110
  when Hash:
111
111
  parenthesize = false if expr.size == 1
112
- # fmt = expr.map {|i| compare_expr(i[0], i[1])}.join(AND_SEPARATOR)
113
- # N.B.: We convert this to an array and sort it in order to have a fixed order for testability.
114
- # Hash in Ruby 1.8 has no order, so Hash#map is indeterminate, which makes it hard to test.
115
- fmt = expr.to_a.sort_by { |k, v| k.to_s }.map {|i| compare_expr(i[0], i[1])}.join(AND_SEPARATOR)
112
+ fmt = expr.map {|i| compare_expr(i[0], i[1])}.join(AND_SEPARATOR)
116
113
  when Array:
117
114
  fmt = expr.shift.gsub(QUESTION_MARK) {literal(expr.shift)}
118
115
  when Proc:
@@ -137,10 +134,10 @@ module Sequel
137
134
  end
138
135
 
139
136
  # Returns a copy of the dataset with the distinct option.
140
- def uniq
141
- clone_merge(:distinct => true)
137
+ def uniq(*args)
138
+ clone_merge(:distinct => args)
142
139
  end
143
- alias distinct uniq
140
+ alias_method :distinct, :uniq
144
141
 
145
142
  # Returns a copy of the dataset with the order changed.
146
143
  def order(*order)
@@ -320,8 +317,8 @@ module Sequel
320
317
 
321
318
  join_conditions = {}
322
319
  expr.each do |k, v|
323
- k = qualified_column_name(k, table).intern if k.is_a?(Symbol)
324
- v = qualified_column_name(v, @opts[:last_joined_table] || @opts[:from].first).intern if v.is_a?(Symbol)
320
+ k = qualified_column_name(k, table) if k.is_a?(Symbol)
321
+ v = qualified_column_name(v, @opts[:last_joined_table] || @opts[:from].first) if v.is_a?(Symbol)
325
322
  join_conditions[k] = v
326
323
  end
327
324
  " #{join_type} #{table} ON #{expression_list(join_conditions)}"
@@ -371,9 +368,13 @@ module Sequel
371
368
 
372
369
  columns = opts[:select]
373
370
  select_columns = columns ? column_list(columns) : WILDCARD
374
- sql = opts[:distinct] ? \
375
- "SELECT DISTINCT #{select_columns}" : \
376
- "SELECT #{select_columns}"
371
+
372
+ if distinct = opts[:distinct]
373
+ distinct_clause = distinct.empty? ? "DISTINCT" : "DISTINCT ON (#{column_list(distinct)})"
374
+ sql = "SELECT #{distinct_clause} #{select_columns}"
375
+ else
376
+ sql = "SELECT #{select_columns}"
377
+ end
377
378
 
378
379
  if opts[:from]
379
380
  sql << " FROM #{source_list(opts[:from])}"
@@ -453,7 +454,7 @@ module Sequel
453
454
  "INSERT INTO #{@opts[:from]} DEFAULT VALUES"
454
455
  else
455
456
  fl, vl = [], []
456
- values.each {|k, v| fl << literal(k); vl << literal(v)}
457
+ values.each {|k, v| fl << k.to_s; vl << literal(v)}
457
458
  "INSERT INTO #{@opts[:from]} (#{fl.join(COMMA_SEPARATOR)}) VALUES (#{vl.join(COMMA_SEPARATOR)})"
458
459
  end
459
460
  when Dataset
File without changes
File without changes
@@ -305,6 +305,11 @@ module Sequel
305
305
  table_name = dataset.opts[:from].first
306
306
  dataset.join(*args).select(table_name.to_sym.ALL)
307
307
  end
308
+
309
+ # Returns an array containing all model records
310
+ def self.all
311
+ dataset.all
312
+ end
308
313
 
309
314
  # Returns value of attribute.
310
315
  def [](column)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
@@ -5,22 +5,22 @@ module Sequel
5
5
  class WorkerStopError < RuntimeError; end
6
6
 
7
7
  attr_reader :queue
8
+ attr_reader :errors
8
9
 
9
10
  def initialize(db = nil)
10
11
  @queue = Queue.new
12
+ @errors = []
11
13
  t = self
12
- if db
13
- super {db.transaction {t.work}}
14
- else
15
- super {t.work}
16
- end
14
+ t.abort_on_exception = true
15
+ @transaction = !db.nil?
16
+ db ? super {db.transaction {t.work}} : super {t.work}
17
17
  end
18
-
18
+
19
19
  def work
20
- begin
21
- loop {@cur = @queue.pop; @cur.call; @cur = nil}
22
- rescue WorkerStopError # do nothing
23
- end
20
+ loop {next_job}
21
+ rescue WorkerStopError # signals the worker thread to stop
22
+ ensure
23
+ rollback! if @transaction && !@errors.empty?
24
24
  end
25
25
 
26
26
  def busy?
@@ -29,6 +29,7 @@ module Sequel
29
29
 
30
30
  def async(proc = nil, &block)
31
31
  @queue << (proc || block)
32
+ self
32
33
  end
33
34
  alias_method :add, :async
34
35
  alias_method :<<, :async
@@ -40,5 +41,17 @@ module Sequel
40
41
  self.raise WorkerStopError
41
42
  super
42
43
  end
44
+
45
+ private
46
+ def next_job
47
+ @cur = @queue.pop
48
+ @cur.call
49
+ rescue WorkerStopError => e
50
+ raise e
51
+ rescue Exception => e
52
+ @errors << e
53
+ ensure
54
+ @cur = nil
55
+ end
43
56
  end
44
57
  end
data/lib/sequel.rb CHANGED
@@ -1,10 +1,12 @@
1
1
  require 'metaid'
2
+ require 'bigdecimal'
3
+ require 'bigdecimal/util'
2
4
 
3
5
  files = %w[
4
6
  core_ext core_sql array_keys error connection_pool pretty_table
5
7
  dataset migration model schema database worker
6
8
  ]
7
- dir = File.join(File.dirname(__FILE__), 'sequel')
9
+ dir = File.join(File.dirname(__FILE__), 'sequel-core')
8
10
  files.each {|f| require(File.join(dir, f))}
9
11
 
10
12
  module Sequel #:nodoc:
data/lib/sequel/dbi.rb CHANGED
@@ -8,6 +8,39 @@ module Sequel
8
8
  module DBI
9
9
  class Database < Sequel::Database
10
10
  set_adapter_scheme :dbi
11
+
12
+ DBI_ADAPTERS = {
13
+ :ado => "ADO",
14
+ :db2 => "DB2",
15
+ :frontbase => "FrontBase",
16
+ :interbase => "InterBase",
17
+ :msql => "Msql",
18
+ :mysql => "Mysql",
19
+ :odbc => "ODBC",
20
+ :oracle => "Oracle",
21
+ :pg => "Pg",
22
+ :proxy => "Proxy",
23
+ :sqlite => "SQLite",
24
+ :sqlrelay => "SQLRelay"
25
+ }
26
+
27
+ # Converts a uri to an options hash. These options are then passed
28
+ # to a newly created database object.
29
+ def self.uri_to_options(uri)
30
+ database = (uri.path =~ /\/(.*)/) && ($1)
31
+ if uri.scheme =~ /dbi-(.+)/
32
+ adapter = DBI_ADAPTERS[$1.to_sym] || $1
33
+ database = "#{adapter}:#{database}"
34
+ end
35
+ {
36
+ :user => uri.user,
37
+ :password => uri.password,
38
+ :host => uri.host,
39
+ :port => uri.port,
40
+ :database => database
41
+ }
42
+ end
43
+
11
44
 
12
45
  def connect
13
46
  dbname = @opts[:database]
@@ -0,0 +1,78 @@
1
+ if !Object.const_defined?('Sequel')
2
+ require File.join(File.dirname(__FILE__), '../sequel')
3
+ end
4
+
5
+ require 'informix'
6
+
7
+ module Sequel
8
+ module Informix
9
+ class Database < Sequel::Database
10
+ set_adapter_scheme :informix
11
+
12
+ # AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
13
+ #
14
+ # def auto_increment_sql
15
+ # AUTO_INCREMENT
16
+ # end
17
+
18
+ def connect
19
+ ::Informix.connect(@opts[:database], @opts[:user], @opts[:password])
20
+ end
21
+
22
+ def disconnect
23
+ @pool.disconnect {|c| c.close}
24
+ end
25
+
26
+ def dataset(opts = nil)
27
+ Sequel::Informix::Dataset.new(self, opts)
28
+ end
29
+
30
+ # Returns number of rows affected
31
+ def execute(sql)
32
+ @logger.info(sql) if @logger
33
+ @pool.hold {|c| c.do(sql)}
34
+ end
35
+ alias_method :do, :execute
36
+
37
+ def query(sql, &block)
38
+ @logger.info(sql) if @logger
39
+ @pool.hold {|c| block[c.cursor(sql)]}
40
+ end
41
+ end
42
+
43
+ class Dataset < Sequel::Dataset
44
+ def literal(v)
45
+ case v
46
+ when Time: literal(v.iso8601)
47
+ else
48
+ super
49
+ end
50
+ end
51
+
52
+ def fetch_rows(sql, &block)
53
+ @db.synchronize do
54
+ @db.query(sql) do |cursor|
55
+ begin
56
+ cursor.open.each_hash {|r| block[r]}
57
+ ensure
58
+ cursor.drop
59
+ end
60
+ end
61
+ end
62
+ self
63
+ end
64
+
65
+ def insert(*values)
66
+ @db.do insert_sql(*values)
67
+ end
68
+
69
+ def update(values, opts = nil)
70
+ @db.do update_sql(values, opts)
71
+ end
72
+
73
+ def delete(opts = nil)
74
+ @db.do delete_sql(opts)
75
+ end
76
+ end
77
+ end
78
+ end
data/lib/sequel/mysql.rb CHANGED
@@ -2,8 +2,6 @@ if !Object.const_defined?('Sequel')
2
2
  require File.join(File.dirname(__FILE__), '../sequel')
3
3
  end
4
4
 
5
- require "bigdecimal"
6
- require "bigdecimal/util"
7
5
  require 'mysql'
8
6
 
9
7
  # Monkey patch Mysql::Result to yield hashes with symbol keys
@@ -18,6 +18,22 @@ class PGconn
18
18
  end
19
19
  end
20
20
 
21
+ class << self
22
+ # The postgres gem's string quoting doesn't render string literals properly, which this fixes.
23
+ #
24
+ # "a basic string" #=> 'a basic string'
25
+ # "this\or that" #=> E'this\\or that'
26
+ #
27
+ # See <http://www.postgresql.org/docs/8.2/static/sql-syntax-lexical.html> for details.
28
+ def quote_with_proper_escaping(s)
29
+ value = quote_without_proper_escaping(s)
30
+ value = "E#{value}" if value =~ /\\/
31
+ return value
32
+ end
33
+ alias_method :quote_without_proper_escaping, :quote
34
+ alias_method :quote, :quote_with_proper_escaping
35
+ end
36
+
21
37
  def connected?
22
38
  status == PGconn::CONNECTION_OK
23
39
  end
@@ -234,3 +234,14 @@ context "Simple stored procedure test" do
234
234
  @server_id_by_sp.should == @server_id # compare it to output from stored procedure
235
235
  end
236
236
  end
237
+
238
+ context "Joiמed MySQL dataset" do
239
+ setup do
240
+ @ds = MYSQL_DB[:nodes].join(:attributes, :node_id => :id)
241
+ end
242
+
243
+ specify "should quote fields correctly" do
244
+ @ds.sql.should == \
245
+ "SELECT * FROM nodes INNER JOIN attributes ON (attributes.`node_id` = nodes.`id`)"
246
+ end
247
+ end
@@ -92,6 +92,11 @@ context "A PostgreSQL dataset" do
92
92
  @d.filter(:name => /bc/).count.should == 2
93
93
  @d.filter(:name => /^bc/).count.should == 1
94
94
  end
95
+
96
+ specify "should consider strings containing backslashes to be escaped string literals" do
97
+ PGconn.quote("\\dingo").should == "E'\\\\dingo'" # literally, E'\\dingo'
98
+ PGconn.quote("dingo").should == "'dingo'"
99
+ end
95
100
  end
96
101
 
97
102
  context "A PostgreSQL dataset in array tuples mode" do
data/spec/dataset_spec.rb CHANGED
@@ -119,6 +119,11 @@ context "A simple dataset" do
119
119
  @dataset.insert_sql(v).should == "INSERT INTO test DEFAULT VALUES"
120
120
  end
121
121
 
122
+ specify "should format an insert statement with string keys" do
123
+ @dataset.insert_sql('name' => 'wxyz', 'price' => 342).
124
+ should match(/INSERT INTO test \(name, price\) VALUES \('wxyz', 342\)|INSERT INTO test \(price, name\) VALUES \(342, 'wxyz'\)/)
125
+ end
126
+
122
127
  specify "should format an insert statement with a model instance" do
123
128
  dbb = Sequel::Database.new
124
129
 
@@ -573,6 +578,10 @@ context "Dataset#literal" do
573
578
  @dataset.update_sql(:a => 'a + 2'.expr).should ==
574
579
  'UPDATE test SET a = a + 2'
575
580
  end
581
+
582
+ specify "should literalize BigDecimal instances correctly" do
583
+ @dataset.literal(BigDecimal.new("80")).should == "80.0"
584
+ end
576
585
  end
577
586
 
578
587
  context "Dataset#from" do
@@ -796,12 +805,12 @@ context "Dataset#qualified_column_name" do
796
805
 
797
806
  specify "should return the same if already qualified" do
798
807
  @dataset.qualified_column_name('test.a'.lit, :items).should == 'test.a'
799
- @dataset.qualified_column_name(:ccc__b, :items).should == 'ccc.b'
808
+ @dataset.qualified_column_name(:ccc__b, :items).should == :ccc__b
800
809
  end
801
810
 
802
811
  specify "should qualify the column with the supplied table name" do
803
- @dataset.qualified_column_name('a'.lit, :items).should == 'items.a'
804
- @dataset.qualified_column_name(:b1, :items).should == 'items.b1'
812
+ @dataset.qualified_column_name('a'.lit, :items).to_s(@dataset).should == 'items.a'
813
+ @dataset.qualified_column_name(:b1, :items).to_s(@dataset).should == 'items.b1'
805
814
  end
806
815
  end
807
816
 
@@ -857,6 +866,12 @@ context "Dataset#uniq" do
857
866
  specify "should be aliased by Dataset#distinct" do
858
867
  @dataset.distinct.sql.should == 'SELECT DISTINCT name FROM test'
859
868
  end
869
+
870
+ specify "should accept an expression list" do
871
+ @dataset.uniq(:a, :b).sql.should == 'SELECT DISTINCT ON (a, b) name FROM test'
872
+
873
+ @dataset.uniq(:stamp.cast_as(:integer), :node_id).sql.should == 'SELECT DISTINCT ON (cast(stamp AS integer), node_id) name FROM test'
874
+ end
860
875
  end
861
876
 
862
877
  context "Dataset#count" do
@@ -1013,14 +1028,14 @@ context "Dataset#join_table" do
1013
1028
  end
1014
1029
 
1015
1030
  specify "should allow for arbitrary conditions in the JOIN clause" do
1016
- @d.join_table(:left_outer, :categories, :id => :category_id, :status => 0).sql.should ==
1017
- 'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.id = items.category_id) AND (categories.status = 0)'
1018
- @d.join_table(:left_outer, :categories, :id => :category_id, :categorizable_type => "Post").sql.should ==
1019
- "SELECT * FROM items LEFT OUTER JOIN categories ON (categories.categorizable_type = 'Post') AND (categories.id = items.category_id)"
1020
- @d.join_table(:left_outer, :categories, :id => :category_id, :timestamp => "CURRENT_TIMESTAMP".lit).sql.should ==
1021
- "SELECT * FROM items LEFT OUTER JOIN categories ON (categories.id = items.category_id) AND (categories.timestamp = CURRENT_TIMESTAMP)"
1022
- @d.join_table(:left_outer, :categories, :id => :category_id, :status => [1, 2, 3]).sql.should ==
1023
- "SELECT * FROM items LEFT OUTER JOIN categories ON (categories.id = items.category_id) AND (categories.status IN (1, 2, 3))"
1031
+ @d.join_table(:left_outer, :categories, :status => 0).sql.should ==
1032
+ 'SELECT * FROM items LEFT OUTER JOIN categories ON (categories.status = 0)'
1033
+ @d.join_table(:left_outer, :categories, :categorizable_type => "Post").sql.should ==
1034
+ "SELECT * FROM items LEFT OUTER JOIN categories ON (categories.categorizable_type = 'Post')"
1035
+ @d.join_table(:left_outer, :categories, :timestamp => "CURRENT_TIMESTAMP".lit).sql.should ==
1036
+ "SELECT * FROM items LEFT OUTER JOIN categories ON (categories.timestamp = CURRENT_TIMESTAMP)"
1037
+ @d.join_table(:left_outer, :categories, :status => [1, 2, 3]).sql.should ==
1038
+ "SELECT * FROM items LEFT OUTER JOIN categories ON (categories.status IN (1, 2, 3))"
1024
1039
  end
1025
1040
  end
1026
1041
 
data/spec/worker_spec.rb CHANGED
@@ -32,7 +32,19 @@ context "A worker" do
32
32
 
33
33
  @w.join
34
34
  values.should == [1, 2, 3]
35
- @w = nil
35
+ end
36
+
37
+ specify "should isolate errors and hold them in #errors" do
38
+ values = []
39
+ @w.add {values << 1}
40
+ @w.async {values << 2}
41
+ @w.async {raise "bad bad bad"}
42
+ @w << proc {values << 3}
43
+
44
+ @w.join
45
+ values.should == [1, 2, 3]
46
+ @w.errors.size.should == 1
47
+ @w.errors.first.message.should == 'bad bad bad'
36
48
  end
37
49
  end
38
50
 
@@ -40,7 +52,13 @@ context "A worker with a given db" do
40
52
  setup do
41
53
  @db = MockDatabase.new
42
54
  @m = Module.new do
43
- def transaction; execute('BEGIN'); yield; execute('COMMIT'); end
55
+ def transaction
56
+ execute('BEGIN')
57
+ yield
58
+ execute('COMMIT')
59
+ rescue
60
+ execute('ROLLBACK')
61
+ end
44
62
  end
45
63
  @db.extend(@m)
46
64
  @w = Sequel::Worker.new(@db)
@@ -60,4 +78,19 @@ context "A worker with a given db" do
60
78
  'COMMIT'
61
79
  ]
62
80
  end
81
+
82
+ specify "should rollback the transaction if any error is raised" do
83
+ @w.async {@db[:items] << {:x => 1}}
84
+ @w.async {sleep 0.2; raise "that's bad"}
85
+ @w.async {@db[:items] << {:x => 2}}
86
+ @w.join
87
+ @db.sqls.should == [
88
+ 'BEGIN',
89
+ 'INSERT INTO items (x) VALUES (1)',
90
+ 'INSERT INTO items (x) VALUES (2)',
91
+ 'ROLLBACK'
92
+ ]
93
+ @w.errors.size.should == 1
94
+ @w.errors.first.message.should == "that's bad"
95
+ end
63
96
  end
metadata CHANGED
@@ -1,33 +1,54 @@
1
1
  --- !ruby/object:Gem::Specification
2
- rubygems_version: 0.9.4
3
- specification_version: 1
4
2
  name: sequel
5
3
  version: !ruby/object:Gem::Version
6
- version: 0.3.4.1
7
- date: 2007-11-10 00:00:00 +02:00
8
- summary: Lightweight ORM library for Ruby
9
- require_paths:
10
- - lib
11
- email: ciconia@gmail.com
12
- homepage: http://sequel.rubyforge.org
13
- rubyforge_project:
14
- description: Lightweight ORM library for Ruby
15
- autorequire:
16
- default_executable:
17
- bindir: bin
18
- has_rdoc: true
19
- required_ruby_version: !ruby/object:Gem::Version::Requirement
20
- requirements:
21
- - - ">="
22
- - !ruby/object:Gem::Version
23
- version: 1.8.4
24
- version:
4
+ version: 0.4.0
25
5
  platform: ruby
26
- signing_key:
27
- cert_chain:
28
- post_install_message:
29
6
  authors:
30
7
  - Sharon Rosner
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2007-11-24 00:00:00 +02:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: metaid
17
+ version_requirement:
18
+ version_requirements: !ruby/object:Gem::Requirement
19
+ requirements:
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: "0"
23
+ version:
24
+ - !ruby/object:Gem::Dependency
25
+ name: ParseTree
26
+ version_requirement:
27
+ version_requirements: !ruby/object:Gem::Requirement
28
+ requirements:
29
+ - - ">="
30
+ - !ruby/object:Gem::Version
31
+ version: 2.0.0
32
+ version:
33
+ - !ruby/object:Gem::Dependency
34
+ name: ruby2ruby
35
+ version_requirement:
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: "0"
41
+ version:
42
+ description: Lightweight ORM library for Ruby
43
+ email: ciconia@gmail.com
44
+ executables:
45
+ - sequel
46
+ extensions: []
47
+
48
+ extra_rdoc_files:
49
+ - README
50
+ - CHANGELOG
51
+ - COPYING
31
52
  files:
32
53
  - COPYING
33
54
  - README
@@ -54,43 +75,46 @@ files:
54
75
  - spec/worker_spec.rb
55
76
  - lib/sequel
56
77
  - lib/sequel/ado.rb
57
- - lib/sequel/array_keys.rb
58
- - lib/sequel/connection_pool.rb
59
- - lib/sequel/core_ext.rb
60
- - lib/sequel/core_sql.rb
61
- - lib/sequel/database.rb
62
- - lib/sequel/dataset
63
- - lib/sequel/dataset/convenience.rb
64
- - lib/sequel/dataset/sequelizer.rb
65
- - lib/sequel/dataset/sql.rb
66
- - lib/sequel/dataset.rb
67
78
  - lib/sequel/db2.rb
68
79
  - lib/sequel/dbi.rb
69
- - lib/sequel/error.rb
70
- - lib/sequel/migration.rb
71
- - lib/sequel/model
72
- - lib/sequel/model/base.rb
73
- - lib/sequel/model/caching.rb
74
- - lib/sequel/model/hooks.rb
75
- - lib/sequel/model/record.rb
76
- - lib/sequel/model/relations.rb
77
- - lib/sequel/model/schema.rb
78
- - lib/sequel/model.rb
80
+ - lib/sequel/informix.rb
79
81
  - lib/sequel/mysql.rb
80
82
  - lib/sequel/odbc.rb
81
83
  - lib/sequel/oracle.rb
82
84
  - lib/sequel/postgres.rb
83
- - lib/sequel/pretty_table.rb
84
- - lib/sequel/schema
85
- - lib/sequel/schema/schema_generator.rb
86
- - lib/sequel/schema/schema_sql.rb
87
- - lib/sequel/schema.rb
88
85
  - lib/sequel/sqlite.rb
89
- - lib/sequel/worker.rb
86
+ - lib/sequel-core
87
+ - lib/sequel-core/array_keys.rb
88
+ - lib/sequel-core/connection_pool.rb
89
+ - lib/sequel-core/core_ext.rb
90
+ - lib/sequel-core/core_sql.rb
91
+ - lib/sequel-core/database.rb
92
+ - lib/sequel-core/dataset
93
+ - lib/sequel-core/dataset/convenience.rb
94
+ - lib/sequel-core/dataset/sequelizer.rb
95
+ - lib/sequel-core/dataset/sql.rb
96
+ - lib/sequel-core/dataset.rb
97
+ - lib/sequel-core/error.rb
98
+ - lib/sequel-core/migration.rb
99
+ - lib/sequel-core/model
100
+ - lib/sequel-core/model/base.rb
101
+ - lib/sequel-core/model/caching.rb
102
+ - lib/sequel-core/model/hooks.rb
103
+ - lib/sequel-core/model/record.rb
104
+ - lib/sequel-core/model/relations.rb
105
+ - lib/sequel-core/model/schema.rb
106
+ - lib/sequel-core/model.rb
107
+ - lib/sequel-core/pretty_table.rb
108
+ - lib/sequel-core/schema
109
+ - lib/sequel-core/schema/schema_generator.rb
110
+ - lib/sequel-core/schema/schema_sql.rb
111
+ - lib/sequel-core/schema.rb
112
+ - lib/sequel-core/worker.rb
90
113
  - lib/sequel.rb
91
114
  - CHANGELOG
92
- test_files: []
93
-
115
+ has_rdoc: true
116
+ homepage: http://sequel.rubyforge.org
117
+ post_install_message:
94
118
  rdoc_options:
95
119
  - --quiet
96
120
  - --title
@@ -105,41 +129,26 @@ rdoc_options:
105
129
  - ^(examples|extras)\/
106
130
  - --exclude
107
131
  - lib/sequel.rb
108
- extra_rdoc_files:
109
- - README
110
- - CHANGELOG
111
- - COPYING
112
- executables:
113
- - sequel
114
- extensions: []
115
-
132
+ require_paths:
133
+ - lib
134
+ required_ruby_version: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: 1.8.4
139
+ version:
140
+ required_rubygems_version: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - ">="
143
+ - !ruby/object:Gem::Version
144
+ version: "0"
145
+ version:
116
146
  requirements: []
117
147
 
118
- dependencies:
119
- - !ruby/object:Gem::Dependency
120
- name: metaid
121
- version_requirement:
122
- version_requirements: !ruby/object:Gem::Version::Requirement
123
- requirements:
124
- - - ">"
125
- - !ruby/object:Gem::Version
126
- version: 0.0.0
127
- version:
128
- - !ruby/object:Gem::Dependency
129
- name: ParseTree
130
- version_requirement:
131
- version_requirements: !ruby/object:Gem::Version::Requirement
132
- requirements:
133
- - - ">="
134
- - !ruby/object:Gem::Version
135
- version: 2.0.0
136
- version:
137
- - !ruby/object:Gem::Dependency
138
- name: ruby2ruby
139
- version_requirement:
140
- version_requirements: !ruby/object:Gem::Version::Requirement
141
- requirements:
142
- - - ">"
143
- - !ruby/object:Gem::Version
144
- version: 0.0.0
145
- version:
148
+ rubyforge_project:
149
+ rubygems_version: 0.9.5
150
+ signing_key:
151
+ specification_version: 2
152
+ summary: Lightweight ORM library for Ruby
153
+ test_files: []
154
+