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/API.rdoc +90 -0
- data/README.rdoc +14 -6
- data/Rakefile +1 -0
- data/VERSION +1 -1
- data/examples/async.rb +11 -6
- data/examples/db.rb +5 -1
- data/examples/scheme.rb +10 -2
- data/ext/adapter.cc +259 -0
- data/ext/adapter.h +13 -0
- data/ext/extconf.rb +21 -0
- data/ext/iostream.cc +44 -0
- data/ext/iostream.h +17 -0
- data/ext/pool.cc +89 -0
- data/ext/pool.h +8 -0
- data/ext/query.cc +38 -0
- data/ext/query.h +17 -0
- data/ext/request.cc +44 -0
- data/ext/request.h +10 -0
- data/ext/result.cc +246 -0
- data/ext/result.h +17 -0
- data/ext/statement.cc +87 -0
- data/ext/statement.h +12 -0
- data/ext/swift.cc +31 -739
- data/ext/swift.h +35 -0
- data/lib/swift/adapter.rb +4 -7
- data/lib/swift/attribute.rb +8 -2
- data/lib/swift/db.rb +7 -16
- data/lib/swift/identity_map.rb +8 -0
- data/lib/swift/migrations.rb +15 -0
- data/lib/swift/pool.rb +2 -1
- data/lib/swift/scheme.rb +0 -8
- data/lib/swift/validations.rb +27 -0
- data/lib/swift.rb +0 -5
- data/swift.gemspec +40 -11
- data/test/helper.rb +6 -6
- data/test/test_adapter.rb +9 -16
- data/test/test_error.rb +26 -0
- data/test/test_io.rb +6 -4
- data/test/test_pool.rb +1 -1
- data/test/test_scheme.rb +51 -0
- data/test/test_timestamps.rb +27 -14
- data/test/test_transactions.rb +76 -0
- data/test/test_validations.rb +46 -0
- metadata +55 -10
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
|
-
|
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
|
data/lib/swift/attribute.rb
CHANGED
@@ -12,12 +12,18 @@ module Swift
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def default
|
15
|
-
@default.respond_to?(:call)
|
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})
|
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
|
data/lib/swift/identity_map.rb
CHANGED
@@ -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::
|
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
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.
|
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-
|
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
|
-
"
|
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/
|
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.
|
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/
|
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/
|
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::
|
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
|
-
|
2
|
-
require 'minitest/spec'
|
1
|
+
$:.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
3
2
|
|
4
|
-
|
5
|
-
|
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::
|
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
|
data/test/test_error.rb
ADDED
@@ -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
|
8
|
-
|
9
|
-
|
10
|
-
|
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
|
data/test/test_scheme.rb
ADDED
@@ -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
|
data/test/test_timestamps.rb
CHANGED
@@ -2,25 +2,38 @@ require_relative 'helper'
|
|
2
2
|
require 'date'
|
3
3
|
|
4
4
|
describe 'Adapter' do
|
5
|
-
supported_by Swift::DB::Postgres
|
5
|
+
supported_by Swift::DB::Postgres do
|
6
6
|
describe 'time parsing and time zones' do
|
7
|
-
|
8
|
-
|
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
|
-
|
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
|
-
|
15
|
-
match = Regexp.new
|
16
|
-
|
17
|
-
|
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
|