swift 0.4.3 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
data/ext/swift.h ADDED
@@ -0,0 +1,35 @@
1
+ #ifndef SWIFT_H
2
+ #define SWIFT_H
3
+
4
+ #include <dbic++.h>
5
+ #include <ruby/ruby.h>
6
+ #include <ruby/io.h>
7
+ #include <stdint.h>
8
+
9
+ #define CONST_GET(scope, constant) rb_funcall(scope, rb_intern("const_get"), 1, rb_str_new2(constant))
10
+ #define TO_S(v) rb_funcall(v, rb_intern("to_s"), 0)
11
+ #define CSTRING(v) RSTRING_PTR(TO_S(v))
12
+
13
+ extern VALUE eSwiftError;
14
+ extern VALUE eSwiftArgumentError;
15
+ extern VALUE eSwiftRuntimeError;
16
+ extern VALUE eSwiftConnectionError;
17
+
18
+ #define CATCH_DBI_EXCEPTIONS() \
19
+ catch (dbi::ConnectionError &error) { \
20
+ rb_raise(eSwiftConnectionError, "%s", CSTRING(rb_str_new2(error.what()))); \
21
+ } \
22
+ catch (dbi::Error &error) { \
23
+ rb_raise(eSwiftRuntimeError, "%s", CSTRING(rb_str_new2(error.what()))); \
24
+ }
25
+
26
+ #include "adapter.h"
27
+ #include "iostream.h"
28
+ #include "query.h"
29
+ #include "result.h"
30
+ #include "statement.h"
31
+ #include "request.h"
32
+ #include "pool.h"
33
+
34
+ #endif
35
+
data/lib/swift/adapter.rb CHANGED
@@ -1,13 +1,7 @@
1
1
  module Swift
2
- #--
3
- # TODO: Still not convinced all and first are necessary.
4
2
  class Adapter
5
3
  attr_reader :options
6
4
 
7
- def identity_map
8
- @identity_map ||= IdentityMap.new
9
- end
10
-
11
5
  def get scheme, keys
12
6
  relation = scheme.new(keys)
13
7
  prepare_get(scheme).execute(*relation.tuple.values_at(*scheme.header.keys)).first
@@ -19,7 +13,8 @@ module Swift
19
13
  end
20
14
 
21
15
  def first scheme, conditions = '', *binds, &block
22
- all(scheme, "#{conditions} limit 1", *binds, &block).first
16
+ where = "where #{exchange_names(scheme, conditions)}" unless conditions.empty?
17
+ prepare(scheme, "select * from #{scheme.store} #{where} limit 1").execute(*binds, &block).first
23
18
  end
24
19
 
25
20
  def create scheme, *relations
@@ -38,6 +33,7 @@ module Swift
38
33
  relations.map do |relation|
39
34
  relation = scheme.new(relation) unless relation.kind_of?(scheme)
40
35
  statement.execute(*relation.tuple.values_at(*scheme.header.updatable, *scheme.header.keys))
36
+ relation
41
37
  end
42
38
  end
43
39
 
@@ -113,6 +109,7 @@ module Swift
113
109
  when Type::BigDecimal then 'numeric'
114
110
  when Type::Time then 'timestamp'
115
111
  when Type::Boolean then 'boolean'
112
+ when Type::IO then 'blob'
116
113
  else 'text'
117
114
  end
118
115
  end
@@ -12,12 +12,18 @@ module Swift
12
12
  end
13
13
 
14
14
  def default
15
- @default.respond_to?(:call) ? @default.call : (@default.nil? ? nil : @default.dup)
15
+ if @default.respond_to?(:call)
16
+ @default.call
17
+ elsif Swift.special_constant?(@default)
18
+ @default
19
+ else
20
+ @default.dup
21
+ end
16
22
  end
17
23
 
18
24
  def define_scheme_methods scheme
19
25
  scheme.class_eval <<-RUBY, __FILE__, __LINE__ + 1
20
- def #{name}; tuple.fetch(:#{field}) end
26
+ def #{name}; tuple.fetch(:#{field}, nil) end
21
27
  def #{name}= value; tuple.store(:#{field}, value) end
22
28
  RUBY
23
29
  end
data/lib/swift/db.rb CHANGED
@@ -3,13 +3,6 @@ module Swift
3
3
  class Mysql < Adapter
4
4
  def initialize options = {}
