sbf-data_objects 0.10.17

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.
Files changed (66) hide show
  1. checksums.yaml +7 -0
  2. data/ChangeLog.markdown +115 -0
  3. data/LICENSE +20 -0
  4. data/README.markdown +18 -0
  5. data/Rakefile +20 -0
  6. data/lib/data_objects/byte_array.rb +6 -0
  7. data/lib/data_objects/command.rb +79 -0
  8. data/lib/data_objects/connection.rb +95 -0
  9. data/lib/data_objects/error/connection_error.rb +4 -0
  10. data/lib/data_objects/error/data_error.rb +4 -0
  11. data/lib/data_objects/error/integrity_error.rb +4 -0
  12. data/lib/data_objects/error/sql_error.rb +17 -0
  13. data/lib/data_objects/error/syntax_error.rb +4 -0
  14. data/lib/data_objects/error/transaction_error.rb +4 -0
  15. data/lib/data_objects/error.rb +4 -0
  16. data/lib/data_objects/extension.rb +9 -0
  17. data/lib/data_objects/logger.rb +247 -0
  18. data/lib/data_objects/pooling.rb +243 -0
  19. data/lib/data_objects/quoting.rb +99 -0
  20. data/lib/data_objects/reader.rb +45 -0
  21. data/lib/data_objects/result.rb +21 -0
  22. data/lib/data_objects/spec/lib/pending_helpers.rb +13 -0
  23. data/lib/data_objects/spec/lib/ssl.rb +19 -0
  24. data/lib/data_objects/spec/setup.rb +5 -0
  25. data/lib/data_objects/spec/shared/command_spec.rb +201 -0
  26. data/lib/data_objects/spec/shared/connection_spec.rb +148 -0
  27. data/lib/data_objects/spec/shared/encoding_spec.rb +161 -0
  28. data/lib/data_objects/spec/shared/error/sql_error_spec.rb +23 -0
  29. data/lib/data_objects/spec/shared/quoting_spec.rb +0 -0
  30. data/lib/data_objects/spec/shared/reader_spec.rb +180 -0
  31. data/lib/data_objects/spec/shared/result_spec.rb +67 -0
  32. data/lib/data_objects/spec/shared/typecast/array_spec.rb +29 -0
  33. data/lib/data_objects/spec/shared/typecast/bigdecimal_spec.rb +112 -0
  34. data/lib/data_objects/spec/shared/typecast/boolean_spec.rb +133 -0
  35. data/lib/data_objects/spec/shared/typecast/byte_array_spec.rb +76 -0
  36. data/lib/data_objects/spec/shared/typecast/class_spec.rb +53 -0
  37. data/lib/data_objects/spec/shared/typecast/date_spec.rb +114 -0
  38. data/lib/data_objects/spec/shared/typecast/datetime_spec.rb +140 -0
  39. data/lib/data_objects/spec/shared/typecast/float_spec.rb +115 -0
  40. data/lib/data_objects/spec/shared/typecast/integer_spec.rb +92 -0
  41. data/lib/data_objects/spec/shared/typecast/ipaddr_spec.rb +0 -0
  42. data/lib/data_objects/spec/shared/typecast/nil_spec.rb +107 -0
  43. data/lib/data_objects/spec/shared/typecast/other_spec.rb +41 -0
  44. data/lib/data_objects/spec/shared/typecast/range_spec.rb +29 -0
  45. data/lib/data_objects/spec/shared/typecast/string_spec.rb +130 -0
  46. data/lib/data_objects/spec/shared/typecast/time_spec.rb +111 -0
  47. data/lib/data_objects/transaction.rb +111 -0
  48. data/lib/data_objects/uri.rb +109 -0
  49. data/lib/data_objects/utilities.rb +18 -0
  50. data/lib/data_objects/version.rb +3 -0
  51. data/lib/data_objects.rb +20 -0
  52. data/spec/command_spec.rb +24 -0
  53. data/spec/connection_spec.rb +31 -0
  54. data/spec/do_mock.rb +29 -0
  55. data/spec/do_mock2.rb +29 -0
  56. data/spec/pooling_spec.rb +153 -0
  57. data/spec/reader_spec.rb +19 -0
  58. data/spec/result_spec.rb +21 -0
  59. data/spec/spec_helper.rb +18 -0
  60. data/spec/transaction_spec.rb +37 -0
  61. data/spec/uri_spec.rb +23 -0
  62. data/tasks/release.rake +14 -0
  63. data/tasks/spec.rake +10 -0
  64. data/tasks/yard.rake +9 -0
  65. data/tasks/yardstick.rake +19 -0
  66. metadata +122 -0
