swift 0.13.0 → 0.14.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,28 @@
1
+ require_relative 'helper'
2
+
3
+ describe 'Adapter' do
4
+ supported_by Swift::DB::Postgres do
5
+ describe 'async operations' do
6
+ it 'can runs queries async' do
7
+ rows = []
8
+ pool = 3.times.map.with_index {|n| Swift.setup n, Swift::DB::Postgres, db: 'swift' }
9
+
10
+ Thread.new do
11
+ pool[0].async_execute('select pg_sleep(0.3), 1 as query_id') {|row| rows << row[:query_id]}
12
+ end
13
+
14
+ Thread.new do
15
+ pool[1].async_execute('select pg_sleep(0.2), 2 as query_id') {|row| rows << row[:query_id]}
16
+ end
17
+
18
+ Thread.new do
19
+ pool[2].async_execute('select pg_sleep(0.1), 3 as query_id') {|row| rows << row[:query_id]}
20
+ end
21
+
22
+ Thread.list.reject {|thread| Thread.current == thread}.each(&:join)
23
+
24
+ assert_equal [3, 2, 1], rows
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,12 @@
1
+ require_relative 'helper'
2
+
3
+ describe 'DateTimeParser' do
4
+ it 'should handle ymd style values' do
5
+ text = "2011-12-31 09:01:02.80300899 +1100"
6
+ time = DateTime.parse(text)
7
+ result = Swift::DateTime.parse(text)
8
+
9
+ assert_equal time.strftime('%F %T %z'), result.strftime('%F %T %z')
10
+ assert_equal time.second_fraction.to_f.round(8), result.second_fraction.to_f.round(8)
11
+ end
12
+ end
data/test/test_error.rb CHANGED
@@ -23,4 +23,27 @@ describe 'Error' do
23
23
  end
24
24
  end
25
25
  end
26
+
27
+ supported_by Swift::DB::Postgres do
28
+ describe 'execute' do
29
+ before do
30
+ Swift.db do |db|
31
+ db.execute %q{drop table if exists users}
32
+ db.execute %q{create table users(id integer, name text, primary key(id))}
33
+ end
34
+ end
35
+
36
+ it 'throws connection error on connection failures' do
37
+ select1 = Swift.db.prepare("select * from users")
38
+ select2 = Swift.db.prepare("select * from users where id > ?")
39
+
40
+ Swift.db.close
41
+
42
+ assert_raises(SwiftConnectionError) { select1.execute }
43
+ assert_raises(SwiftConnectionError) { select2.execute(1) }
44
+ assert_raises(SwiftConnectionError) { Swift.db.execute("select * from users") }
45
+ assert_raises(SwiftConnectionError) { Swift.db.execute("select * from users where id > ?", 1) }
46
+ end
47
+ end
48
+ end
26
49
  end
data/test/test_scheme.rb CHANGED
@@ -10,7 +10,7 @@ describe 'scheme' do
10
10
  attribute :height, Swift::Type::Float, default: 172.25
11
11
  attribute :email, Swift::Type::String
12
12
  attribute :verified, Swift::Type::Boolean, default: false
13
- attribute :created_at, Swift::Type::Time, default: proc { Time.now }
13
+ attribute :created_at, Swift::Type::DateTime, default: proc { Time.now }
14
14
 
15
15
  migrations do |db|