5
5
  super options.update(driver: 'mysql')
6
- execute('select unix_timestamp() - unix_timestamp(utc_timestamp()) as offset') {|r| @tzoffset = r[:offset] }
7
- end
8
-
9
- def timezone *args
10
- super(*args)
11
- execute('select unix_timestamp() - unix_timestamp(utc_timestamp()) as offset') {|r| @tzoffset = r[:offset] }
12
- @tzoffset
13
6
  end
14
7
 
15
8
  def returning?
@@ -20,20 +13,18 @@ module Swift
20
13
  class Postgres < Adapter
21
14
  def initialize options = {}
22
15
  super options.update(driver: 'postgresql')
23
- sql = "select extract(epoch from now())::bigint - extract(epoch from now() at time zone 'UTC')::bigint"
24
- execute('%s as offset' % sql) {|r| @tzoffset = r[:offset] }
25
- end
26
-
27
- def timezone *args
28
- super(*args)
29
- sql = "select extract(epoch from now())::bigint - extract(epoch from now() at time zone 'UTC')::bigint"
30
- execute('%s as offset' % sql) {|r| @tzoffset = r[:offset] }
31
- @tzoffset
32
16
  end
33
17
 
34
18
  def returning?
35
19
  true
36
20
  end
21
+
22
+ def field_definition attribute
23
+ case attribute
24
+ when Type::IO then '%s bytea' % attribute.field
25
+ else super
26
+ end
27
+ end
37
28
  end # Postgres
38
29
  end # DB
39
30
  end # Swift
@@ -27,7 +27,15 @@ module Swift
27
27
  end
28
28
  end # IdentityMap
29
29
 
30
+ class Adapter
31
+ def identity_map
32
+ @identity_map ||= IdentityMap.new
33
+ end
34
+ end
35
+
30
36
  class Scheme
37
+ #--
38
+ # TODO: Redefined method :(
31
39
  def self.load tuple
32
40
  im = [self, *tuple.values_at(*header.keys)]
33
41
  unless scheme = Swift.db.identity_map.get(im)
@@ -0,0 +1,15 @@
1
+ module Swift
2
+ class Scheme
3
+ def self.migrations &migrations
4
+ (class << self; self end).send :define_method, :migrate!, lambda{|db = Swift.db| migrations.call(db) }
5
+ end
6
+
7
+ def self.migrate! db = Swift.db
8
+ db.migrate! self
9
+ end
10
+ end # Scheme
11
+
12
+ def self.migrate! name = nil
13
+ schema.each{|scheme| scheme.migrate!(db(name)) }
14
+ end
15
+ end # Swift
data/lib/swift/pool.rb CHANGED
@@ -19,8 +19,9 @@ module Swift
19
19
  end
20
20
  end # Handler
21
21
 
22
+
22
23
  def initialize size, options
23
- @pool = Swift::ConnectionPool.new size, options
24
+ @pool = Swift::DB::Pool.new size, options
24
25
  @stop_reactor = EM.reactor_running? ? false : true
25
26
  @pending = {}
26
27
  @queue = []
data/lib/swift/scheme.rb CHANGED
@@ -41,14 +41,6 @@ module Swift
41
41
  name ? @store = name : @store
42
42
  end
43
43
 
44
- def migration &migration
45
- (class << self; self end).send(:define_method, :migrate!, lambda{ Swift.db.instance_eval(&migration) })
46
- end
47
-
48
- def migrate!
49
- Swift.db.migrate!(self)
50
- end
51
-
52
44
  def create options = {}
53
45
  Swift.db.create(self, options)
54
46
  end
@@ -0,0 +1,27 @@
1
+ module Swift
2
+ class Errors < Array
3
+ attr_accessor :relation
4
+
5
+ def initialize relation
6
+ @relation = relation
7
+ end
8
+ end # Errors
9
+
10
+ class Scheme
11
+ def self.validations &validations
12
+ define_method :validate do
13
+ errors = Errors.new(self)
14
+ instance_exec errors, &validations
15
+ errors
16
+ end
17
+ end
18
+
19
+ def validate errors = Errors.new(self)
20
+ errors
21
+ end
22
+
23
+ def valid?
24
+ validate.empty?
25
+ end
26
+ end # Scheme
27
+ end # Swift
data/lib/swift.rb CHANGED
@@ -39,10 +39,5 @@ module Swift
39
39
  def schema
