swift 0.4.3 → 0.5.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/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