16
16
  db.execute %q{
@@ -1,13 +1,18 @@
1
1
  require_relative 'helper'
2
- require 'date'
3
2
 
4
3
  describe 'Adapter' do
5
4
  supported_by Swift::DB::Postgres do
6
5
  %w(America/Chicago Australia/Melbourne).each do |timezone|
7
6
  describe 'time parsing in %s' % timezone do
8
7
  before do
9
- @db = Swift.db
10
8
  ENV['TZ'] = ":#{timezone}"
9
+ @db = Swift.db
10
+ @db.execute 'create table datetime_test(id serial, ts timestamp with time zone)'
11
+ @db.execute "set time zone '#{timezone}'"
12
+ end
13
+
14
+ after do
15
+ @db.execute 'drop table datetime_test'
11
16
  end
12
17
 
13
18
  it 'should parse timestamps and do conversion accordingly' do
@@ -25,14 +30,19 @@ describe 'Adapter' do
25
30
  assert_timestamp_like time, fetch_timestamp_at(time), 'DST off'
26
31
  end
27
32
 
28
- describe 'Adapter timezone' do
29
- %w(+05:30 -05:30).each do |offset|
30
- it 'should parse timestamps and do conversion accordingly for offset ' + offset do
31
- @db = Swift::DB::Postgres.new(@db.options.merge(timezone: offset))
32
- server = DateTime.parse('2010-01-01 10:00:00')
33
- local = DateTime.parse('2010-01-01 10:00:00 ' + offset)
34
- assert_timestamp_like local, fetch_timestamp_at(server, ''), 'parses correctly'
35
- end
33
+ it 'should store fractional seconds' do
34
+ time = Time.now
35
+ datetime = time.to_datetime
36
+
37
+ @db.execute 'insert into datetime_test(ts) values (?), (?)', time, datetime
38
+ values = @db.execute('select ts from datetime_test').map(&:values).flatten
39
+
40
+ assert_equal 2, values.size
41
+
42
+ # postgres resolution is microsecond.
43
+ values.each do |value|
44
+ assert_equal datetime.strftime('%F %T %z'), value.strftime('%F %T %z')
45
+ assert_equal datetime.second_fraction.to_f.round(6), value.second_fraction.to_f.round(6)
36
46
  end
37
47
  end
38
48
 
@@ -49,8 +59,8 @@ describe 'Adapter' do
49
59
 
50
60
  def assert_timestamp_like expect, given, comment
51
61
  match = Regexp.new expect.to_time.strftime('%F %T')
52
- assert_kind_of Time, given
53
- assert_match match, given.strftime('%F %T'), comment
62
+ assert_kind_of DateTime, given
63
+ assert_match match, given.to_time.strftime('%F %T'), comment
54
64
  end
55
65
  end
56
66
  end
data/test/test_types.rb CHANGED
@@ -37,10 +37,10 @@ describe 'Adapter' do
37
37
  assert_kind_of TrueClass, result[:hacker]
38
38
  assert_kind_of FalseClass, result[:slacker]
39
39
  assert_kind_of Date, result[:created]
40
- assert_kind_of Time, result[:updated]
40
+ assert_kind_of DateTime, result[:updated]
41
41
 
42
42
  assert_equal dt, result[:updated].strftime('%F %T')
43
- assert_equal 65000, result[:updated].usec unless @db.kind_of?(Swift::DB::Mysql)
43
+ assert_equal 65000, result[:updated].to_time.usec unless @db.kind_of?(Swift::DB::Mysql)
44
44
  end
45
45
  end
46
46
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: swift
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.0
4
+ version: 0.14.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -10,11 +10,11 @@ authors:
10
10
  autorequire:
11
11
  bindir: bin
12
12
  cert_chain: []
13
- date: 2011-11-16 00:00:00.000000000Z
13
+ date: 2012-03-21 00:00:00.000000000Z
14
14
  dependencies:
15
15
  - !ruby/object:Gem::Dependency
16
16
  name: minitest
17
- requirement: &6952860 !ruby/object:Gem::Requirement
17
+ requirement: &10639060 !ruby/object:Gem::Requirement
18
18
  none: false
19
19
  requirements:
20
20
  - - ! '>='
@@ -22,18 +22,7 @@ dependencies:
22
22
  version: 1.7.0
23
23
  type: :development
24
24
  prerelease: false
25
- version_requirements: *6952860
26
- - !ruby/object:Gem::Dependency
27
- name: eventmachine
28
- requirement: &6951140 !ruby/object:Gem::Requirement
29
- none: false
30
- requirements:
31
- - - ! '>='
32
- - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :development
35
- prerelease: false
36
- version_requirements: *6951140
25
+ version_requirements: *10639060
37
26
  description: A rational rudimentary database abstraction.
38
27
  email:
39
28
  - shane.hanna@gmail.com
@@ -43,11 +32,11 @@ extensions:
43
32
  - ext/extconf.rb
44
33
  extra_rdoc_files:
45
34
  - LICENSE
46
- - README.rdoc
35
+ - README.md
47
36
  files:
48
37
  - API.rdoc
49
38
  - LICENSE
50
- - README.rdoc
39
+ - README.md
51
40
  - Rakefile
52
41
  - VERSION
53
42
  - ext/adapter.cc
@@ -56,13 +45,11 @@ files:
56
45
  - ext/adapter_io.h
57
46
  - ext/attribute.cc
58
47
  - ext/attribute.h
48
+ - ext/datetime.cc
49
+ - ext/datetime.h
59
50
  - ext/extconf.rb
60
- - ext/pool.cc
61
- - ext/pool.h
62
51
  - ext/query.cc
63
52
  - ext/query.h
64
- - ext/request.cc
65
- - ext/request.h
66
53
  - ext/result.cc
67
54
  - ext/result.h
68
55
  - ext/statement.cc
@@ -77,7 +64,6 @@ files:
77
64
  - lib/swift/header.rb
78
65
  - lib/swift/identity_map.rb
79
66
  - lib/swift/migrations.rb
80
- - lib/swift/pool.rb
81
67
  - lib/swift/scheme.rb
82
68
  - lib/swift/type.rb
83
69
  - lib/swift/validations.rb
@@ -86,11 +72,12 @@ files:
86
72
  - test/house-explode.jpg
87
73
  - test/minitest_teardown_hack.rb
88
74
  - test/test_adapter.rb
75
+ - test/test_async.rb
76
+ - test/test_datetime_parser.rb
89
77
  - test/test_encoding.rb
90
78
  - test/test_error.rb
91
79
  - test/test_identity_map.rb
92
80
  - test/test_io.rb
93
- - test/test_pool.rb
94
81
  - test/test_scheme.rb
95
82
  - test/test_swift.rb
96
83
  - test/test_timestamps.rb
data/ext/pool.cc DELETED
@@ -1,96 +0,0 @@
1
- #include "pool.h"
2
-
3
- VALUE cSwiftPool;
4
-
5
- static void pool_free(dbi::ConnectionPool *self) {
6
- if (self) delete self;
7
- }
8
-
9
- VALUE pool_alloc(VALUE klass) {
10
- dbi::ConnectionPool *pool = 0;
11
- return Data_Wrap_Struct(klass, 0, pool_free, pool);
12
- }
13
-
14
- static dbi::ConnectionPool* pool_handle(VALUE self) {
15
- dbi::ConnectionPool *pool;
16
- Data_Get_Struct(self, dbi::ConnectionPool, pool);
17
- if (!pool) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super ?");
18
- return pool;
19
- }
20
-
21
- VALUE pool_init(VALUE self, VALUE n, VALUE options) {
22
- VALUE db = rb_hash_aref(options, ID2SYM(rb_intern("db")));
23
- VALUE user = rb_hash_aref(options, ID2SYM(rb_intern("user")));
24
- VALUE driver = rb_hash_aref(options, ID2SYM(rb_intern("driver")));
25
-
26
- if (NIL_P(db)) rb_raise(eSwiftArgumentError, "Pool#new called without :db");
27
- if (NIL_P(driver)) rb_raise(eSwiftArgumentError, "#new called without :driver");
28
-
29
- user = NIL_P(user) ? current_user() : user;
30
- if (NUM2INT(n) < 1) rb_raise(eSwiftArgumentError, "Pool#new called with invalid pool size.");
31
-
32
- try {
33
- DATA_PTR(self) = new dbi::ConnectionPool(
34
- NUM2INT(n),
35
- CSTRING(driver),
36
- CSTRING(user),
37
- CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("password")))),
38
- CSTRING(db),
39
- CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("host")))),
40
- CSTRING(rb_hash_aref(options, ID2SYM(rb_intern("port"))))
41
- );
42
-
43
- rb_iv_set(self, "@timezone", rb_hash_aref(options, ID2SYM(rb_intern("timezone"))));
44
- }
45
- CATCH_DBI_EXCEPTIONS();
46
-
47
- return Qnil;
48
- }
49
-
50
- void pool_callback(dbi::AbstractResult *result) {
51
- VALUE callback = (VALUE)result->context;
52
-
53
- // NOTE: C Result object will be deallocated in dbic++
54
- if (!NIL_P(callback)) {
55
- VALUE obj = result_wrap_handle(cSwiftResult, 0, result, false);
56
- rb_iv_set(obj, "@timezone", rb_iv_get(callback, "@timezone"));
57
- rb_proc_call(callback, rb_ary_new3(1, obj));
58
- }
59
- }
60
-
61
- VALUE pool_execute(int argc, VALUE *argv, VALUE self) {
62
- int n;
63
- VALUE sql;
64
- VALUE bind_values;
65
- VALUE callback;
66
- VALUE request = Qnil;
67
-
68
- dbi::ConnectionPool *pool = pool_handle(self);
69
- rb_scan_args(argc, argv, "1*&", &sql, &bind_values, &callback);
70
-
71
- // The only way to pass timezone to the C callback routine.
72
- if (NIL_P(callback))
73
- rb_raise(eSwiftArgumentError, "No block given in Pool#execute");
74
- else
75
- rb_iv_set(callback, "@timezone", rb_iv_get(self, "@timezone"));
76
-
77
- try {
78
- Query query;
79
- query_bind_values(&query, bind_values);
80
- request = request_alloc(cSwiftRequest);
81
- DATA_PTR(request) = pool->execute(CSTRING(sql), query.bind, pool_callback, (void*)callback);
82
- return request;
83
- }
84
- CATCH_DBI_EXCEPTIONS();
85
- }
86
-
87
- void init_swift_pool() {
88
- VALUE mSwift = rb_define_module("Swift");
89
- VALUE mDB = rb_define_module_under(mSwift, "DB");
90
- cSwiftPool = rb_define_class_under(mDB, "Pool", rb_cObject);
91
-
92
- rb_define_alloc_func(cSwiftPool, pool_alloc);
93
-
94
- rb_define_method(cSwiftPool, "initialize", RUBY_METHOD_FUNC(pool_init), 2);
95
- rb_define_method(cSwiftPool, "execute", RUBY_METHOD_FUNC(pool_execute), -1);
96
- }
data/ext/pool.h DELETED
@@ -1,8 +0,0 @@
1
- #ifndef SWIFT_POOL_H
2
- #define SWIFT_POOL_H
3
-
4
- #include "swift.h"
5
-
6
- void init_swift_pool();
7
-
8
- #endif
data/ext/request.cc DELETED
@@ -1,44 +0,0 @@
1
- #include "request.h"
2
-
3
- VALUE cSwiftRequest;
4
-
5
- static void request_free(dbi::Request *request) {
6
- if(request) delete request;
7
- }
8
-
9
- VALUE request_alloc(VALUE klass) {
10
- dbi::Request *request = 0;
11
- return Data_Wrap_Struct(klass, 0, request_free, request);
12
- }
13
-
14
- static dbi::Request* request_handle(VALUE self) {
15
- dbi::Request *request;
16
- Data_Get_Struct(self, dbi::Request, request);
17
- if (!request) rb_raise(eSwiftRuntimeError, "Invalid object, did you forget to call #super ?");
18
- return request;
19
- }
20
-
21
- VALUE request_socket(VALUE self) {
22
- dbi::Request *request = request_handle(self);
23
- try {
24
- return INT2NUM(request->socket());
25
- }
26
- CATCH_DBI_EXCEPTIONS();
27
- }
28
-
29
- VALUE request_process(VALUE self) {
30
- dbi::Request *request = request_handle(self);
31
- try {
32
- return request->process() ? Qtrue : Qfalse;
33
- }
34
- CATCH_DBI_EXCEPTIONS();
35
- }
36
-
37
- void init_swift_request() {
38
- VALUE mSwift = rb_define_module("Swift");
39
- cSwiftRequest = rb_define_class_under(mSwift, "Request", rb_cObject);
40
-
41
- rb_define_alloc_func(cSwiftRequest, request_alloc);
42
- rb_define_method(cSwiftRequest, "socket", RUBY_METHOD_FUNC(request_socket), 0);
43
- rb_define_method(cSwiftRequest, "process", RUBY_METHOD_FUNC(request_process), 0);
44
- }
data/ext/request.h DELETED
@@ -1,10 +0,0 @@
1
- #ifndef SWIFT_REQUEST_H
2
- #define SWIFT_REQUEST_H
3
-
4
- #include "swift.h"
5
-
6
- extern VALUE cSwiftRequest;
7
- VALUE request_alloc(VALUE klass);
8
- void init_swift_request();
9
-
10
- #endif
data/lib/swift/pool.rb DELETED
@@ -1,76 +0,0 @@
1
- require 'eventmachine'
2
-
3
- module Swift
4
- class Pool
5
- module Handler
6
- def initialize request, pool
7
- @request, @pool = request, pool
8
- end
9
-
10
- def socket
11
- @request.socket
12
- end
13
-
14
- def notify_readable
15
- if @request.process
16
- detach
17
- @pool.detach self
18
- end
19
- end
20
-
21
- def notify_writable
22
- notify_readable
23
- end
24
- end # Handler
25
-
26
-
27
- def initialize size, options
28
- @pool = Swift::DB::Pool.new size, options
29
-
30
- # used to be used for db2
31
- @writable = false
32
- @pending = {}
33
- @queue = []
34
- end
35
-
36
- def attach c
37
- @pending[c] = true
38
- end
39
-
40
- def detach c
41
- @pending.delete(c)
42
- unless @queue.empty?
43
- sql, bind, callback = @queue.shift
44
- execute(sql, *bind, &callback)
45
- end
46
- end
47
-
48
- def attached? fd
49
- @pending.keys.select{|c| c.socket == fd}.length > 0
50
- end
51
-
52
- def execute sql, *bind, &callback
53
- request = @pool.execute sql, *bind, &callback
54
- # NOTE EM segfaults if we try to attach same fd twice.
55
- if request && !attached?(request.socket)
56
- EM.watch(request.socket, Handler, request, self) do |c|
57
- attach c
58
- c.notify_writable = @writable
59
- c.notify_readable = true
60
- end
61
- else
62
- @queue << [ sql, bind, callback ]
63
- end
64
- end
65
-
66
- def run &block
67
- EM.run{ yield self }
68
- end
69
- end # Pool
70
-
71
- def self.pool size, name = :default, &block
72
- pool = Pool.new(size, Swift.db(name).options)
73
- pool.run(&block) if block_given?
74
- pool
75
- end
76
- end # Swift
data/test/test_pool.rb DELETED
@@ -1,38 +0,0 @@
1
- require_relative 'helper'
2
- require 'swift/pool'
3
-
4
- describe 'Adapter' do
5
- supported_by Swift::DB::Postgres, Swift::DB::Mysql do
6
- describe 'Asynchronous connection pool' do
7
- before do
8
- Swift.db do |db|
9
- db.execute %q{drop table if exists users}
10
- db.execute %Q{create table users(name varchar(64))}
11
- end
12
- end
13
-
14
- it 'creates connection pool and runs queries' do
15
- rows = []
16
- Swift.pool(5) do |pool|
17
- assert pool
18
- assert Swift.db.write('users', %w{name}, StringIO.new("user1\nuser2\nuser3\n"))
19
- pool.execute('select * from users') do |rs|
20
- rows += rs.to_a
21
- Thread.new do
22
- sleep 0.25
23
- pool.execute('select * from users order by name desc') {|rs| rows += rs.to_a; EM.stop }
24
- end
25
- end
26
- pool.execute('select * from users') do |rs|
27
- rows += rs.to_a
28
- end
29
- end
30
-
31
- data = %w(user1 user2 user3)
32
-
33
- assert_equal 9, rows.length
34
- assert_equal data*2 + data.reverse, rows.map {|r| r[:name] }
35
- end
36
- end
37
- end
38
- end