40
40
  @schema ||= []
41
41
  end
42
-
43
- def migrate! name = nil
44
- db(name){ schema.each(&:migrate!)}
45
- end
46
42
  end
47
43
  end # Swift
48
-
data/swift.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{swift}
8
- s.version = "0.4.3"
8
+ s.version = "0.5.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Shane Hanna", "Bharanee 'Barney' Rathna"]
12
- s.date = %q{2010-08-07}
12
+ s.date = %q{2010-08-30}
13
13
  s.description = %q{A rational rudimentary database abstraction.}
14
14
  s.email = ["shane.hanna@gmail.com", "deepfryed@gmail.com"]
15
15
  s.extensions = ["ext/extconf.rb"]
@@ -18,46 +18,72 @@ Gem::Specification.new do |s|
18
18
  "README.rdoc"
19
19
  ]
20
20
  s.files = [
21
- "LICENSE",
21
+ "API.rdoc",
22
+ "LICENSE",
22
23
  "README.rdoc",
23
24
  "Rakefile",
24
25
  "VERSION",
26
+ "ext/adapter.cc",
27
+ "ext/adapter.h",
25
28
  "ext/extconf.rb",
29
+ "ext/iostream.cc",
30
+ "ext/iostream.h",
31
+ "ext/pool.cc",
32
+ "ext/pool.h",
33
+ "ext/query.cc",
34
+ "ext/query.h",
35
+ "ext/request.cc",
36
+ "ext/request.h",
37
+ "ext/result.cc",
38
+ "ext/result.h",
39
+ "ext/statement.cc",
40
+ "ext/statement.h",
26
41
  "ext/swift.cc",
42
+ "ext/swift.h",
27
43
  "lib/swift.rb",
28
44
  "lib/swift/adapter.rb",
29
45
  "lib/swift/attribute.rb",
30
46
  "lib/swift/db.rb",
31
47
  "lib/swift/header.rb",
32
48
  "lib/swift/identity_map.rb",
49
+ "lib/swift/migrations.rb",
33
50
  "lib/swift/pool.rb",
34
51
  "lib/swift/scheme.rb",
35
52
  "lib/swift/type.rb",
53
+ "lib/swift/validations.rb",
36
54
  "swift.gemspec",
37
55
  "test/helper.rb",
38
56
  "test/house-explode.jpg",
39
57
  "test/test_adapter.rb",
40
58
  "test/test_encoding.rb",
59
+ "test/test_error.rb",
41
60
  "test/test_identity_map.rb",
42
61
  "test/test_io.rb",
43
62
  "test/test_pool.rb",
44
- "test/test_timestamps.rb"
63
+ "test/test_scheme.rb",
64
+ "test/test_timestamps.rb",
65
+ "test/test_transactions.rb",
66
+ "test/test_validations.rb"
45
67
  ]
