rails-dbi 0.1.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/ChangeLog +3694 -0
- data/LICENSE +25 -0
- data/README +271 -0
- data/bin/dbi +518 -0
- data/bin/test_broken_dbi +37 -0
- data/build/Rakefile.dbi.rb +60 -0
- data/examples/test1.pl +39 -0
- data/examples/test1.rb +20 -0
- data/examples/xmltest.rb +8 -0
- data/lib/dbi/base_classes/database.rb +135 -0
- data/lib/dbi/base_classes/driver.rb +47 -0
- data/lib/dbi/base_classes/statement.rb +171 -0
- data/lib/dbi/base_classes.rb +12 -0
- data/lib/dbi/binary.rb +25 -0
- data/lib/dbi/columninfo.rb +107 -0
- data/lib/dbi/exceptions.rb +65 -0
- data/lib/dbi/handles/database.rb +241 -0
- data/lib/dbi/handles/driver.rb +60 -0
- data/lib/dbi/handles/statement.rb +408 -0
- data/lib/dbi/handles.rb +49 -0
- data/lib/dbi/row.rb +270 -0
- data/lib/dbi/sql/preparedstatement.rb +115 -0
- data/lib/dbi/sql.rb +22 -0
- data/lib/dbi/sql_type_constants.rb +75 -0
- data/lib/dbi/trace.rb +91 -0
- data/lib/dbi/types.rb +218 -0
- data/lib/dbi/typeutil.rb +109 -0
- data/lib/dbi/utils/date.rb +59 -0
- data/lib/dbi/utils/tableformatter.rb +112 -0
- data/lib/dbi/utils/time.rb +52 -0
- data/lib/dbi/utils/timestamp.rb +96 -0
- data/lib/dbi/utils/xmlformatter.rb +73 -0
- data/lib/dbi/utils.rb +60 -0
- data/lib/dbi.rb +337 -0
- data/test/dbi/tc_columninfo.rb +94 -0
- data/test/dbi/tc_date.rb +88 -0
- data/test/dbi/tc_dbi.rb +184 -0
- data/test/dbi/tc_row.rb +256 -0
- data/test/dbi/tc_sqlbind.rb +168 -0
- data/test/dbi/tc_statementhandle.rb +29 -0
- data/test/dbi/tc_time.rb +77 -0
- data/test/dbi/tc_timestamp.rb +142 -0
- data/test/dbi/tc_types.rb +268 -0
- data/test/ts_dbi.rb +15 -0
- metadata +132 -0
data/lib/dbi/types.rb
ADDED
@@ -0,0 +1,218 @@
|
|
1
|
+
require 'time'
|
2
|
+
require 'bigdecimal'
|
3
|
+
require 'rational'
|
4
|
+
|
5
|
+
module DBI
|
6
|
+
#
|
7
|
+
# Interface to convert SQL result sets to native Ruby types.
|
8
|
+
#
|
9
|
+
# Type is used to convert result sets, which differ from bound variables
|
10
|
+
# (which generally go in the opposite direction). For those, see
|
11
|
+
# DBI::TypeUtil#convert and DBI::TypeUtil#register_conversion.
|
12
|
+
#
|
13
|
+
# Type objects have a simple interface: they implement a +parse+ method
|
14
|
+
# which takes the result from the DBD and attempts to convert it to the
|
15
|
+
# native type. In the event that they do not do this successfully, they are
|
16
|
+
# expected to return the object in its original form.
|
17
|
+
#
|
18
|
+
# As a result, many of the built-in Type classes fallback to simpler forms:
|
19
|
+
# Float falls back to Integer, Integer to Varchar, etc. It's questionable
|
20
|
+
# at this point if it's desirable to do this, but testing has so far proven
|
21
|
+
# it a non-issue.
|
22
|
+
#
|
23
|
+
# To reiterate, it is *never acceptable* to return +nil+ or some other
|
24
|
+
# placeholder when an object will not successfully parse. Return the object
|
25
|
+
# handed to you.
|
26
|
+
#
|
27
|
+
# Types must also handle +nil+ as a result to parse. In this case, the
|
28
|
+
# advisable solution is to just let the +nil+ pass through, as it's usually
|
29
|
+
# indicative of a SQL NULL result.
|
30
|
+
#
|
31
|
+
# DBI::Row handles delegation of these objects as a converter for the
|
32
|
+
# results. Typically, the type object is a class inferred from
|
33
|
+
# DBI::TypeUtil#type_name_to_module ran against the ColumnInfo field
|
34
|
+
# +type_name+. However, the the +dbi_type+ field can be used in its place
|
35
|
+
# to directly associate a Type object with the column in the DBD, and
|
36
|
+
# end-users can leverage StatementHandle#bind_coltype to manually tweak
|
37
|
+
# this transformation.
|
38
|
+
#
|
39
|
+
# As stated before, Type objects are objects. These objects may be Modules
|
40
|
+
# or Classes (and typically are), but there is no reason a traditional
|
41
|
+
# constructed object with a +parse+ method cannot be returned; in fact, it
|
42
|
+
# is used in a few spots to emulate complex types such as PostgreSQL
|
43
|
+
# arrays. Look into the +dbi_type+ ColumnInfo field to pass these types
|
44
|
+
# around.
|
45
|
+
#
|
46
|
+
module Type
|
47
|
+
#
|
48
|
+
# Represents a SQL NULL.
|
49
|
+
#
|
50
|
+
class Null
|
51
|
+
def self.parse(obj)
|
52
|
+
return nil if obj.to_s.match(/^null$/i)
|
53
|
+
return obj
|
54
|
+
end
|
55
|
+
end
|
56
|
+
|
57
|
+
#
|
58
|
+
# Represents a SQL char or varchar. General fallback class.
|
59
|
+
#
|
60
|
+
class Varchar
|
61
|
+
def self.parse(obj)
|
62
|
+
return obj unless obj
|
63
|
+
return obj.to_s if obj.respond_to? :to_s
|
64
|
+
return obj.to_str if obj.respond_to? :to_str
|
65
|
+
return obj
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
#
|
70
|
+
# Represents a whole number. Falls back to Varchar.
|
71
|
+
#
|
72
|
+
class Integer < Varchar
|
73
|
+
def self.parse(obj)
|
74
|
+
return nil if Null.parse(obj).nil?
|
75
|
+
return obj.to_i if obj.respond_to? :to_i
|
76
|
+
super
|
77
|
+
end
|
78
|
+
end
|
79
|
+
|
80
|
+
#
|
81
|
+
# Represents a decimal number with floating-point precision. Falls back
|
82
|
+
# to Integer.
|
83
|
+
#
|
84
|
+
class Float < Integer
|
85
|
+
def self.parse(obj)
|
86
|
+
return nil if Null.parse(obj).nil?
|
87
|
+
return obj.to_f if obj.respond_to? :to_f
|
88
|
+
super
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
#
|
93
|
+
# Represents a Decimal with real precision (BigDecimal). Falls back to
|
94
|
+
# Float.
|
95
|
+
#
|
96
|
+
class Decimal < Float
|
97
|
+
def self.parse(obj)
|
98
|
+
BigDecimal.new(obj) rescue super
|
99
|
+
end
|
100
|
+
end
|
101
|
+
|
102
|
+
#
|
103
|
+
# Represents a SQL TIMESTAMP and returns DateTime. Falls back to Null.
|
104
|
+
#
|
105
|
+
class Timestamp < Null
|
106
|
+
def self.create(year, month, day, hour, min, sec, usec=0, of=0)
|
107
|
+
# DateTime will remove leap and leap-leap seconds
|
108
|
+
sec = 59 if sec > 59
|
109
|
+
# store this before we modify it
|
110
|
+
civil = year, month, day
|
111
|
+
time = hour, min, sec, usec
|
112
|
+
|
113
|
+
date = ::DateTime.civil(year, month, day, hour, min, sec, of)
|
114
|
+
date += usec
|
115
|
+
#prefill_cache date, civil, time
|
116
|
+
date
|
117
|
+
end
|
118
|
+
|
119
|
+
# FIXME these methods are broken, I don't know why, and I don't really care right now.
|
120
|
+
# we shouldn't be playing in datetime's garden anyways.
|
121
|
+
if RUBY_VERSION =~ /^1\.8\./
|
122
|
+
def self.prefill_cache date, civil, time
|
123
|
+
time[3] /= 86400000000.0
|
124
|
+
date.instance_variable_set :"@__#{:civil.to_i}__", [civil]
|
125
|
+
date.instance_variable_set :"@__#{:time.to_i}__", [time]
|
126
|
+
end
|
127
|
+
else
|
128
|
+
def self.prefill_cache date, civil, time
|
129
|
+
time[3] /= 1000000.0
|
130
|
+
date.instance_variable_get(:@__ca__)[:civil.object_id] = civil
|
131
|
+
date.instance_variable_get(:@__ca__)[:time.object_id] = time
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def self.parse_string str
|
136
|
+
# special casing the common formats here gives roughly an
|
137
|
+
# 8-fold speed boost over using Date._parse
|
138
|
+
case str
|
139
|
+
when /^(\d{4})-(\d{2})-(\d{2})(?: (\d{2}):(\d{2}):(\d{2})(\.\d+)?)?(?: ([+-]?\d{2}):?(\d{2}))?$/
|
140
|
+
parts = $~[1..-4].map { |s| s.to_i }
|
141
|
+
# i feel unclean. if we have fractional seconds, pad the number and then stuff it into a rational.
|
142
|
+
if $7
|
143
|
+
frac = $7.to_f * 10000000
|
144
|
+
parts << Rational(frac.to_i, 864000000000)
|
145
|
+
else
|
146
|
+
parts << 0
|
147
|
+
end
|
148
|
+
parts << Rational(($8 || 0).to_i * 60 + ($9 || 0).to_i, 1440)
|
149
|
+
else
|
150
|
+
parts = ::Date._parse(str).values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction, :offset)
|
151
|
+
# some defaults
|
152
|
+
today = nil
|
153
|
+
8.times do |i|
|
154
|
+
next if parts[i]
|
155
|
+
today ||= ::Time.now.to_a.values_at(5, 4, 3) + [0, 0, 0, 0, 0]
|
156
|
+
parts[i] = today[i]
|
157
|
+
end
|
158
|
+
parts[6] = parts[6].kind_of?(Rational) ? parts[6] : Rational(parts[6], 1)
|
159
|
+
parts[6] *= Rational(1, 86400)
|
160
|
+
parts[7] = Rational(parts[7], 86400)
|
161
|
+
end
|
162
|
+
parts
|
163
|
+
end
|
164
|
+
|
165
|
+
def self.parse(obj)
|
166
|
+
case obj
|
167
|
+
when ::DateTime
|
168
|
+
return obj
|
169
|
+
when ::Date
|
170
|
+
return create(obj.year, obj.month, obj.day, 0, 0, 0)
|
171
|
+
when ::Time
|
172
|
+
return create(obj.year, obj.month, obj.day, obj.hour, obj.min, obj.sec, Rational(obj.usec, 86400000000), Rational(obj.utc_offset, 86400))
|
173
|
+
else
|
174
|
+
obj = super
|
175
|
+
return obj unless obj
|
176
|
+
return create(*parse_string(obj.to_s)) if obj.respond_to? :to_s
|
177
|
+
return create(*parse_string(obj.to_str)) if obj.respond_to? :to_str
|
178
|
+
return obj
|
179
|
+
end
|
180
|
+
end
|
181
|
+
end
|
182
|
+
|
183
|
+
#
|
184
|
+
# Represents a SQL BOOLEAN. Returns true/false. Falls back to Null.
|
185
|
+
#
|
186
|
+
class Boolean < Null
|
187
|
+
def self.parse(obj)
|
188
|
+
obj = super
|
189
|
+
|
190
|
+
return nil if obj.nil?
|
191
|
+
|
192
|
+
if obj == false or obj.kind_of? FalseClass
|
193
|
+
return false
|
194
|
+
elsif obj.kind_of? TrueClass
|
195
|
+
return true
|
196
|
+
else
|
197
|
+
case obj
|
198
|
+
when 't'
|
199
|
+
return true
|
200
|
+
when 'f'
|
201
|
+
return false
|
202
|
+
end
|
203
|
+
|
204
|
+
if obj.respond_to? :to_i
|
205
|
+
if obj.to_i == 0
|
206
|
+
return false
|
207
|
+
else
|
208
|
+
return true
|
209
|
+
end
|
210
|
+
else
|
211
|
+
# punt
|
212
|
+
return nil
|
213
|
+
end
|
214
|
+
end
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
end
|
data/lib/dbi/typeutil.rb
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
module DBI
|
2
|
+
#
|
3
|
+
# TypeUtil is a series of utility methods for type management.
|
4
|
+
#
|
5
|
+
class TypeUtil
|
6
|
+
@@conversions = { }
|
7
|
+
|
8
|
+
#
|
9
|
+
# Register a conversion for a DBD. This applies to bound parameters for
|
10
|
+
# outgoing statements; please look at DBI::Type for result sets.
|
11
|
+
#
|
12
|
+
# Conversions are given a driver_name, which is then used to look up
|
13
|
+
# the conversion to perform on the object. Please see #convert for more
|
14
|
+
# information. Driver names are typically provided by the DBD, but may
|
15
|
+
# be overridden at any stage temporarily by assigning to the
|
16
|
+
# +driver_name+ attribute for the various handles.
|
17
|
+
#
|
18
|
+
# A conversion block is normally a +case+ statement that identifies
|
19
|
+
# various native ruby types and converts them to string, but ultimately
|
20
|
+
# the result type is dependent on low-level driver. The resulting
|
21
|
+
# object will be fed to the query as the bound value.
|
22
|
+
#
|
23
|
+
# The result of the block is two arguments, the first being the result
|
24
|
+
# object, and the second being a +cascade+ flag, which if true
|
25
|
+
# instructs #convert to run the result through the +default+ conversion
|
26
|
+
# as well and use its result. This is advantageous when you just need
|
27
|
+
# to convert everything to string, and allow +default+ to properly escape
|
28
|
+
# it.
|
29
|
+
#
|
30
|
+
def self.register_conversion(driver_name, &block)
|
31
|
+
raise "Must provide a block" unless block_given?
|
32
|
+
@@conversions[driver_name] = block
|
33
|
+
end
|
34
|
+
|
35
|
+
#
|
36
|
+
# Convert object for +driver_name+. See #register_conversion for a
|
37
|
+
# complete explanation of how type conversion is performed.
|
38
|
+
#
|
39
|
+
# If the conversion is instructed to cascade, it will go to the special
|
40
|
+
# "default" conversion, which is a pre-defined common case (and
|
41
|
+
# mutable) ruleset for native types. Note that it will use the result
|
42
|
+
# from the first conversion, not what was originally passed. Be sure to
|
43
|
+
# leave the object untouched if that is your intent. E.g., if your DBD
|
44
|
+
# converts an Integer to String and tells it to cascade, the "default"
|
45
|
+
# conversion will get a String and quote it, not an Integer (which has
|
46
|
+
# different rules).
|
47
|
+
#
|
48
|
+
def self.convert(driver_name, obj)
|
49
|
+
if @@conversions[driver_name]
|
50
|
+
newobj, cascade = @@conversions[driver_name].call(obj)
|
51
|
+
if cascade
|
52
|
+
return @@conversions["default"].call(newobj)
|
53
|
+
end
|
54
|
+
return newobj
|
55
|
+
end
|
56
|
+
|
57
|
+
return @@conversions["default"].call(obj)
|
58
|
+
end
|
59
|
+
|
60
|
+
#
|
61
|
+
# Convenience method to match many SQL named types to DBI::Type classes. If
|
62
|
+
# none can be matched, returns DBI::Type::Varchar.
|
63
|
+
#
|
64
|
+
def self.type_name_to_module(type_name)
|
65
|
+
case type_name
|
66
|
+
when /^int(?:\d+|eger)?$/i
|
67
|
+
DBI::Type::Integer
|
68
|
+
when /^varchar$/i, /^character varying$/i
|
69
|
+
DBI::Type::Varchar
|
70
|
+
when /^(?:float|real)$/i
|
71
|
+
DBI::Type::Float
|
72
|
+
when /^bool(?:ean)?$/i, /^tinyint$/i
|
73
|
+
DBI::Type::Boolean
|
74
|
+
when /^time(?:stamp(?:tz)?)?$/i
|
75
|
+
DBI::Type::Timestamp
|
76
|
+
when /^(?:decimal|numeric)$/i
|
77
|
+
DBI::Type::Decimal
|
78
|
+
else
|
79
|
+
DBI::Type::Varchar
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
DBI::TypeUtil.register_conversion("default") do |obj|
|
86
|
+
case obj
|
87
|
+
when DBI::Binary # these need to be handled specially by the driver
|
88
|
+
obj
|
89
|
+
when ::NilClass
|
90
|
+
nil
|
91
|
+
when ::TrueClass
|
92
|
+
"'1'"
|
93
|
+
when ::FalseClass
|
94
|
+
"'0'"
|
95
|
+
when ::Time, ::Date, ::DateTime
|
96
|
+
"'#{::DateTime.parse(obj.to_s).strftime("%Y-%m-%dT%H:%M:%S")}'"
|
97
|
+
when ::String, ::Symbol
|
98
|
+
obj = obj.to_s
|
99
|
+
obj = obj.gsub(/\\/) { "\\\\" }
|
100
|
+
obj = obj.gsub(/'/) { "''" }
|
101
|
+
"'#{obj}'"
|
102
|
+
when ::BigDecimal
|
103
|
+
obj.to_s("F")
|
104
|
+
when ::Numeric
|
105
|
+
obj.to_s
|
106
|
+
else
|
107
|
+
"'#{obj.to_s}'"
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module DBI
|
2
|
+
#
|
3
|
+
# Represents a Date.
|
4
|
+
#
|
5
|
+
# DEPRECATED: Please use a regular Date or DateTime object.
|
6
|
+
#
|
7
|
+
class Date
|
8
|
+
attr_accessor :year, :month, :day
|
9
|
+
|
10
|
+
# Aliases
|
11
|
+
alias :mon :month
|
12
|
+
alias :mon= :month=
|
13
|
+
alias :mday :day
|
14
|
+
alias :mday= :day=
|
15
|
+
|
16
|
+
# Returns a new Time object based on the year, month and day or, if a
|
17
|
+
# Time object was passed to the constructor, returns that object.
|
18
|
+
def to_time
|
19
|
+
@original_time || ::Time.local(@year, @month, @day, 0, 0, 0)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns a new Date object based on the year, month and day or, if a
|
23
|
+
# Date object was passed to the constructor, returns that object.
|
24
|
+
def to_date
|
25
|
+
@original_date || ::Date.new(@year, @month, @day)
|
26
|
+
end
|
27
|
+
|
28
|
+
# Returns a DBI::Date object as a string in YYYY-MM-DD format.
|
29
|
+
def to_s
|
30
|
+
sprintf("%04d-%02d-%02d", @year, @month, @day)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
# DBI::Date.new(year = 0, month = 0, day = 0)
|
36
|
+
# DBI::Date.new(Date)
|
37
|
+
# DBI::Date.new(Time)
|
38
|
+
#
|
39
|
+
# Creates and returns a new DBI::Date object. It's similar to the
|
40
|
+
# standard Date class' constructor except that it also accepts a
|
41
|
+
# Date or Time object.
|
42
|
+
def initialize(year=0, month=0, day=0)
|
43
|
+
case year
|
44
|
+
when ::Date
|
45
|
+
@year, @month, @day = year.year, year.month, year.day
|
46
|
+
@original_date = year
|
47
|
+
when ::Time
|
48
|
+
@year, @month, @day = year.year, year.month, year.day
|
49
|
+
@original_time = year
|
50
|
+
else
|
51
|
+
@year, @month, @day = year, month, day
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
public
|
56
|
+
|
57
|
+
deprecate :initialize, :public
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
module DBI
|
2
|
+
module Utils
|
3
|
+
# Formats a resultset in a textual table, suitable for printing.
|
4
|
+
module TableFormatter
|
5
|
+
|
6
|
+
def self.coerce(obj) # :nodoc:
|
7
|
+
# FIXME this is probably short-sighted.
|
8
|
+
obj = "NULL" if obj.nil?
|
9
|
+
obj = (obj.kind_of?(Array) or obj.kind_of?(Hash)) ? obj.inspect : obj.to_s
|
10
|
+
return obj
|
11
|
+
end
|
12
|
+
|
13
|
+
# Perform the formatting.
|
14
|
+
#
|
15
|
+
# * +header+: table headers, as you'd expect they correspond to each column in the row.
|
16
|
+
# * +rows+: array of array (or DBI::Row) which represent the data.
|
17
|
+
# * +header_orient+: jusification of the header. :left, :right, or :center.
|
18
|
+
# * +rows_orient+: justification of the rows. same as +header_orient+.
|
19
|
+
# * +indent+: number of spaces to indent each line in the output.
|
20
|
+
# * +cellspace+: number of spaces to pad the cell on the left and right.
|
21
|
+
# * +pagebreak_after+: introduce a pagebreak each +n+ rows.
|
22
|
+
# * +output+: object that responds to `<<` which will contain the output. Default is STDOUT.
|
23
|
+
#
|
24
|
+
# If a block is provided, +output+ will be yielded each row if
|
25
|
+
# +pagebreak+ is nil, otherwise it will be yielded when the output
|
26
|
+
# is complete.
|
27
|
+
#--
|
28
|
+
# TODO: add a nr-column where the number of the column is shown
|
29
|
+
#++
|
30
|
+
def self.ascii(header,
|
31
|
+
rows,
|
32
|
+
header_orient=:left,
|
33
|
+
rows_orient=:left,
|
34
|
+
indent=2,
|
35
|
+
cellspace=1,
|
36
|
+
pagebreak_after=nil,
|
37
|
+
output=STDOUT)
|
38
|
+
|
39
|
+
if rows.size == 0 or rows[0].size == 0
|
40
|
+
output.puts "No rows selected"
|
41
|
+
return
|
42
|
+
end
|
43
|
+
|
44
|
+
header_orient ||= :left
|
45
|
+
rows_orient ||= :left
|
46
|
+
indent ||= 2
|
47
|
+
cellspace ||= 1
|
48
|
+
|
49
|
+
# pagebreak_after n-rows (without counting header or split-lines)
|
50
|
+
# yield block with output as param after each pagebreak (not at the end)
|
51
|
+
|
52
|
+
col_lengths = (0...(header.size)).collect do |colnr|
|
53
|
+
[
|
54
|
+
(0...rows.size).collect { |rownr|
|
55
|
+
value = rows[rownr][colnr]
|
56
|
+
coerce(value).size
|
57
|
+
}.max,
|
58
|
+
header[colnr].size
|
59
|
+
].max
|
60
|
+
end
|
61
|
+
|
62
|
+
indent = " " * indent
|
63
|
+
|
64
|
+
split_line = indent + "+"
|
65
|
+
col_lengths.each {|col| split_line << "-" * (col+cellspace*2) + "+" }
|
66
|
+
|
67
|
+
cellspace = " " * cellspace
|
68
|
+
|
69
|
+
output_row = proc {|row, orient|
|
70
|
+
output << indent + "|"
|
71
|
+
row.each_with_index {|c,i|
|
72
|
+
output << cellspace
|
73
|
+
|
74
|
+
str = coerce(c)
|
75
|
+
|
76
|
+
output << case orient
|
77
|
+
when :left then str.ljust(col_lengths[i])
|
78
|
+
when :right then str.rjust(col_lengths[i])
|
79
|
+
when :center then str.center(col_lengths[i])
|
80
|
+
end
|
81
|
+
output << cellspace
|
82
|
+
output << "|"
|
83
|
+
}
|
84
|
+
output << "\n"
|
85
|
+
}
|
86
|
+
|
87
|
+
rownr = 0
|
88
|
+
|
89
|
+
loop do
|
90
|
+
output << split_line + "\n"
|
91
|
+
output_row.call(header, header_orient)
|
92
|
+
output << split_line + "\n"
|
93
|
+
if pagebreak_after.nil?
|
94
|
+
rows.each {|ar| output_row.call(ar, rows_orient)}
|
95
|
+
output << split_line + "\n"
|
96
|
+
break
|
97
|
+
end
|
98
|
+
|
99
|
+
rows[rownr,pagebreak_after].each {|ar| output_row.call(ar, rows_orient)}
|
100
|
+
output << split_line + "\n"
|
101
|
+
|
102
|
+
rownr += pagebreak_after
|
103
|
+
|
104
|
+
break if rownr >= rows.size
|
105
|
+
|
106
|
+
yield output if block_given?
|
107
|
+
end
|
108
|
+
|
109
|
+
end
|
110
|
+
end # module TableFormatter
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module DBI
|
2
|
+
#
|
3
|
+
# Represents a Time
|
4
|
+
#
|
5
|
+
# DEPRECATED: Please use a regular Time or DateTime object.
|
6
|
+
class Time
|
7
|
+
attr_accessor :hour, :minute, :second
|
8
|
+
|
9
|
+
private
|
10
|
+
# DBI::Time.new(hour = 0, minute = 0, second = 0)
|
11
|
+
# DBI::Time.new(Time)
|
12
|
+
#
|
13
|
+
# Creates and returns a new DBI::Time object. Unlike the Time object
|
14
|
+
# in the standard library, accepts an hour, minute and second, or a
|
15
|
+
# Time object.
|
16
|
+
def initialize(hour=0, minute=0, second=0)
|
17
|
+
case hour
|
18
|
+
when ::Time
|
19
|
+
@hour, @minute, @second = hour.hour, hour.min, hour.sec
|
20
|
+
@original_time = hour
|
21
|
+
else
|
22
|
+
@hour, @minute, @second = hour, minute, second
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
public
|
27
|
+
|
28
|
+
deprecate :initialize, :public
|
29
|
+
|
30
|
+
alias :min :minute
|
31
|
+
alias :min= :minute=
|
32
|
+
alias :sec :second
|
33
|
+
alias :sec= :second=
|
34
|
+
|
35
|
+
# Returns a new Time object based on the hour, minute and second, using
|
36
|
+
# the current year, month and day. If a Time object was passed to the
|
37
|
+
# constructor, returns that object instead.
|
38
|
+
def to_time
|
39
|
+
if @original_time
|
40
|
+
@original_time
|
41
|
+
else
|
42
|
+
t = ::Time.now
|
43
|
+
::Time.local(t.year, t.month, t.day, @hour, @minute, @second)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Returns a DBI::Time object as a string in HH:MM:SS format.
|
48
|
+
def to_s
|
49
|
+
sprintf("%02d:%02d:%02d", @hour, @minute, @second)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module DBI
|
2
|
+
#
|
3
|
+
# Represents a Timestamp.
|
4
|
+
#
|
5
|
+
# DEPRECATED: Please use a regular DateTime object.
|
6
|
+
#
|
7
|
+
class Timestamp
|
8
|
+
attr_accessor :year, :month, :day
|
9
|
+
attr_accessor :hour, :minute, :second
|
10
|
+
attr_writer :fraction
|
11
|
+
|
12
|
+
private
|
13
|
+
# DBI::Timestamp(year=0,month=0,day=0,hour=0,min=0,sec=0,fraction=nil)
|
14
|
+
# DBI::Timestamp(Time)
|
15
|
+
# DBI::Timestamp(Date)
|
16
|
+
#
|
17
|
+
# Creates and returns a new DBI::Timestamp object. This is similar to
|
18
|
+
# a Time object in the standard library, but it also contains fractional
|
19
|
+
# seconds, expressed in nanoseconds. In addition, the constructor
|
20
|
+
# accepts either a Date or Time object.
|
21
|
+
def initialize(year=0, month=0, day=0, hour=0, min=0, sec=0, fraction=nil)
|
22
|
+
case year
|
23
|
+
when ::Time
|
24
|
+
@year, @month, @day = year.year, year.month, year.day
|
25
|
+
@hour, @minute, @second, @fraction = year.hour, year.min, year.sec, nil
|
26
|
+
@original_time = year
|
27
|
+
when ::Date
|
28
|
+
@year, @month, @day = year.year, year.month, year.day
|
29
|
+
@hour, @minute, @second, @fraction = 0, 0, 0, nil
|
30
|
+
@original_date = year
|
31
|
+
else
|
32
|
+
@year, @month, @day = year, month, day
|
33
|
+
@hour, @minute, @second, @fraction = hour, min, sec, fraction
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
public
|
38
|
+
|
39
|
+
deprecate :initialize, :public
|
40
|
+
|
41
|
+
# Returns true if +timestamp+ has a year, month, day, hour, minute,
|
42
|
+
# second and fraction equal to the comparing object.
|
43
|
+
#
|
44
|
+
# Returns false if the comparison fails for any reason.
|
45
|
+
def ==(timestamp)
|
46
|
+
@year == timestamp.year and @month == timestamp.month and
|
47
|
+
@day == timestamp.day and @hour == timestamp.hour and
|
48
|
+
@minute == timestamp.minute and @second == timestamp.second and
|
49
|
+
(fraction() == timestamp.fraction)
|
50
|
+
rescue
|
51
|
+
false
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns fractional seconds, or 0 if not set.
|
55
|
+
def fraction
|
56
|
+
@fraction || 0
|
57
|
+
end
|
58
|
+
|
59
|
+
# Aliases
|
60
|
+
alias :mon :month
|
61
|
+
alias :mon= :month=
|
62
|
+
alias :mday :day
|
63
|
+
alias :mday= :day=
|
64
|
+
alias :min :minute
|
65
|
+
alias :min= :minute=
|
66
|
+
alias :sec :second
|
67
|
+
alias :sec= :second=
|
68
|
+
|
69
|
+
# Returns a DBI::Timestamp object as a string in YYYY-MM-DD HH:MM:SS
|
70
|
+
# format. If a fraction is present, then it is appended in ".FF" format.
|
71
|
+
def to_s
|
72
|
+
string = sprintf("%04d-%02d-%02d %02d:%02d:%02d",
|
73
|
+
@year, @month, @day, @hour, @minute, @second)
|
74
|
+
|
75
|
+
if @fraction
|
76
|
+
fraction = ("%.9f" % (@fraction.to_i / 1e9)).
|
77
|
+
to_s[1..-1].gsub(/0{1,8}$/, "")
|
78
|
+
string += fraction
|
79
|
+
end
|
80
|
+
|
81
|
+
string
|
82
|
+
end
|
83
|
+
|
84
|
+
# Returns a new Time object based on the year, month and day or, if a
|
85
|
+
# Time object was passed to the constructor, returns that object.
|
86
|
+
def to_time
|
87
|
+
@original_time || ::Time.local(@year, @month, @day, @hour, @minute, @second)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Returns a new Date object based on the year, month and day or, if a
|
91
|
+
# Date object was passed to the constructor, returns that object.
|
92
|
+
def to_date
|
93
|
+
@original_date || ::Date.new(@year, @month, @day)
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|