ruby-informix 0.6.2 → 0.7.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,280 @@
1
+ # $Id: interval.rb,v 1.3 2008/03/29 20:32:57 santana Exp $
2
+ #
3
+ # Copyright (c) 2008, Gerardo Santana Gomez Garrido <gerardo.santana@gmail.com>
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ # 2. Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ # 3. The name of the author may not be used to endorse or promote products
16
+ # derived from this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27
+ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ # POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ module Informix
31
+ # The +IntervalBase+ class is used for sending and retrieving INTERVAL
32
+ # values to/from Informix.
33
+ #
34
+ # It can be used in some expressions with +Numeric+, +Date+, +DateTime+ and
35
+ # +Time+.
36
+ class IntervalBase
37
+ include Comparable
38
+
39
+ attr_reader :val
40
+
41
+ # IntervalBase.new(val) => interval
42
+ #
43
+ # Creates an +Interval+ object with +val+ as value.
44
+ def initialize(val)
45
+ return @val = val if Numeric === val
46
+ raise TypeError, "Expected Numeric"
47
+ end
48
+
49
+ def +@; self end
50
+ def -@; self.class.new(-@val) end
51
+
52
+ # Adds an +Interval+ object to a +Numeric+ or another compatible +Interval+
53
+ #
54
+ # interval + numeric => interval
55
+ # interval + interval => interval
56
+ def +(obj)
57
+ case obj
58
+ when Numeric
59
+ self.class.new(@val + obj)
60
+ when self.class
61
+ self.class.new(@val + obj.val)
62
+ else
63
+ raise TypeError, "#{self.class} cannot be added to #{obj.class}"
64
+ end
65
+ end
66
+
67
+ # Multiplies an +Interval+ object by a +Numeric+
68
+ #
69
+ # interval*numeric => interval
70
+ def *(n)
71
+ return self.class.new(@val*n) if Numeric === n
72
+ raise TypeError, "Expected Numeric"
73
+ end
74
+
75
+ # Divides an +Interval+ object by a +Numeric+
76
+ #
77
+ # interval/numeric => interval
78
+ def /(n)
79
+ return self.class.new(@val/n) if Numeric === n
80
+ raise TypeError, "Expected Numeric"
81
+ end
82
+
83
+ # Compares two compatible +Interval+ objects.
84
+ #
85
+ # interval1 <=> interval2 => true or false
86
+ def <=>(ivl)
87
+ return @val <=> ivl.val if self.class === ivl
88
+ raise ArgumentError, "Incompatible qualifiers"
89
+ end
90
+
91
+ # Returns the fields of an +Interval+ object as an +Array+
92
+ #
93
+ # invl.to_a => array
94
+ def to_a; @fields end
95
+
96
+ end # class IntervalBase
97
+
98
+ # The +IntervalYTM+ class is an Interval class dedicated only to
99
+ # represent intervals in the scope YEAR TO MONTH.
100
+ class IntervalYTM < IntervalBase
101
+ attr_reader :years, :months
102
+
103
+ # Creates an IntervalYTM object with +val+ as value.
104
+ #
105
+ # IntervalYTM.new(val) => interval
106
+ def initialize(val)
107
+ super
108
+ @val = val.to_i
109
+ @years, @months = @val.abs.divmod 12
110
+ if @val < 0
111
+ @years = -@years
112
+ @months = -@months
113
+ end
114
+ @fields = [ @years, @months ]
115
+ end
116
+
117
+ # interval + date => date
118
+ # interval + datetime => datetime
119
+ def +(obj)
120
+ case obj
121
+ when Date, DateTime
122
+ obj >> @val
123
+ else
124
+ super
125
+ end
126
+ end
127
+
128
+ # Returns an ANSI SQL standards compliant string representation
129
+ #
130
+ # invl.to_s => string
131
+ def to_s; "%d-%02d" % [@years, @months.abs] end
132
+
133
+ # Converts a invl to years
134
+ #
135
+ # invl.to_years => numeric
136
+ def to_years; Rational === @val ? @val/12 : @val/12.0 end
137
+
138
+ # Converts invl to months
139
+ #
140
+ # invl.to_months => numeric
141
+ def to_months; @val end
142
+ end # class IntervalYTM
143
+
144
+ # The +IntervalDTS+ class is an Interval class dedicated only to
145
+ # represent intervals in the scope DAY TO SECOND.
146
+ class IntervalDTS < IntervalBase
147
+ attr_reader :days, :hours, :minutes, :seconds
148
+
149
+ # Creates an IntervalDTS object with +val+ as value.
150
+ #
151
+ # IntervalDTS.new(val) => interval
152
+ def initialize(val)
153
+ super
154
+ @days, @hours = @val.abs.divmod(24*60*60)
155
+ @hours, @minutes = @hours.divmod(60*60)
156
+ @minutes, @seconds = @minutes.divmod(60)
157
+ if @val < 0
158
+ @days = -@days; @hours = -@hours; @minutes = -@minutes;
159
+ @seconds = -@seconds
160
+ end
161
+ @fields = [ @days, @hours, @minutes, @seconds ]
162
+ end
163
+
164
+ # interval + datetime => datetime
165
+ # interval + time => time
166
+ def +(obj)
167
+ case obj
168
+ when DateTime
169
+ obj + (Rational === @val ? @val/86400 : @val/86400.0)
170
+ when Time
171
+ obj + @val
172
+ else
173
+ super
174
+ end
175
+ end
176
+
177
+ # Returns an ANSI SQL standards compliant string representation
178
+ #
179
+ # invl.to_s => string
180
+ def to_s
181
+ "%d %02d:%02d:%08.5f" % [@days, @hours.abs, @minutes.abs, @seconds.abs]
182
+ end
183
+
184
+ # Converts invl to days
185
+ #
186
+ # invl.to_days => numeric
187
+ def to_days; Rational === @val ? @val/60/60/24 : @val/60.0/60/24 end
188
+
189
+ # Converts invl to hours
190
+ #
191
+ # invl.to_hours => numeric
192
+ def to_hours; Rational === @val ? @val/60/60 : @val/60.0/60 end
193
+
194
+ # Converts invl to minutes
195
+ #
196
+ # invl.to_minutes => numeric
197
+ def to_minutes; Rational === @val ? @val/60 : @val/60.0 end
198
+
199
+ # Converts invl to seconds
200
+ #
201
+ # invl.to_seconds => numeric
202
+ def to_seconds; @val end
203
+ end # class IntervalDTS
204
+
205
+ # The +Interval+ module provides shortcuts for creating +IntervalYTM+ and
206
+ # +IntervalDTS+ objects
207
+ module Interval
208
+ # Shortcut to create an IntervalYTM object.
209
+ #
210
+ # Interval.year_to_month(years = 0, months = 0) => interval
211
+ # Interval.year_to_month(:years => yy, :months => mm) => interval
212
+ #
213
+ # Interval.year_to_month(5) #=> '5-00'
214
+ # Interval.year_to_month(0, 3) #=> '0-03'
215
+ # Interval.year_to_month(5, 3) #=> '5-03'
216
+ # Interval.year_to_month(:years => 5.5) #=> '5-06'
217
+ # Interval.year_to_month(:months => 3) #=> '0-03'
218
+ # Interval.year_to_month(:years => 5.5, :months => 5) #=> '5-11'
219
+ def self.year_to_month(*args)
220
+ if args.size == 1 && Hash === args[0]
221
+ years, months = args[0][:years], args[0][:months]
222
+ elsif args.size <= 2 && args.all? {|e| Numeric === e }
223
+ years, months = args
224
+ else
225
+ raise TypeError, "Expected Numerics or a Hash"
226
+ end
227
+ years ||= 0; months ||= 0
228
+ if ![years, months].all? {|e| Numeric === e && e >= 0 }
229
+ raise ArgumentError, "Expected Numerics >= 0"
230
+ end
231
+ from_months(years*12 + months.to_i)
232
+ end
233
+
234
+ # Shortcut to create an IntervalYTM object.
235
+ #
236
+ # Interval.from_months(3) #=> '0-03'
237
+ # Interval.from_months(71) #=> '5-11'
238
+ def self.from_months(months)
239
+ IntervalYTM.new(months)
240
+ end
241
+
242
+ # Shortcut to create an IntervalDTS object.
243
+ #
244
+ # Interval.day_to_second(days = 0, hours = 0,
245
+ # minutes = 0, seconds = 0) => interval
246
+ # Interval.day_to_second(:days => dd, :hours => hh,
247
+ # :minutes => mm, :seconds => ss) => interval
248
+ #
249
+ # Interval.day_to_second(5, 3) # => '5 03:00:00.00000'
250
+ # Interval.day_to_second(0, 2, 0, 30) # => '0 02:00:30.00000'
251
+ # Interval.day_to_second(:hours=>2.5) # => '0 02:30:00.00000'
252
+ # Interval.day_to_second(:seconds=>Rational(151,10))# => '0 00:00:15.10000'
253
+ # Interval.day_to_second(:seconds=> 20.13) # => '0 00:00:20.13000'
254
+ # Interval.day_to_second(:days=>1.5, :hours=>2) # => '1 14:00:00.00000'
255
+ def self.day_to_second(*args)
256
+ if args.size == 1 && Hash === args[0]
257
+ h = args[0]
258
+ days, hours, minutes, seconds = h[:days], h[:hours], h[:minutes],
259
+ h[:seconds]
260
+ elsif args.size <= 5 && args.all? {|e| Numeric === e || e.nil? }
261
+ days, hours, minutes, seconds = args
262
+ else
263
+ raise TypeError, "Expected Numerics or a Hash"
264
+ end
265
+ days ||= 0; hours ||= 0; minutes ||= 0; seconds ||= 0
266
+ if ![days, hours, minutes, seconds].all? {|e| Numeric === e && e >= 0 }
267
+ raise ArgumentError, "Expected Numerics >= 0"
268
+ end
269
+ from_seconds(days*24*60*60 + hours*60*60 + minutes*60 + seconds)
270
+ end
271
+
272
+ # Shortcut to create an IntervalDTS object.
273
+ #
274
+ # Interval.from_seconds(9000) #=> '0 02:30:00.00000'
275
+ # Interval.from_seconds(Rational(151, 10)) #=> '0 00:00:15.10000'
276
+ def self.from_seconds(seconds)
277
+ IntervalDTS.new(seconds)
278
+ end
279
+ end # module Interval
280
+ end # module Informix
@@ -0,0 +1,306 @@
1
+ # $Id: scrollcursor.rb,v 1.4 2008/03/29 07:35:03 santana Exp $
2
+ #
3
+ # Copyright (c) 2008, Gerardo Santana Gomez Garrido <gerardo.santana@gmail.com>
4
+ # All rights reserved.
5
+ #
6
+ # Redistribution and use in source and binary forms, with or without
7
+ # modification, are permitted provided that the following conditions
8
+ # are met:
9
+ #
10
+ # 1. Redistributions of source code must retain the above copyright
11
+ # notice, this list of conditions and the following disclaimer.
12
+ # 2. Redistributions in binary form must reproduce the above copyright
13
+ # notice, this list of conditions and the following disclaimer in the
14
+ # documentation and/or other materials provided with the distribution.
15
+ # 3. The name of the author may not be used to endorse or promote products
16
+ # derived from this software without specific prior written permission.
17
+ #
18
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
19
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
+ # WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
+ # DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
22
+ # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
+ # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
24
+ # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
25
+ # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
26
+ # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
27
+ # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
+ # POSSIBILITY OF SUCH DAMAGE.
29
+
30
+ require 'informixc'
31
+
32
+ module Informix
33
+ class ScrollCursor < SequentialCursor
34
+ # Returns the record at _index_, or returns a subarray starting at _start_
35
+ # and continuing for _length_ records. Negative indices count backward from
36
+ # the end of the cursor (-1 is the last element). Returns nil if the
37
+ # (starting) index is out of range.
38
+ #
39
+ # <b>Warning</b>: if the (starting) index is negative and out of range, the
40
+ # position in the cursor is set to the last record. Otherwise the current
41
+ # position in the cursor is preserved.
42
+ #
43
+ # cursor[index] => array or nil
44
+ # cursor[start, length] => array or nil
45
+ # cursor.slice(index) => array or nil
46
+ # cursor.slice(start, length) => array or nil
47
+ def slice(*args)
48
+ slice0(args, Array)
49
+ end
50
+
51
+ alias [] slice
52
+
53
+ # Returns the record at _index_. Negative indices count backward from
54
+ # the end of the cursor (-1 is the last element). Returns nil if the index
55
+ # is out of range.
56
+ #
57
+ # Stores the record fetched always in the same Array object.
58
+ #
59
+ # <b>Warning</b>: if the index is negative and out of range, the
60
+ # position in the cursor is set to the last record. Otherwise the current
61
+ # position in the cursor is preserved.
62
+ #
63
+ # cursor.slice!(index) => array or nil
64
+ def slice!(index)
65
+ entry(index, Array, true)
66
+ end
67
+
68
+ # Returns the record at _index_, or returns a subarray starting at _start_
69
+ # and continuing for _length_ records. Negative indices count backward from
70
+ # the end of the cursor (-1 is the last element). Returns nil if the
71
+ # (starting) index is out of range.
72
+ #
73
+ # <b>Warning</b>: if the (starting) index is negative and out of range, the
74
+ # position in the cursor is set to the last record. Otherwise the current
75
+ # position in the cursor is preserved.
76
+ #
77
+ # cursor.slice_hash(index) => hash or nil
78
+ # cursor.slice_hash(start, length) => array or nil
79
+ def slice_hash(*args)
80
+ slice0(args, Hash)
81
+ end
82
+
83
+ # Returns the record at _index_. Negative indices count backward from
84
+ # the end of the cursor (-1 is the last element). Returns nil if the index
85
+ # is out of range.
86
+ #
87
+ # Stores the record fetched always in the same Hash object.
88
+ #
89
+ # <b>Warning</b>: if the index is negative and out of range, the
90
+ # position in the cursor is set to the last record. Otherwise the current
91
+ # position in the cursor is preserved.
92
+ #
93
+ # cursor.slice_hash!(index) => hash or nil
94
+ def slice_hash!(index)
95
+ entry(index, Hash, true)
96
+ end
97
+
98
+ # Returns the previous _offset_ th record. Negative indices count
99
+ # forward from the current position. Returns nil if the _offset_ is out of
100
+ # range.
101
+ #
102
+ # cursor.prev(offset = 1) => array or nil
103
+ def prev(offset = 1)
104
+ rel(-offset, Array, false)
105
+ end
106
+
107
+ # Returns the previous _offset_ th record. Negative indices count
108
+ # forward from the current position. Returns nil if the _offset_ is out of
109
+ # range.
110
+ #
111
+ # Stores the record fetched always in the same Array object.
112
+ #
113
+ # cursor.prev!(offset = 1) => array or nil
114
+ def prev!(offset = 1)
115
+ rel(-offset, Array, true)
116
+ end
117
+
118
+ # Returns the previous _offset_ th record. Negative indices count
119
+ # forward from the current position. Returns nil if the _offset_ is out of
120
+ # range.
121
+ #
122
+ # cursor.prev_hash(offset = 1) => hash or nil
123
+ def prev_hash(offset = 1)
124
+ rel(-offset, Hash, false)
125
+ end
126
+
127
+ # Returns the previous _offset_ th record. Negative indices count
128
+ # forward from the current position. Returns nil if the _offset_ is out of
129
+ # range.
130
+ #
131
+ # Stores the record fetched always in the same Hash object.
132
+ #
133
+ # cursor.prev_hash!(offset = 1) => hash or nil
134
+ def prev_hash!(offset = 1)
135
+ rel(-offset, Hash, true)
136
+ end
137
+
138
+ # Returns the next _offset_ th record. Negative indices count
139
+ # backward from the current position. Returns nil if the _offset_ is out of
140
+ # range.
141
+ #
142
+ # cursor.next(offset = 1) => array or nil
143
+ def next(offset = 1)
144
+ rel(offset, Array, false)
145
+ end
146
+
147
+ # Returns the next _offset_ th record. Negative indices count
148
+ # backward from the current position. Returns nil if the _offset_ is out of
149
+ # range.
150
+ #
151
+ # Stores the record fetched always in the same Array object.
152
+ #
153
+ # cursor.next!(offset = 1) => array or nil
154
+ def next!(offset = 1)
155
+ rel(offset, Array, true)
156
+ end
157
+
158
+ # Returns the next _offset_ th record. Negative indices count
159
+ # backward from the current position. Returns nil if the _offset_ is out of
160
+ # range.
161
+ #
162
+ # cursor.next_hash(offset = 1) => hash or nil
163
+ def next_hash(offset = 1)
164
+ rel(offset, Hash, false)
165
+ end
166
+
167
+ # Returns the next _offset_ th record. Negative indices count
168
+ # backward from the current position. Returns nil if the _offset_ is out of
169
+ # range.
170
+ #
171
+ # cursor.next_hash!(offset = 1) => hash or nil
172
+ def next_hash!(offset = 1)
173
+ rel(offset, Hash, true)
174
+ end
175
+
176
+ # Returns the first record of the cursor. If the cursor is empty,
177
+ # returns nil.
178
+ #
179
+ # cursor.first => array or nil
180
+ def first
181
+ entry(0, Array, false)
182
+ end
183
+
184
+ # Returns the first record of the cursor. If the cursor is empty,
185
+ # returns nil.
186
+ #
187
+ # Stores the record fetched always in the same Array object.
188
+ #
189
+ # cursor.first! => array or nil
190
+ def first!
191
+ entry(0, Array, true)
192
+ end
193
+
194
+ # Returns the first record of the cursor. If the cursor is empty,
195
+ # returns nil.
196
+ #
197
+ # cursor.first_hash => hash or nil
198
+ def first_hash
199
+ entry(0, Hash, false)
200
+ end
201
+
202
+ # Returns the first record of the cursor. If the cursor is empty,
203
+ # returns nil.
204
+ #
205
+ # Stores the record fetched always in the same Hash object.
206
+ #
207
+ # cursor.first_hash! => hash or nil
208
+ def first_hash!
209
+ entry(0, Hash, true)
210
+ end
211
+
212
+ # Returns the last record of the cursor. If the cursor is empty,
213
+ # returns nil.
214
+ #
215
+ # cursor.last => array or nil
216
+ def last
217
+ entry(-1, Array, false)
218
+ end
219
+
220
+ # Returns the last record of the cursor. If the cursor is empty,
221
+ # returns nil.
222
+ #
223
+ # Stores the record fetched always in the same Array object.
224
+ #
225
+ # cursor.last! => array or nil
226
+ def last!
227
+ entry(-1, Array, true)
228
+ end
229
+
230
+ # Returns the last record of the cursor. If the cursor is empty,
231
+ # returns nil.
232
+ #
233
+ # cursor.last_hash => hash or nil
234
+ def last_hash
235
+ entry(-1, Hash, false)
236
+ end
237
+
238
+ # Returns the last record of the cursor. If the cursor is empty,
239
+ # returns nil.
240
+ #
241
+ # Stores the record fetched always in the same Hash object.
242
+ #
243
+ # cursor.last_hash! => hash or nil
244
+ def last_hash!
245
+ entry(-1, Hash, true)
246
+ end
247
+
248
+ # Returns the current record of the cursor. If the cursor is empty,
249
+ # returns nil.
250
+ #
251
+ # cursor.current => array or nil
252
+ def current
253
+ entry(nil, Array, false)
254
+ end
255
+
256
+ # Returns the current record of the cursor. If the cursor is empty,
257
+ # returns nil.
258
+ #
259
+ # Stores the record fetched always in the same Array object.
260
+ #
261
+ # cursor.current! => array or nil
262
+ def current!
263
+ entry(nil, Array, true)
264
+ end
265
+
266
+ # Returns the current record of the cursor. If the cursor is empty,
267
+ # returns nil.
268
+ #
269
+ # cursor.current_hash => hash or nil
270
+ def current_hash
271
+ entry(nil, Hash, false)
272
+ end
273
+
274
+ # Returns the current record of the cursor. If the cursor is empty,
275
+ # returns nil.
276
+ #
277
+ # Stores the record fetched always in the same Hash object.
278
+ #
279
+ # cursor.current_hash! => hash or nil
280
+ def current_hash!
281
+ entry(nil, Hash, true)
282
+ end
283
+
284
+ private
285
+
286
+ # Provides the Array-like functionality for scroll cursors when using the
287
+ # cursor[start, length] syntax
288
+ def subseq(start, length, type) #:nodoc:
289
+ first = entry(start, type, false)
290
+ return if first.nil?
291
+
292
+ records = length > 1 ? fetch_many0(length - 1, type) : []
293
+ records.unshift(first)
294
+ end
295
+
296
+ # Base function for slice and slice_hash methods
297
+ def slice0(args, type) #:nodoc:
298
+ return entry(args[0], type, false) if args.size == 1
299
+ if args.size == 2
300
+ return subseq(args[0], args[1], type) unless args[1] < 0
301
+ raise(ArgumentError, "length must be positive")
302
+ end
303
+ raise(ArgumentError, "wrong number of arguments (%d for 2)", args.size)
304
+ end
305
+ end # class ScrollCursor
306
+ end # module Informix