data_objects 0.2.0 → 0.9.2
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/README +3 -3
- data/Rakefile +42 -22
- data/TODO +0 -5
- data/lib/data_objects.rb +35 -337
- data/lib/data_objects/command.rb +30 -0
- data/lib/data_objects/connection.rb +88 -0
- data/lib/data_objects/field.rb +19 -0
- data/lib/data_objects/logger.rb +233 -0
- data/lib/data_objects/quoting.rb +98 -0
- data/lib/data_objects/reader.rb +22 -0
- data/lib/data_objects/result.rb +13 -0
- data/lib/data_objects/support/pooling.rb +236 -0
- data/lib/data_objects/transaction.rb +42 -0
- data/spec/command_spec.rb +37 -0
- data/spec/connection_spec.rb +83 -0
- data/spec/dataobjects_spec.rb +1 -0
- data/spec/do_mock.rb +31 -0
- data/spec/reader_spec.rb +18 -0
- data/spec/result_spec.rb +23 -0
- data/spec/spec_helper.rb +5 -0
- data/spec/support/pooling_spec.rb +374 -0
- data/spec/transaction_spec.rb +39 -0
- metadata +80 -36
data/README
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
data_objects
|
2
|
+
============
|
3
3
|
|
4
|
-
A
|
4
|
+
A unified Ruby API for popular databases.
|
data/Rakefile
CHANGED
@@ -1,33 +1,53 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'rake/clean'
|
2
3
|
require 'rake/gempackagetask'
|
4
|
+
require 'spec/rake/spectask'
|
5
|
+
require 'pathname'
|
3
6
|
|
4
|
-
|
5
|
-
|
6
|
-
AUTHOR = "Yehuda Katz"
|
7
|
-
EMAIL = "wycats@gmail.com"
|
8
|
-
HOMEPAGE = "http://dataobjects.devjavu.com"
|
9
|
-
SUMMARY = "The Core DataObjects class"
|
7
|
+
WINDOWS = (RUBY_PLATFORM =~ /mswin|mingw|cygwin/) rescue nil
|
8
|
+
SUDO = WINDOWS ? '' : ('sudo' unless ENV['SUDOLESS'])
|
10
9
|
|
11
10
|
spec = Gem::Specification.new do |s|
|
12
|
-
s.name
|
13
|
-
s.version
|
14
|
-
s.platform
|
15
|
-
s.has_rdoc
|
16
|
-
s.extra_rdoc_files = [
|
17
|
-
s.summary
|
18
|
-
s.description
|
19
|
-
s.author
|
20
|
-
s.email
|
21
|
-
s.homepage
|
22
|
-
s.require_path
|
23
|
-
s.
|
24
|
-
s.files = %w(LICENSE README Rakefile TODO) + Dir.glob("{lib,specs}/**/*")
|
11
|
+
s.name = 'data_objects'
|
12
|
+
s.version = '0.9.2'
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
s.has_rdoc = true
|
15
|
+
s.extra_rdoc_files = %w[ README LICENSE TODO ]
|
16
|
+
s.summary = 'The Core DataObjects class'
|
17
|
+
s.description = s.summary
|
18
|
+
s.author = 'Yehuda Katz'
|
19
|
+
s.email = 'wycats@gmail.com'
|
20
|
+
s.homepage = 'http://rubyforge.org/projects/dorb'
|
21
|
+
s.require_path = 'lib'
|
22
|
+
s.files = FileList[ 'lib/**/*', 'spec/**/*.rb', 'Rakefile', *s.extra_rdoc_files ]
|
25
23
|
end
|
26
24
|
|
25
|
+
spec.add_dependency 'addressable', ">= 1.0.3"
|
26
|
+
spec.add_dependency 'extlib', ">= #{spec.version}"
|
27
|
+
|
27
28
|
Rake::GemPackageTask.new(spec) do |pkg|
|
28
29
|
pkg.gem_spec = spec
|
29
30
|
end
|
30
31
|
|
31
|
-
|
32
|
-
|
33
|
-
|
32
|
+
desc "Install #{spec.name} #{spec.version} (default ruby)"
|
33
|
+
task :install => [ :package ] do
|
34
|
+
sh %{#{SUDO} gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources}, :verbose => false
|
35
|
+
end
|
36
|
+
|
37
|
+
desc "Uninstall #{spec.name} #{spec.version} (default ruby)"
|
38
|
+
task :uninstall => [ :clobber ] do
|
39
|
+
sh "#{SUDO} gem uninstall #{spec.name} -v#{spec.version} -I -x", :verbose => false
|
40
|
+
end
|
41
|
+
|
42
|
+
desc 'Run specifications'
|
43
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
44
|
+
t.spec_opts << '--options' << 'spec/spec.opts' if File.exists?('spec/spec.opts')
|
45
|
+
t.spec_files = Pathname.glob(Pathname.new(__FILE__).dirname + 'spec/**/*_spec.rb')
|
46
|
+
end
|
47
|
+
|
48
|
+
namespace :jruby do
|
49
|
+
desc "Install #{spec.name} #{spec.version} with JRuby"
|
50
|
+
task :install => [ :package ] do
|
51
|
+
sh %{#{SUDO} jruby -S gem install --local pkg/#{spec.name}-#{spec.version} --no-update-sources}, :verbose => false
|
52
|
+
end
|
53
|
+
end
|
data/TODO
CHANGED
data/lib/data_objects.rb
CHANGED
@@ -1,345 +1,43 @@
|
|
1
|
-
require '
|
2
|
-
|
1
|
+
require 'rubygems'
|
2
|
+
gem 'extlib', '>= 0.9'
|
3
|
+
require 'extlib'
|
3
4
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
end
|
5
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'support', 'pooling'))
|
6
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'logger'))
|
7
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'connection'))
|
8
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'transaction'))
|
9
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'command'))
|
10
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'result'))
|
11
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'reader'))
|
12
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'field'))
|
13
|
+
require File.expand_path(File.join(File.dirname(__FILE__), 'data_objects', 'quoting'))
|
14
14
|
|
15
|
-
|
16
|
-
|
17
|
-
DateTime.civil(year, mon, day, hour, min, sec)
|
18
|
-
end
|
19
|
-
|
20
|
-
def to_s_db
|
21
|
-
strftime("%Y-%m-%d %X")
|
22
|
-
end
|
23
|
-
end
|
15
|
+
module DataObjects
|
16
|
+
class LengthMismatchError < StandardError; end
|
24
17
|
|
25
|
-
|
26
|
-
|
27
|
-
STATE_CLOSED = 1
|
28
|
-
|
29
|
-
class Connection
|
30
|
-
|
31
|
-
attr_reader :timeout, :database, :datasource, :server_version, :state
|
32
|
-
|
33
|
-
def initialize(connection_string)
|
34
|
-
end
|
35
|
-
|
36
|
-
def logger
|
37
|
-
@logger || @logger = Logger.new(nil)
|
38
|
-
end
|
39
|
-
|
40
|
-
def logger=(value)
|
41
|
-
@logger = value
|
42
|
-
end
|
43
|
-
|
44
|
-
def begin_transaction
|
45
|
-
# TODO: Hook this up
|
46
|
-
Transaction.new
|
47
|
-
end
|
48
|
-
|
49
|
-
def change_database(database_name)
|
50
|
-
raise NotImplementedError
|
51
|
-
end
|
52
|
-
|
53
|
-
def open
|
54
|
-
raise NotImplementedError
|
55
|
-
end
|
56
|
-
|
57
|
-
def close
|
58
|
-
raise NotImplementedError
|
59
|
-
end
|
60
|
-
|
61
|
-
def create_command(text)
|
62
|
-
Command.new(self, text)
|
63
|
-
end
|
64
|
-
|
65
|
-
def closed?
|
66
|
-
@state == STATE_CLOSED
|
67
|
-
end
|
68
|
-
|
18
|
+
def self.root
|
19
|
+
@root ||= Pathname(__FILE__).dirname.parent.expand_path
|
69
20
|
end
|
70
|
-
|
71
|
-
class Transaction
|
72
|
-
|
73
|
-
attr_reader :connection
|
74
|
-
|
75
|
-
def initialize(conn)
|
76
|
-
@connection = conn
|
77
|
-
end
|
78
|
-
|
79
|
-
# Commits the transaction
|
80
|
-
def commit
|
81
|
-
raise NotImplementedError
|
82
|
-
end
|
83
|
-
|
84
|
-
# Rolls back the transaction
|
85
|
-
def rollback(savepoint = nil)
|
86
|
-
raise NotImplementedError
|
87
|
-
end
|
88
|
-
|
89
|
-
# Creates a savepoint for rolling back later (not commonly supported)
|
90
|
-
def save(name)
|
91
|
-
raise NotImplementedError
|
92
|
-
end
|
93
|
-
|
94
|
-
end
|
95
|
-
|
96
|
-
class Reader
|
97
|
-
include Enumerable
|
98
|
-
|
99
|
-
attr_reader :field_count, :records_affected, :fields
|
100
|
-
|
101
|
-
def each
|
102
|
-
raise NotImplementedError
|
103
|
-
end
|
104
|
-
|
105
|
-
def has_rows?
|
106
|
-
@has_rows
|
107
|
-
end
|
108
|
-
|
109
|
-
def current_row
|
110
|
-
ret = []
|
111
|
-
field_count.times do |i|
|
112
|
-
ret[i] = item(i)
|
113
|
-
end
|
114
|
-
ret
|
115
|
-
end
|
116
|
-
|
117
|
-
def open?
|
118
|
-
@state != STATE_CLOSED
|
119
|
-
end
|
120
|
-
|
121
|
-
def close
|
122
|
-
real_close
|
123
|
-
@reader = nil
|
124
|
-
@state = STATE_CLOSED
|
125
|
-
true
|
126
|
-
end
|
127
|
-
|
128
|
-
def real_close
|
129
|
-
raise NotImplementedError
|
130
|
-
end
|
131
|
-
|
132
|
-
# retrieves the Ruby data type for a particular column number
|
133
|
-
def data_type_name(col)
|
134
|
-
raise ReaderClosed, "You cannot ask for metadata once the reader is closed" if state_closed?
|
135
|
-
end
|
136
|
-
|
137
|
-
# retrieves the name of a particular column number
|
138
|
-
def name(col)
|
139
|
-
raise ReaderClosed, "You cannot ask for metadata once the reader is closed" if state_closed?
|
140
|
-
end
|
141
|
-
|
142
|
-
# retrives the index of the column with a particular name
|
143
|
-
def get_index(name)
|
144
|
-
raise ReaderClosed, "You cannot ask for metadata once the reader is closed" if state_closed?
|
145
|
-
end
|
146
|
-
|
147
|
-
def item(idx)
|
148
|
-
raise ReaderClosed, "You cannot ask for information once the reader is closed" if state_closed?
|
149
|
-
end
|
150
21
|
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
# max_size: the maximum allowed size of the data in the column
|
156
|
-
# precision: the precision (for column types that support it)
|
157
|
-
# scale: the scale (for column types that support it)
|
158
|
-
# unique: boolean specifying whether the values must be unique
|
159
|
-
# key: boolean specifying whether this column is, or is part
|
160
|
-
# of, the primary key
|
161
|
-
# catalog: the name of the database this column is part of
|
162
|
-
# base_name: the original name of the column (if AS was used,
|
163
|
-
# this will provide the original name)
|
164
|
-
# schema: the name of the schema (if supported)
|
165
|
-
# table: the name of the table this column is part of
|
166
|
-
# data_type: the name of the Ruby data type used
|
167
|
-
# allow_null: boolean specifying whether nulls are allowed
|
168
|
-
# db_type: the type specified by the DB
|
169
|
-
# aliased: boolean specifying whether the column has been
|
170
|
-
# renamed using AS
|
171
|
-
# calculated: boolean specifying whether the field is calculated
|
172
|
-
# serial: boolean specifying whether the field is a serial
|
173
|
-
# column
|
174
|
-
# blob: boolean specifying whether the field is a BLOB
|
175
|
-
# readonly: boolean specifying whether the field is readonly
|
176
|
-
def get_schema
|
177
|
-
raise ReaderClosed, "You cannot ask for metadata once the reader is closed" if state_closed?
|
178
|
-
end
|
179
|
-
|
180
|
-
# specifies whether the column identified by the passed in index
|
181
|
-
# is null.
|
182
|
-
def null?(idx)
|
183
|
-
raise ReaderClosed, "You cannot ask for column information once the reader is closed" if state_closed?
|
184
|
-
end
|
185
|
-
|
186
|
-
# Consumes the next result. Returns true if a result is consumed and
|
187
|
-
# false if none is
|
188
|
-
def next
|
189
|
-
raise ReaderClosed, "You cannot increment the cursor once the reader is closed" if state_closed?
|
190
|
-
end
|
191
|
-
|
192
|
-
protected
|
193
|
-
def state_closed?
|
194
|
-
@state == STATE_CLOSED
|
195
|
-
end
|
196
|
-
|
197
|
-
def native_type
|
198
|
-
raise ReaderClosed, "You cannot check the type of a column once the reader is closed" if state_closed?
|
22
|
+
def self.find_const(name)
|
23
|
+
klass = Object
|
24
|
+
name.to_s.split('::').each do |part|
|
25
|
+
klass = klass.const_get(part)
|
199
26
|
end
|
200
|
-
|
27
|
+
klass
|
201
28
|
end
|
202
|
-
|
203
|
-
class ResultData
|
204
|
-
|
205
|
-
def initialize(conn, affected_rows, last_insert_row = nil)
|
206
|
-
@conn, @affected_rows, @last_insert_row = conn, affected_rows, last_insert_row
|
207
|
-
end
|
208
|
-
|
209
|
-
attr_reader :affected_rows, :last_insert_row
|
210
|
-
alias_method :to_i, :affected_rows
|
211
|
-
|
212
|
-
end
|
213
|
-
|
214
|
-
class Schema < Array
|
215
|
-
|
216
|
-
end
|
217
|
-
|
218
|
-
class Command
|
219
|
-
|
220
|
-
attr_reader :text, :timeout, :connection
|
221
|
-
|
222
|
-
# initialize creates a new Command object
|
223
|
-
def initialize(connection, text)
|
224
|
-
@connection, @text = connection, text
|
225
|
-
end
|
226
|
-
|
227
|
-
def execute_non_query(*args)
|
228
|
-
raise LostConnectionError, "the connection to the database has been lost" if @connection.closed?
|
229
|
-
end
|
230
|
-
|
231
|
-
def execute_reader(*args)
|
232
|
-
raise LostConnectionError, "the connection to the database has been lost" if @connection.closed?
|
233
|
-
end
|
234
|
-
|
235
|
-
def prepare
|
236
|
-
raise NotImplementedError
|
237
|
-
end
|
238
|
-
|
239
|
-
# Escape a string of SQL with a set of arguments.
|
240
|
-
# The first argument is assumed to be the SQL to escape,
|
241
|
-
# the remaining arguments (if any) are assumed to be
|
242
|
-
# values to escape and interpolate.
|
243
|
-
#
|
244
|
-
# ==== Examples
|
245
|
-
# escape_sql("SELECT * FROM zoos")
|
246
|
-
# # => "SELECT * FROM zoos"
|
247
|
-
#
|
248
|
-
# escape_sql("SELECT * FROM zoos WHERE name = ?", "Dallas")
|
249
|
-
# # => "SELECT * FROM zoos WHERE name = `Dallas`"
|
250
|
-
#
|
251
|
-
# escape_sql("SELECT * FROM zoos WHERE name = ? AND acreage > ?", "Dallas", 40)
|
252
|
-
# # => "SELECT * FROM zoos WHERE name = `Dallas` AND acreage > 40"
|
253
|
-
#
|
254
|
-
# ==== Warning
|
255
|
-
# This method is meant mostly for adapters that don't support
|
256
|
-
# bind-parameters.
|
257
|
-
def escape_sql(args)
|
258
|
-
sql = text.dup
|
259
|
-
|
260
|
-
unless args.empty?
|
261
|
-
sql.gsub!(/\?/) do |x|
|
262
|
-
quote_value(args.shift)
|
263
|
-
end
|
264
|
-
end
|
265
|
-
|
266
|
-
sql
|
267
|
-
end
|
268
|
-
|
269
|
-
def quote_value(value)
|
270
|
-
return 'NULL' if value.nil?
|
29
|
+
end
|
271
30
|
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
else
|
286
|
-
raise "Don't know how to quote #{value.inspect}"
|
287
|
-
end
|
288
|
-
end
|
289
|
-
end
|
290
|
-
|
291
|
-
def quote_symbol(value)
|
292
|
-
quote_string(value.to_s)
|
293
|
-
end
|
294
|
-
|
295
|
-
def quote_numeric(value)
|
296
|
-
value.to_s
|
297
|
-
end
|
298
|
-
|
299
|
-
def quote_string(value)
|
300
|
-
"'#{value.gsub("'", "''")}'"
|
301
|
-
end
|
302
|
-
|
303
|
-
def quote_class(value)
|
304
|
-
"'#{value.name}'"
|
305
|
-
end
|
306
|
-
|
307
|
-
def quote_time(value)
|
308
|
-
"'#{value.xmlschema}'"
|
309
|
-
end
|
310
|
-
|
311
|
-
def quote_datetime(value)
|
312
|
-
"'#{value.dup}'"
|
313
|
-
end
|
314
|
-
|
315
|
-
def quote_date(value)
|
316
|
-
"'#{value.strftime("%Y-%m-%d")}'"
|
317
|
-
end
|
318
|
-
|
319
|
-
def quote_boolean(value)
|
320
|
-
value.to_s.upcase
|
321
|
-
end
|
322
|
-
|
323
|
-
def quote_array(value)
|
324
|
-
"(#{value.map { |entry| quote_value(entry) }.join(', ')})"
|
325
|
-
end
|
326
|
-
|
327
|
-
end
|
328
|
-
|
329
|
-
class NotImplementedError < StandardError; end
|
330
|
-
|
331
|
-
class ConnectionFailed < StandardError; end
|
332
|
-
|
333
|
-
class ReaderClosed < StandardError; end
|
334
|
-
|
335
|
-
class ReaderError < StandardError; end
|
336
|
-
|
337
|
-
class QueryError < StandardError; end
|
338
|
-
|
339
|
-
class NoInsertError < StandardError; end
|
340
|
-
|
341
|
-
class LostConnectionError < StandardError; end
|
342
|
-
|
343
|
-
class UnknownError < StandardError; end
|
344
|
-
|
345
|
-
end
|
31
|
+
# class ConnectionFailed < StandardError; end
|
32
|
+
#
|
33
|
+
# class ReaderClosed < StandardError; end
|
34
|
+
#
|
35
|
+
# class ReaderError < StandardError; end
|
36
|
+
#
|
37
|
+
# class QueryError < StandardError; end
|
38
|
+
#
|
39
|
+
# class NoInsertError < StandardError; end
|
40
|
+
#
|
41
|
+
# class LostConnectionError < StandardError; end
|
42
|
+
#
|
43
|
+
# class UnknownError < StandardError; end
|