@@ -0,0 +1,111 @@
1
+ require 'English'
2
+ require 'socket'
3
+ require 'digest'
4
+ require 'digest/sha2'
5
+
6
+ module DataObjects
7
+ class Transaction
8
+ # The local host name. Do not attempt to resolve in DNS to prevent potentially long delay
9
+ HOST = begin
10
+ Socket.gethostname.to_s
11
+ rescue
12
+ 'localhost'
13
+ end
14
+ @@counter = 0
15
+
16
+ # The connection object allocated for this transaction
17
+ attr_reader :connection
18
+ # A unique ID for this transaction
19
+ attr_reader :id
20
+
21
+ # Instantiate the Transaction subclass that's appropriate for this uri scheme
22
+ def self.create_for_uri(uri)
23
+ uri = URI.parse(uri) if uri.is_a?(String)
24
+ DataObjects.const_get(uri.scheme.capitalize)::Transaction.new(uri)
25
+ end
26
+
27
+ #
28
+ # Creates a Transaction bound to a connection for the given DataObjects::URI
29
+ #
30
+ def initialize(uri, connection = nil)
31
+ @connection = connection || DataObjects::Connection.new(uri)
32
+ # PostgreSQL can't handle the full 64 bytes. This should be enough for everyone.
33
+ @id = Digest::SHA256.hexdigest("#{HOST}:#{$PROCESS_ID}:#{Time.now.to_f}:#{@@counter += 1}")[0..-2]
34
+ end
35
+
36
+ # Close the connection for this Transaction
37
+ def close
38
+ @connection.close
39
+ end
40
+
41
+ def begin
42
+ run 'BEGIN'
43
+ end
44
+
45
+ def commit
46
+ run 'COMMIT'
47
+ end
48
+
49
+ def rollback
50
+ run 'ROLLBACK'
51
+ end
52
+
53
+ def prepare
54
+ not_implemented
55
+ end
56
+
57
+ def begin_prepared
58
+ not_implemented
59
+ end
60
+
61
+ def commit_prepared
62
+ not_implemented
63
+ end
64
+
65
+ def rollback_prepared
66
+ not_implemented
67
+ end
68
+
69
+ def prepare
70
+ not_implemented
71
+ end
72
+
73
+ protected def run(cmd)
74
+ connection.create_command(cmd).execute_non_query
75
+ end
76
+
77
+ private def not_implemented
78
+ raise NotImplementedError
79
+ end
80
+ end
81
+
82
+ class SavePoint < Transaction
83
+ # We don't bounce through DO::<Adapter/scheme>::SavePoint because there
84
+ # doesn't appear to be any custom SQL to support this.
85
+ def self.create_for_uri(uri, connection)
86
+ uri = URI.parse(uri) if uri.is_a?(String)
87
+ DataObjects::SavePoint.new(uri, connection)
88
+ end
89
+
90
+ # SavePoints can only occur in the context of a Transaction, thus they
91
+ # re-use TXN's connection (which was acquired from the connection pool
92
+ # legitimately via DO::Connection.new). We no-op #close in SP because
93
+ # calling DO::Connection#close will release the connection back into the
94
+ # pool (before the top-level Transaction might be done with it).
95
+ def close
96
+ # no-op
97
+ end
98
+
99
+ def begin
100
+ run %(SAVEPOINT "#{@id}")
101
+ end
102
+
103
+ def commit
104
+ run %(RELEASE SAVEPOINT "#{@id}")
105
+ end
106
+
107
+ def rollback
108
+ run %(ROLLBACK TO SAVEPOINT "#{@id}")
109
+ end
110
+ end
111
+ end
@@ -0,0 +1,109 @@
1
+ require 'addressable/uri'
2
+
3
+ module DataObjects
4
+ # A DataObjects URI is of the form scheme://user:password@host:port/path#fragment
5
+ #
6
+ # The elements are all optional except scheme and path:
7
+ # scheme:: The name of a DBMS for which you have a do_\&lt;scheme\&gt; adapter gem installed.
8
+ # If scheme is *jdbc*, the actual DBMS is in the _path_ followed by a colon.
9
+ # user:: The name of the user to authenticate to the database
10
+ # password:: The password to use in authentication
11
+ # host:: The domain name (defaulting to localhost) where the database is available
12
+ # port:: The TCP/IP port number to use for the connection
13
+ # path:: The name or path to the database
14
+ # query:: Parameters for the connection, for example encoding=utf8
15
+ # fragment:: Not currently known to be in use, but available to the adapters
16
+ class URI
17
+ attr_reader :scheme, :subscheme, :user, :password, :host, :port, :path, :query, :fragment
18
+
19
+ # Make a DataObjects::URI object by parsing a string. Simply delegates to Addressable::URI::parse.
20
+ def self.parse(uri)
21
+ return uri if uri.is_a?(self)
22
+
23
+ if uri.is_a?(Addressable::URI)
24
+ scheme = uri.scheme
25
+ elsif uri[0, 4] == 'jdbc'
26
+ scheme = uri[0, 4]
27
+ uri = Addressable::URI.parse(uri[5, uri.length])
28
+ subscheme = uri&.scheme
29
+ else
30
+ uri = Addressable::URI.parse(uri)
31
+ scheme = uri&.scheme
32
+ subscheme = nil
33
+ end
34
+
35
+ new(
36
+ scheme: scheme,
37
+ subscheme: subscheme,
38
+ user: uri&.user,
39
+ password: uri&.password,
40
+ host: uri&.host,
41
+ port: uri&.port,
42
+ path: uri&.path,
43
+ query: uri&.query_values,
44
+ fragment: uri&.fragment,
45
+ relative: !uri.to_s.index('//').nil? # basic (naive) check for relativity / opaqueness
46
+ )
47
+ end
48
+
49
+ def initialize(*args)
50
+ if (component = args.first).is_a?(Hash)
51
+ @scheme = component[:scheme]
52
+ @subscheme = component[:subscheme]
53
+ @user = component[:user]
54
+ @password = component[:password]
55
+ @host = component[:host]
56
+ @port = component[:port]
57
+ @path = component[:path]
58
+ @query = component[:query]
59
+ @fragment = component[:fragment]
60
+ @relative = component[:relative]
61
+ elsif args.size > 1
62
+ warn "DataObjects::URI.new with arguments is deprecated, use a Hash of URI components (#{caller.first})"
63
+ @scheme, @user, @password, @host, @port, @path, @query, @fragment = *args
64
+ else
65
+ raise ArgumentError, "argument should be a Hash of URI components, was: #{args.inspect}"
66
+ end
67
+ end
68
+
69
+ def opaque?
70
+ !@relative
71
+ end
72
+
73
+ def relative?
74
+ @relative
75
+ end
76
+
77
+ # Display this URI object as a string
78
+ def to_s
79
+ string = ''
80
+ string << "#{scheme}:" if scheme
81
+ string << "#{subscheme}:" if subscheme
82
+ string << '//' if relative?
83
+ if user
84
+ string << user.to_s
85
+ string << '@'
86
+ end
87
+ string << host.to_s if host
88
+ string << ":#{port}" if port
89
+ string << path.to_s
90
+ if query
91
+ string << '?' << query.map do |key, value|
92
+ "#{key}=#{value}"
93
+ end.join('&')
94
+ end
95
+ string << "##{fragment}" if fragment
96
+ string
97
+ end
98
+
99
+ # Compare this URI to another for hashing
100
+ def eql?(other)
101
+ to_s.eql?(other.to_s)
102
+ end
103
+
104
+ # Hash this URI
105
+ def hash
106
+ to_s.hash
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,18 @@
1
+ # This is here to remove DataObject's dependency on Extlib.
2
+
3
+ module DataObjects
4
+ # @param name<String> The name of the constant to get, e.g. "Merb::Router".
5
+ #
6
+ # @return <Object> The constant corresponding to the name.
7
+ def self.full_const_get(name)
8
+ list = name.split('::')
9
+ list.shift if list.first.nil? || list.first&.strip&.empty?
10
+ obj = ::Object
11
+ list.each do |x|
12
+ # This is required because const_get tries to look for constants in the
13
+ # ancestor chain, but we only want constants that are HERE
14
+ obj = obj.const_defined?(x) ? obj.const_get(x) : obj.const_missing(x)
15
+ end
16
+ obj
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module DataObjects
2
+ VERSION = '0.10.17'.freeze
3
+ end
@@ -0,0 +1,20 @@
1
+ require 'data_objects/version'
2
+ require 'data_objects/utilities'
3
+ require 'data_objects/logger'
4
+ require 'data_objects/byte_array'
5
+ require 'data_objects/pooling'
6
+ require 'data_objects/connection'
7
+ require 'data_objects/uri'
8
+ require 'data_objects/transaction'
9
+ require 'data_objects/command'
10
+ require 'data_objects/result'
11
+ require 'data_objects/reader'
12
+ require 'data_objects/quoting'
13
+ require 'data_objects/extension'
14
+ require 'data_objects/error'
15
+ require 'data_objects/error/sql_error'
16
+ require 'data_objects/error/connection_error'
17
+ require 'data_objects/error/data_error'
18
+ require 'data_objects/error/integrity_error'
19
+ require 'data_objects/error/syntax_error'
20
+ require 'data_objects/error/transaction_error'
@@ -0,0 +1,24 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Command do
4
+ before do
5
+ @connection = DataObjects::Connection.new('mock://localhost')
6
+ @command = DataObjects::Command.new(@connection, 'SQL STRING')
7
+ end
8
+
9
+ after do
10
+ @connection.close
11
+ end
12
+
13
+ %w(connection execute_non_query execute_reader set_types).each do |meth|
14
+ it "responds to ##{meth}" do
15
+ expect(@command).to respond_to(meth.intern)
16
+ end
17
+ end
18
+
19
+ %w(execute_non_query execute_reader set_types).each do |meth|
20
+ it "raises NotImplementedError on ##{meth}" do
21
+ expect { @command.send(meth.intern, nil) }.to raise_error(NotImplementedError)
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,31 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+ require 'stringio'
3
+
4
+ describe DataObjects::Connection do
5
+ subject { connection }
6
+
7
+ let(:connection) { described_class.new(uri) }
8
+
9
+ after { connection.close }
10
+
11
+ context 'defines a standard API' do
12
+ let(:uri) { 'mock://localhost' }
13
+
14
+ it { is_expected.to respond_to(:dispose) }
15
+ it { is_expected.to respond_to(:create_command) }
16
+
17
+ its(:to_s) { is_expected.to eq 'mock://localhost' }
18
+ end
19
+
20
+ describe 'initialization' do
21
+ context 'with a connection uri as a Addressable::URI' do
22
+ let(:uri) { Addressable::URI.parse('mock://localhost/database') }
23
+
24
+ it { is_expected.to be_kind_of(DataObjects::Mock::Connection) }
25
+ it { is_expected.to be_kind_of(DataObjects::Pooling) }
26
+
27
+ its(:to_s) { is_expected.to eq 'mock://localhost/database' }
28
+ end
29
+
30
+ end
31
+ end
data/spec/do_mock.rb ADDED
@@ -0,0 +1,29 @@
1
+ module DataObjects
2
+ module Mock
3
+ class Connection < DataObjects::Connection
4
+ def initialize(uri)
5
+ @uri = uri
6
+ end
7
+
8
+ def dispose
9
+ nil
10
+ end
11
+ end
12
+
13
+ class Command < DataObjects::Command
14
+ def execute_non_query(*_args)
15
+ Result.new(self, 0, nil)
16
+ end
17
+
18
+ def execute_reader(*_args)
19
+ Reader.new
20
+ end
21
+ end
22
+
23
+ class Result < DataObjects::Result
24
+ end
25
+
26
+ class Reader < DataObjects::Reader
27
+ end
28
+ end
29
+ end
data/spec/do_mock2.rb ADDED
@@ -0,0 +1,29 @@
1
+ module DataObjects
2
+ module Mock2
3
+ class Connection < DataObjects::Connection
4
+ def initialize(uri)
5
+ @uri = uri
6
+ end
7
+
8
+ def dispose
9
+ nil
10
+ end
11
+ end
12
+
13
+ class Command < DataObjects::Command
14
+ def execute_non_query(*_args)
15
+ Result.new(self, 0, nil)
16
+ end
17
+
18
+ def execute_reader(*_args)
19
+ Reader.new
20
+ end
21
+ end
22
+
23
+ class Result < DataObjects::Result
24
+ end
25
+
26
+ class Reader < DataObjects::Reader
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,153 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+ require 'timeout'
3
+
4
+ describe 'DataObjects::Pooling' do
5
+ before do
6
+ Object.send(:remove_const, :Person) if defined?(Person)
7
+
8
+ class ::Person
9
+ include DataObjects::Pooling
10
+
11
+ attr_accessor :name
12
+
13
+ def initialize(name)
14
+ @name = name
15
+ end
16
+
17
+ def dispose
18
+ @name = nil
19
+ end
20
+ end
21
+
22
+ Object.send(:remove_const, :Overwriter) if defined?(Overwriter)
23
+ class ::Overwriter
24
+ def self.new(*args)
25
+ instance = allocate
26
+ instance.send(:initialize, *args)
27
+ instance.overwritten = true
28
+ instance
29
+ end
30
+
31
+ include DataObjects::Pooling
32
+
33
+ attr_accessor :name
34
+
35
+ def initialize(name)
36
+ @name = name
37
+ @overwritten = false
38
+ end
39
+
40
+ def overwritten?
41
+ @overwritten
42
+ end
43
+
44
+ attr_writer :overwritten
45
+
46
+ class << self
47
+ remove_method :pool_size if instance_methods(false).any? { |m| m.to_sym == :pool_size }
48
+ def pool_size
49
+ 2
50
+ end
51
+ end
52
+
53
+ def dispose
54
+ @name = nil
55
+ end
56
+ end
57
+ end
58
+
59
+ after :each do
60
+ DataObjects::Pooling.lock.synchronize do
61
+ DataObjects::Pooling.pools.each do |pool|
62
+ pool.lock.synchronize do
63
+ pool.dispose
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ it 'maintains a size of 1' do
70
+ bob = Person.new('Bob')
71
+ fred = Person.new('Fred')
72
+ ted = Person.new('Ted')
73
+
74
+ Person.__pools.each_value do |pool|
75
+ expect(pool.size).to eq 1
76
+ end
77
+
78
+ bob.release
79
+ fred.release
80
+ ted.release
81
+
82
+ Person.__pools.each_value do |pool|
83
+ expect(pool.size).to eq 1
84
+ end
85
+ end
86
+
87
+ it 'tracks the initialized pools' do
88
+ bob = Person.new('Bob') # Ensure the pool is "primed"
89
+ expect(bob.name).to eq 'Bob'
90
+ expect(bob.instance_variable_get(:@__pool)).not_to be_nil
91
+ expect(Person.__pools.size).to eq 1
92
+ bob.release
93
+ expect(Person.__pools.size).to eq 1
94
+
95
+ expect(DataObjects::Pooling.pools).not_to be_empty
96
+
97
+ sleep(1.2)
98
+
99
+ # NOTE: This assertion is commented out, as our MockConnection objects are
100
+ # currently in the pool.
101
+ # expect(DataObjects::Pooling::pools).to be_empty
102
+ expect(bob.name).to be_nil
103
+ end
104
+
105
+ it 'allows you to overwrite Class#new' do
106
+ bob = Overwriter.new('Bob')
107
+ expect(bob).to be_overwritten
108
+ bob.release
109
+ end
110
+
111
+ it 'allows multiple threads to access the pool' do
112
+ t1 = Thread.new do
113
+ bob = Person.new('Bob')
114
+ sleep(1)
115
+ bob.release
116
+ end
117
+
118
+ expect do
119
+ bob = Person.new('Bob')
120
+ t1.join
121
+ bob.release
122
+ end.not_to raise_error
123
+ end
124
+
125
+ it 'allows you to flush a pool' do
126
+ bob = Overwriter.new('Bob')
127
+ Overwriter.new('Bob').release
128
+ bob.release
129
+
130
+ expect(bob.name).to eq 'Bob'
131
+
132
+ expect(Overwriter.__pools[['Bob']].size).to eq 2
133
+ Overwriter.__pools[['Bob']].flush!
134
+ expect(Overwriter.__pools[['Bob']].size).to eq 0
135
+
136
+ expect(bob.name).to be_nil
137
+ end
138
+
139
+ it 'wakes up the scavenger thread when exiting' do
140
+ bob = Person.new('Bob')
141
+ bob.release
142
+ DataObjects.exiting = true
143
+ sleep(1)
144
+ expect(DataObjects::Pooling.scavenger?).to be false
145
+ end
146
+
147
+ it 'detaches an instance from the pool' do
148
+ bob = Person.new('Bob')
149
+ expect(Person.__pools[['Bob']].size).to eq 1
150
+ bob.detach
151
+ expect(Person.__pools[['Bob']].size).to eq 0
152
+ end
153
+ end
@@ -0,0 +1,19 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Reader do
4
+ subject { command.execute_reader }
5
+
6
+ let(:connection) { DataObjects::Connection.new('mock://localhost') }
7
+ let(:command) { connection.create_command('SELECT * FROM example') }
8
+
9
+ after { connection.close }
10
+
11
+ context 'defines a standard API' do
12
+ it { is_expected.to be_a(Enumerable) }
13
+ it { is_expected.to respond_to(:close) }
14
+ it { is_expected.to respond_to(:next!) }
15
+ it { is_expected.to respond_to(:values) }
16
+ it { is_expected.to respond_to(:fields) }
17
+ it { is_expected.to respond_to(:each) }
18
+ end
19
+ end
@@ -0,0 +1,21 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Result do
4
+ subject { command.execute_non_query }
5
+
6
+ let(:connection) { DataObjects::Connection.new('mock://localhost') }
7
+ let(:command) { connection.create_command('SELECT * FROM example') }
8
+
9
+ after { connection.close }
10
+
11
+ context 'defines a standard API' do
12
+ it 'provides the number of affected rows' do
13
+ is_expected.to respond_to(:to_i)
14
+ expect(subject.to_i).to eq 0
15
+ end
16
+
17
+ it 'provides the id of the inserted row' do
18
+ is_expected.to respond_to(:insert_id)
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,18 @@
1
+ require 'rubygems'
2
+ require 'data_objects'
3
+ require 'rspec'
4
+ require 'rspec/its'
5
+
6
+ module DataObjects
7
+ module Pooling
8
+ class << self
9
+ remove_method :scavenger_interval if instance_methods(false).any? { |m| m.to_sym == :scavenger_interval }
10
+ def scavenger_interval
11
+ 0.5
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ require File.expand_path(File.join(File.dirname(__FILE__), 'do_mock'))
18
+ require File.expand_path(File.join(File.dirname(__FILE__), 'do_mock2'))
@@ -0,0 +1,37 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::Transaction do
4
+ before :each do
5
+ @connection = double('connection')
6
+ expect(DataObjects::Connection).to receive(:new).with('mock://mock/mock').once.and_return(@connection)
7
+ @transaction = DataObjects::Transaction.new('mock://mock/mock')
8
+ end
9
+
10
+ it 'has a HOST constant' do
11
+ expect(DataObjects::Transaction::HOST).not_to eq nil?
12
+ end
13
+
14
+ describe '#initialize' do
15
+ it 'provides a connection' do
16
+ expect(@transaction.connection).to eq @connection
17
+ end
18
+ it 'provides an id' do
19
+ expect(@transaction.id).not_to be_nil
20
+ end
21
+ it 'provides a unique id' do
22
+ expect(DataObjects::Connection).to receive(:new).with('mock://mock/mock2').once.and_return(@connection)
23
+ expect(@transaction.id).not_to eq DataObjects::Transaction.new('mock://mock/mock2').id
24
+ end
25
+ end
26
+ describe '#close' do
27
+ it 'closes its connection' do
28
+ expect(@connection).to receive(:close).once
29
+ expect { @transaction.close }.not_to raise_error
30
+ end
31
+ end
32
+ %i(prepare commit_prepared rollback_prepared).each do |meth|
33
+ it "raises NotImplementedError on #{meth}" do
34
+ expect { @transaction.send(meth) }.to raise_error(NotImplementedError)
35
+ end
36
+ end
37
+ end
data/spec/uri_spec.rb ADDED
@@ -0,0 +1,23 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), 'spec_helper'))
2
+
3
+ describe DataObjects::URI do
4
+ subject { described_class.parse(uri) }
5
+
6
+ context 'parsing parts' do
7
+ let(:uri) { 'mock://username:password@localhost:12345/path?encoding=utf8#fragment' }
8
+
9
+ its(:scheme) { is_expected.to eq 'mock' }
10
+ its(:user) { is_expected.to eq 'username' }
11
+ its(:password) { is_expected.to eq 'password' }
12
+ its(:host) { is_expected.to eq 'localhost' }
13
+ its(:port) { is_expected.to eq 12_345 }
14
+ its(:path) { is_expected.to eq '/path' }
15
+ its(:query) { is_expected.to eq({'encoding' => 'utf8'}) }
16
+ its(:fragment) { is_expected.to eq 'fragment' }
17
+
18
+ it 'provides a correct string representation' do
19
+ expect(subject.to_s).to eq 'mock://username@localhost:12345/path?encoding=utf8#fragment'
20
+ end
21
+ end
22
+
23
+ end
@@ -0,0 +1,14 @@
1
+ desc 'Builds all gems (native, binaries for JRuby and Windows)'
2
+ task :build_all do
3
+ `rake clean`
4
+ `rake build`
5
+ end
6
+
7
+ desc 'Release all gems (native, binaries for JRuby and Windows)'
8
+ task release_all: :build_all do
9
+ Dir.children("pkg").each do |gem_path|
10
+ command = "gem push pkg/#{gem_path}"
11
+ puts "Executing #{command.inspect}:"
12
+ sh command
13
+ end
14
+ end
data/tasks/spec.rake ADDED
@@ -0,0 +1,10 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ RSpec::Core::RakeTask.new(:spec) do |spec|
4
+ spec.pattern = './spec/**/*_spec.rb'
5
+
6
+ require 'simplecov'
7
+ SimpleCov.start do
8
+ minimum_coverage 100
9
+ end
10
+ end
data/tasks/yard.rake ADDED
@@ -0,0 +1,9 @@
1
+ begin
2
+ require 'yard'
3
+
4
+ YARD::Rake::YardocTask.new
5
+ rescue LoadError
6
+ task :yard do
7
+ abort 'YARD is not available. In order to run yard, you must: gem install yard'
8
+ end
9
+ end