46
68
  s.homepage = %q{http://github.com/shanna/swift}
47
69
  s.rdoc_options = ["--charset=UTF-8"]
48
70
  s.require_paths = ["lib"]
49
- s.rubygems_version = %q{1.3.6}
71
+ s.rubygems_version = %q{1.3.7}
50
72
  s.summary = %q{A rational rudimentary database abstraction.}
51
73
  s.test_files = [
52
- "test/test_adapter.rb",
74
+ "test/test_pool.rb",
53
75
  "test/test_io.rb",
76
+ "test/test_validations.rb",
77
+ "test/test_transactions.rb",
78
+ "test/test_adapter.rb",
79
+ "test/test_identity_map.rb",
80
+ "test/test_error.rb",
81
+ "test/helper.rb",
54
82
  "test/test_encoding.rb",
55
83
  "test/test_timestamps.rb",
56
- "test/helper.rb",
57
- "test/test_identity_map.rb",
58
- "test/test_pool.rb",
59
- "examples/async.rb",
84
+ "test/test_scheme.rb",
60
85
  "examples/scheme.rb",
86
+ "examples/async.rb",
61
87
  "examples/db.rb"
62
88
  ]
63
89
 
@@ -65,13 +91,16 @@ Gem::Specification.new do |s|
65
91
  current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
66
92
  s.specification_version = 3
67
93
 
68
- if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
94
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
69
95
  s.add_development_dependency(%q<minitest>, [">= 1.7.0"])
96
+ s.add_development_dependency(%q<eventmachine>, [">= 0"])
70
97
  else
71
98
  s.add_dependency(%q<minitest>, [">= 1.7.0"])
99
+ s.add_dependency(%q<eventmachine>, [">= 0"])
72
100
  end
73
101
  else
74
102
  s.add_dependency(%q<minitest>, [">= 1.7.0"])
103
+ s.add_dependency(%q<eventmachine>, [">= 0"])
75
104
  end
76
105
  end
77
106
 
data/test/helper.rb CHANGED
@@ -1,10 +1,8 @@
1
- require 'minitest/unit'
2
- require 'minitest/spec'
1
+ $:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
2
 
4
- $LOAD_PATH.unshift(File.dirname(__FILE__))
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'minitest/spec'
4
+ require 'minitest/unit'
6
5
  require 'swift'
7
- require 'swift/pool'
8
6
 
9
7
  class MiniTest::Unit::TestCase
10
8
  end
@@ -12,13 +10,13 @@ end
12
10
  class MiniTest::Spec
13
11
  def self.supported_by *adapters, &block
14
12
  adapters.each do |adapter|
15
- # test if adapter can be loaded.
16
13
  begin
17
14
  Swift.setup :default, adapter, db: 'swift_test'
18
15
  rescue => error
19
16
  warn "Unable to setup 'swift_test' db for #{adapter}, #{error.message}. Skipping..."
20
17
  next
21
18
  end
19
+
22
20
  describe("Adapter #{adapter.name}") do
23
21
  before do
24
22
  Swift.setup :default, adapter, db: 'swift_test'
@@ -29,4 +27,6 @@ class MiniTest::Spec
29
27
  end
30
28
  end
31
29
 
30
+ # All tests run in this timezone.
31
+ ENV['TZ'] = ":Australia/Melbourne"
32
32
  MiniTest::Unit.autorun
data/test/test_adapter.rb CHANGED
@@ -44,6 +44,14 @@ describe 'Adapter' do
44
44
  assert sth.execute('Apple Arthurton')
45
45
  assert sth.execute('Benny Arthurton')
46
46
  end
47
+
48
+ it 'has insert_id' do
49
+ sql = case @db
50
+ when Swift::DB::Postgres then %q{insert into users (name, created_at) values (?, now()) returning id}
51
+ when Swift::DB::Mysql then %q{insert into users (name, created_at) values (?, now())}
52
+ end
53
+ assert_kind_of Numeric, @db.prepare(sql).execute('Connie Arthurton').insert_id
54
+ end
47
55
  end
48
56
 
49
57
  describe 'executed prepared statements' do
@@ -76,31 +84,16 @@ describe 'Adapter' do
76
84
  assert_kind_of Hash, @sth.first
77
85
  end
78
86
 
79
- it 'returns array rows for fetchrow' do
80
- assert_kind_of Array, @sth.fetchrow
81
- end
82
-
83
87
  it 'returns a result set on Adapter#execute{}' do
84
88
  @db.execute('select * from users') {|r| assert_kind_of Hash, r }
85
89
  end
86
90
 
87
91
  it 'returns a result set on Adapter#results' do
88
92
  @db.execute('select * from users')
89
- assert_kind_of Swift::ResultSet, @db.results
93
+ assert_kind_of Swift::Result, @db.results
90
94
  end
91
95
  end
92
96
 
93
- describe 'transactions' do
94
- it 'yields db to block' do
95
- Swift.db.transaction do |db|
96
- assert_kind_of Swift::Adapter, db
97
- end
98
-
99
- Swift.db.transaction :sweet do |db|
100
- assert_kind_of Swift::Adapter, db
101
- end
102
- end
103
- end
104
97
 
105
98
  #--
106
99
  # TODO: Not sure how I feel about the block in write; feels like it's just there to get around the fields in the
@@ -0,0 +1,26 @@
1
+ require_relative 'helper'
2
+
3
+ describe 'Error' do
4
+ supported_by Swift::DB::Postgres, Swift::DB::Mysql do
5
+ describe 'prepare' do
6
+ before do
7
+ Swift.db do |db|
8
+ db.execute %q{drop table if exists users}
9
+ db.execute %q{create table users(id serial, name text, primary key(id))}
10
+ end
11
+ end
12
+
13
+ it 'throws a runtime error on invalid sql' do
14
+ assert_raises(SwiftRuntimeError) do
15
+ Swift.db.prepare('garble garble garble')
16
+ end
17
+ end
18
+
19
+ it 'throws a runtime error on invalid bind parameters' do
20
+ assert_raises(SwiftRuntimeError) do
21
+ Swift.db.prepare('select * from users where id > ?').execute
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
data/test/test_io.rb CHANGED
@@ -4,11 +4,13 @@ describe 'Adapter' do
4
4
  supported_by Swift::DB::Postgres, Swift::DB::Mysql do
5
5
  describe 'Storing binary objects' do
6
6
  before do
7
- Swift.db do |db|
8
- type = db.is_a?(Swift::DB::Postgres) ? 'bytea' : 'blob'
9
- db.execute %q{drop table if exists users}
10
- db.execute %Q{create table users(id serial, name text, image #{type}, primary key(id))}
7
+ user = Class.new(Swift::Scheme) do
8
+ store :users
9
+ attribute :id, Swift::Type::Integer, serial: true, key: true
10
+ attribute :name, Swift::Type::String
11
+ attribute :image, Swift::Type::IO
11
12
  end
13
+ Swift.db.migrate! user
12
14
  end
13
15
 
14
16
  it 'stores and retrieves an image' do
data/test/test_pool.rb CHANGED
@@ -1,11 +1,11 @@
1
1
  require_relative 'helper'
2
+ require 'swift/pool'
2
3
 
3
4
  describe 'Adapter' do
4
5
  supported_by Swift::DB::Postgres, Swift::DB::Mysql do
5
6
  describe 'Asynchronous connection pool' do
6
7
  before do
7
8
  Swift.db do |db|
8
- type = db.is_a?(Swift::DB::Postgres) ? 'bytea' : 'blob'
9
9
  db.execute %q{drop table if exists users}
10
10
  db.execute %Q{create table users(id serial, name text)}
11
11
  end
@@ -0,0 +1,51 @@
1
+ require_relative 'helper'
2
+
3
+ describe 'scheme' do
4
+ before do
5
+ @user = Class.new(Swift::Scheme) do
6
+ store :users
7
+ attribute :id, Swift::Type::Integer, serial: true, key: true
8
+ attribute :name, Swift::Type::String, default: "dave"
9
+ attribute :age, Swift::Type::Integer, default: 18
10
+ attribute :email, Swift::Type::String
11
+ attribute :verified, Swift::Type::Boolean, default: false
12
+ attribute :created_at, Swift::Type::Time, default: proc { Time.now }
13
+ end
14
+ end
15
+
16
+ describe 'attributes' do
17
+ it 'defines attributes' do
18
+ instance = @user.new
19
+ %w(id name age email created_at).each do |m|
20
+ assert instance.respond_to?(m), "responds to m"
21
+ assert instance.respond_to?("#{m}="), "responds to m="
22
+ end
23
+ end
24
+ end
25
+
26
+ describe 'instantiation' do
27
+ it 'returns a new instance with defaults' do
28
+ instance = @user.new
29
+ assert_kind_of @user, instance
30
+ assert_kind_of Time, instance.created_at
31
+
32
+ assert_equal nil, instance.id
33
+ assert_equal 'dave', instance.name
34
+ assert_equal 18, instance.age
35
+ assert_equal nil, instance.email
36
+ assert_equal false, instance.verified
37
+ end
38
+
39
+ it 'returns a new instance' do
40
+ instance = @user.new name: 'cary', age: 22, email: 'cary@local'
41
+
42
+ assert_kind_of @user, instance
43
+ assert_kind_of Time, instance.created_at
44
+
45
+ assert_equal nil, instance.id
46
+ assert_equal 'cary', instance.name
47
+ assert_equal 22, instance.age
48
+ assert_equal 'cary@local', instance.email
49
+ end
50
+ end
51
+ end
@@ -2,25 +2,38 @@ require_relative 'helper'
2
2
  require 'date'
3
3
 
4
4
  describe 'Adapter' do
5
- supported_by Swift::DB::Postgres, Swift::DB::Mysql do
5
+ supported_by Swift::DB::Postgres do
6
6
  describe 'time parsing and time zones' do
7
- it 'should set timezone' do
8
- assert Swift.db.timezone(8, 0) # +08:00
7
+
8
+ before do
9
+ @db = Swift.db.class.new Swift.db.options.merge(timezone: 'Asia/Kabul')
9
10
  end
10
11
 
11
12
  it 'should parse timestamps and do conversion accordingly' do
12
- assert Swift.db.timezone(8, 30) # +08:30
13
+ time = DateTime.parse('2010-01-01 15:00:00+04:30')
14
+ assert_timestamp_like time, fetch_timestamp_at(time), 'parses correctly'
15
+ end
16
+
17
+ it 'should parse correctly when DST is on' do
18
+ time = DateTime.parse('2010-10-02 20:31:00+04:30')
19
+ assert_timestamp_like time, fetch_timestamp_at(time), 'DST on'
20
+ end
21
+
22
+ it 'should parse correctly when DST is off' do
23
+ time = DateTime.parse('2010-04-04 20:31:00+04:30')
24
+ assert_timestamp_like time, fetch_timestamp_at(time), 'DST off'
25
+ end
26
+
27
+ def fetch_timestamp_at value
28
+ sql = "select '%s'::timestamp with time zone as now" % value.strftime('%F %T%z')
29
+ @db.execute(sql)
30
+ @db.results.first.fetch(:now)
31
+ end
13
32
 
14
- time = DateTime.parse('2010-01-01 15:00:00+08:30')
15
- match = Regexp.new time.to_time.strftime("%F %H:%M")
16
- sql = if Swift.db.kind_of?(Swift::DB::Postgres)
17
- "select '#{time.strftime('%F %H:%M:%S')}'::timestamp as now"
18
- else
19
- "select timestamp('#{time.strftime('%F %H:%M:%S')}') as now"
20
- end
21
- Swift.db.execute(sql) do |r|
22
- assert_match match, r[:now].to_s, "parses time and does zone conversion"
23
- end
33
+ def assert_timestamp_like expect, given, comment
34
+ match = Regexp.new expect.to_time.strftime('%F %T')
35
+ assert_kind_of DateTime, given
36
+ assert_match match, given.strftime('%F %T'), comment
24
37
  end
25
38
  end
26
39
  end
@@ -0,0 +1,76 @@
1
+ require_relative 'helper'
2
+ describe 'Adapter' do
3
+ supported_by Swift::DB::Postgres do #, Swift::DB::Mysql do
4
+ before do
5
+ Swift.db.execute %q{drop table if exists users}
6
+ Swift.db.execute %q{create table users(id serial, name text, created_at timestamp)}
7
+ end
8
+
9
+ describe 'transactions' do
10
+ before do
11
+ @name = 'test1 - transaction 1'
12
+ @db = Swift.db
13
+ end
14
+
15
+ it 'yields db to block' do
16
+ @db.transaction do |db|
17
+ assert_kind_of Swift::Adapter, db
18
+ end
19
+
20
+ @db.transaction :sweet do |db|
21
+ assert_kind_of Swift::Adapter, db
22
+ end
23
+ end
24
+
25
+ describe 'commits work' do
26
+ before do
27
+ @db.execute('truncate users')
28
+ end
29
+
30
+ after do
31
+ @db.execute('select count(*) as c from users where name = ?', @name) {|r| assert_equal 1, r[:c] }
32
+ end
33
+
34
+ it 'should allow explicit commits' do
35
+ @db.transaction do |db|
36
+ db.execute('insert into users(name) values(?)', @name)
37
+ db.commit
38
+ end
39
+ end
40
+
41
+ it 'should autocommit' do
42
+ @db.transaction do |db|
43
+ db.execute('insert into users(name) values(?)', @name)
44
+ end
45
+ end
46
+ end
47
+
48
+ describe 'rollbacks work' do
49
+
50
+ before do
51
+ @db.execute('truncate users')
52
+ end
53
+
54
+ after do
55
+ @db.execute('select count(*) as c from users where name = ?', @name) {|r| assert_equal 0, r[:c] }
56
+ end
57
+
58
+ it 'should allow explicit rollbacks' do
59
+ @db.transaction do |db|
60
+ db.execute('insert into users(name) values(?)', @name)
61
+ db.rollback
62
+ end
63
+ end
64
+
65
+ it 'should auto rollback' do
66
+ assert_raises(RuntimeError) do
67
+ @db.transaction do |db|
68
+ db.execute('insert into users(name) values(?)', @name)
69
+ raise 'foo'
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end