rq-ruby1.8 3.4.3

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 (67) hide show
  1. data/Gemfile +22 -0
  2. data/Gemfile.lock +22 -0
  3. data/INSTALL +166 -0
  4. data/LICENSE +10 -0
  5. data/Makefile +6 -0
  6. data/README +1183 -0
  7. data/Rakefile +37 -0
  8. data/TODO +24 -0
  9. data/TUTORIAL +230 -0
  10. data/VERSION +1 -0
  11. data/bin/rq +902 -0
  12. data/bin/rqmailer +865 -0
  13. data/example/a.rb +7 -0
  14. data/extconf.rb +198 -0
  15. data/gemspec.rb +40 -0
  16. data/install.rb +210 -0
  17. data/lib/rq.rb +155 -0
  18. data/lib/rq/arrayfields.rb +371 -0
  19. data/lib/rq/backer.rb +31 -0
  20. data/lib/rq/configfile.rb +82 -0
  21. data/lib/rq/configurator.rb +40 -0
  22. data/lib/rq/creator.rb +54 -0
  23. data/lib/rq/cron.rb +144 -0
  24. data/lib/rq/defaultconfig.txt +5 -0
  25. data/lib/rq/deleter.rb +51 -0
  26. data/lib/rq/executor.rb +40 -0
  27. data/lib/rq/feeder.rb +527 -0
  28. data/lib/rq/ioviewer.rb +48 -0
  29. data/lib/rq/job.rb +51 -0
  30. data/lib/rq/jobqueue.rb +947 -0
  31. data/lib/rq/jobrunner.rb +110 -0
  32. data/lib/rq/jobrunnerdaemon.rb +193 -0
  33. data/lib/rq/lister.rb +47 -0
  34. data/lib/rq/locker.rb +43 -0
  35. data/lib/rq/lockfile.rb +564 -0
  36. data/lib/rq/logging.rb +124 -0
  37. data/lib/rq/mainhelper.rb +189 -0
  38. data/lib/rq/orderedautohash.rb +39 -0
  39. data/lib/rq/orderedhash.rb +240 -0
  40. data/lib/rq/qdb.rb +733 -0
  41. data/lib/rq/querier.rb +98 -0
  42. data/lib/rq/rails.rb +80 -0
  43. data/lib/rq/recoverer.rb +28 -0
  44. data/lib/rq/refresher.rb +80 -0
  45. data/lib/rq/relayer.rb +283 -0
  46. data/lib/rq/resource.rb +22 -0
  47. data/lib/rq/resourcemanager.rb +40 -0
  48. data/lib/rq/resubmitter.rb +100 -0
  49. data/lib/rq/rotater.rb +98 -0
  50. data/lib/rq/sleepcycle.rb +46 -0
  51. data/lib/rq/snapshotter.rb +40 -0
  52. data/lib/rq/sqlite.rb +286 -0
  53. data/lib/rq/statuslister.rb +48 -0
  54. data/lib/rq/submitter.rb +113 -0
  55. data/lib/rq/toucher.rb +182 -0
  56. data/lib/rq/updater.rb +94 -0
  57. data/lib/rq/usage.rb +1222 -0
  58. data/lib/rq/util.rb +304 -0
  59. data/rdoc.sh +17 -0
  60. data/rq-ruby1.8.gemspec +120 -0
  61. data/test/.gitignore +1 -0
  62. data/test/test_rq.rb +145 -0
  63. data/white_box/crontab +2 -0
  64. data/white_box/joblist +8 -0
  65. data/white_box/killrq +18 -0
  66. data/white_box/rq_killer +27 -0
  67. metadata +208 -0
@@ -0,0 +1,22 @@
1
+ unless defined? $__rq_resource__
2
+ $__rq_resource__ = __FILE__
3
+
4
+ module RQ
5
+ #--{{{
6
+ RQ::LIBDIR =
7
+ File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
8
+ defined? RQ::LIBDIR
9
+
10
+ RQ::INCDIR =
11
+ File::dirname(RQ::LIBDIR) + File::SEPARATOR unless
12
+ defined? RQ::INCDIR
13
+
14
+ require INCDIR + 'rq'
15
+
16
+ class Resource
17
+ #--{{{
18
+ #--}}}
19
+ end # class Resource
20
+ #--}}}
21
+ end # module RQ
22
+ end
@@ -0,0 +1,40 @@
1
+ unless defined? $__rq_resourcemanager__
2
+ $__rq_resourcemanager__ = __FILE__
3
+
4
+ #require 'yaml'
5
+ #require 'yaml/store'
6
+ #require 'socket'
7
+
8
+ module RQ
9
+ #--{{{
10
+ RQ::LIBDIR =
11
+ File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
12
+ defined? RQ::LIBDIR
13
+
14
+ RQ::INCDIR =
15
+ File::dirname(RQ::LIBDIR) + File::SEPARATOR unless
16
+ defined? RQ::INCDIR
17
+
18
+ #require INCDIR + 'rq'
19
+
20
+ class ResourceManager
21
+ #--{{{
22
+ attr 'path'
23
+ attr 'ystore'
24
+
25
+ def initialize path
26
+ #--{{{
27
+ @path = File.expand_path path.to_s
28
+ @ystore = YAML::Store.new @path
29
+ #--}}}
30
+ end
31
+
32
+ def valid? expr
33
+ #--{{{
34
+ #--}}}
35
+ end
36
+ #--}}}
37
+ end # class ResourceManager
38
+ #--}}}
39
+ end # module RQ
40
+ end
@@ -0,0 +1,100 @@
1
+ unless defined? $__rq_resubmitter__
2
+ module RQ
3
+ #--{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require LIBDIR + 'mainhelper'
8
+ require LIBDIR + 'job'
9
+
10
+ #
11
+ # the Submitter class is responsible for submitting commands to the queue,
12
+ # the commands it submits are taken from the command line, stdin, or the
13
+ # specified infile. the format of commands read from stdin or file is
14
+ # either a simple list of commands, one per line, where blank lines and
15
+ # comments (#) are ignored OR it is valid yaml input. if the Submitter sees
16
+ # the token '---' in the input stream it is assumed the input is yaml. for
17
+ # an example of valid yaml input examine the output of a Lister using
18
+ #
19
+ # rq q list
20
+ #
21
+ # the output of other commands, such as that of a Querier may also be used
22
+ # as input to submit
23
+ #
24
+ class ReSubmitter < MainHelper
25
+ #--{{{
26
+ def resubmit
27
+ #--{{{
28
+ set_q
29
+
30
+ @priority = @options['priority']
31
+ debug{ "priority <#{ @priority }>" }
32
+
33
+ @tag = @options['tag']
34
+ debug{ "tag <#{ @tag }>" }
35
+
36
+ @runner = @options['runner']
37
+ debug{ "runner <#{ @runner }>" }
38
+
39
+ @restartable = @options['restartable']
40
+ debug{ "restartable <#{ @restartable }>" }
41
+
42
+ @infile = @options['infile']
43
+ debug{ "infile <#{ @infile }>" }
44
+
45
+ @data = @options['data']
46
+ debug{ "data <#{ @data }>" }
47
+
48
+ if job_stdin == '-' and stdin?
49
+ abort "cannot specify both jobs and job input on stdin"
50
+ end
51
+
52
+
53
+ jobs = []
54
+
55
+ if @infile
56
+ open(@infile) do |f|
57
+ debug{ "reading jobs from <#{ @infile }>" }
58
+ loadio f, @infile, jobs
59
+ end
60
+ end
61
+
62
+ if stdin?
63
+ debug{ "reading jobs from <stdin>" }
64
+ loadio stdin, 'stdin', jobs
65
+ end
66
+ jobs.each{|job| @argv << Integer(job['jid'])}
67
+
68
+ abort "no jobs specified!" if @argv.empty?
69
+
70
+ init_job_stdin!
71
+
72
+ puts '---'
73
+ @q.transaction do
74
+ jobs = @q.list(*@argv)
75
+ jobs.each do |job|
76
+ job['priority'] = @priority if @options.has_key?('priority')
77
+ job['tag'] = @tag if @options.has_key?('tag')
78
+ job['runner'] = @runner if @options.has_key?('runner')
79
+ job['restartable'] = @restartable if @options.has_key?('restartable')
80
+ job['stdin'] = @job_stdin if @job_stdin
81
+ job['data'] = @data if @data
82
+ unless job['state'] =~ %r/running/io
83
+ resubmitted = nil
84
+ @q.resubmit(job){|resubmitted|}
85
+ puts '-'
86
+ resubmitted.fields.each{|f| puts " #{ f }: #{ resubmitted[f] }" }
87
+ end
88
+ end
89
+ end
90
+
91
+ jobs = nil
92
+ self
93
+ #--}}}
94
+ end
95
+ #--}}}
96
+ end # class ReSubmitter
97
+ #--}}}
98
+ end # module RQ
99
+ $__rq_resubmitter__ = __FILE__
100
+ end
@@ -0,0 +1,98 @@
1
+ unless defined? $__rq_rotater__
2
+ module RQ
3
+ #--{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require LIBDIR + 'mainhelper'
8
+ require LIBDIR + 'util'
9
+
10
+ class Rotater < MainHelper
11
+ #--{{{
12
+ def rotate
13
+ #--{{{
14
+ set_q
15
+
16
+ rot = Util::realpath @argv.shift
17
+
18
+ rot =
19
+ if rot
20
+ if File::directory? rot
21
+ t = Time::now.strftime('%Y%m%d%H%M%S')
22
+ File::join(rot, "#{ File.basename @qpath }.#{ t }")
23
+ else
24
+ rot
25
+ end
26
+ else
27
+ t = Time::now.strftime('%Y%m%d%H%M%S')
28
+ "#{ @qpath }.#{ t }"
29
+ end
30
+
31
+ #rot ||= "#{ @qpath }.#{ Util::timestamp.gsub(/[:\s\.-]/,'_') }.rot"
32
+
33
+ abort "<#{ rot }> exists" if rot and test(?e, rot)
34
+ debug{ "rotation <#{ rot }>" }
35
+
36
+ FileUtils::mkdir_p(File::dirname(rot)) rescue nil
37
+
38
+ rotq = nil
39
+
40
+ @q.transaction do
41
+ begin
42
+ #FileUtils::cp_r @qpath, rot
43
+ self.cp_r @qpath, rot
44
+ rotq = JobQueue::new rot, 'logger' => @logger
45
+ rotq.delete 'pending', 'running', 'holding', 'force' => true
46
+ @q.delete 'dead', 'finished'
47
+ rescue
48
+ FileUtils::rm_rf rot
49
+ raise
50
+ end
51
+ end
52
+
53
+ tgz = File::expand_path "#{ rot }.tgz"
54
+ #dirname = File::dirname rot
55
+ if(system("cd #{ File::dirname rot } && tar cvfz #{ File::basename tgz } #{ File::basename rot }/ >/dev/null 2>&1"))
56
+ FileUtils::rm_rf rot
57
+ rot = tgz
58
+ end
59
+
60
+ puts "---"
61
+ puts "rotation : #{ rot }"
62
+
63
+ #puts '---'
64
+ #puts "q: #{ rotq.path }"
65
+ #puts "db: #{ rotq.db.path }"
66
+ #puts "schema: #{ rotq.db.schema }"
67
+ #puts "lock: #{ rotq.db.lockfile }"
68
+
69
+ EXIT_SUCCESS
70
+ #--}}}
71
+ end
72
+ ##
73
+ # the cp_r should never fail, so we build in another attempt under
74
+ # failing conditions
75
+ def cp_r srcdir, dstdir
76
+ #--{{{
77
+ attempts = 0
78
+ loop do
79
+ begin
80
+ break(FileUtils::cp_r(srcdir, dstdir))
81
+ rescue => e
82
+ raise if attempts > 2
83
+ warn{ e }
84
+ Util::uncache srcdir rescue nil
85
+ Util::uncache dstdir rescue nil
86
+ sleep 2
87
+ ensure
88
+ attempts += 1
89
+ end
90
+ end
91
+ #--}}}
92
+ end
93
+ #--}}}
94
+ end # class Rotater
95
+ #--}}}
96
+ end # module RQ
97
+ $__rq_rotater__ = __FILE__
98
+ end
@@ -0,0 +1,46 @@
1
+ unless defined? $__rq_sleepcycle__
2
+ module RQ
3
+ #--{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ #
8
+ # the sleepcycle class provides timeouts for better than average polling
9
+ # performance by the locking protocol used by the QDB
10
+ #
11
+ class SleepCycle < Array
12
+ #--{{{
13
+ attr :min
14
+ attr :max
15
+ attr :range
16
+ attr :inc
17
+ def initialize min, max, inc
18
+ #--{{{
19
+ @min, @max, @inc = Float(min), Float(max), Float(inc)
20
+ @range = @max - @min
21
+ raise RangeError, "max < min" if @max < @min
22
+ raise RangeError, "inc > range" if @inc > @range
23
+ s = @min
24
+ push(s) and s += @inc while(s <= @max)
25
+ self[-1] = @max if self[-1] < @max
26
+ reset
27
+ #--}}}
28
+ end
29
+ def next
30
+ #--{{{
31
+ ret = self[@idx]
32
+ @idx = ((@idx + 1) % self.size)
33
+ ret
34
+ #--}}}
35
+ end
36
+ def reset
37
+ #--{{{
38
+ @idx = 0
39
+ #--}}}
40
+ end
41
+ #--}}}
42
+ end # class SleepCycle
43
+ #--}}}
44
+ end # module RQ
45
+ $__rq_sleepcycle__ = __FILE__
46
+ end
@@ -0,0 +1,40 @@
1
+ unless defined? $__rq_snapshotter__
2
+ module RQ
3
+ #--{{{
4
+ LIBDIR = File::dirname(File::expand_path(__FILE__)) + File::SEPARATOR unless
5
+ defined? LIBDIR
6
+
7
+ require LIBDIR + 'mainhelper'
8
+
9
+ #
10
+ # a Snapshotter is responsible for safely making a hot snapshot of a queue's
11
+ # db. it's very useful to make a snapshot if, for instance, you are working
12
+ # out a complex query over several attempts since each attempt will compete
13
+ # with other processes for the queue's lock. by making a snapshot you will
14
+ # have your own read only copy to perfect your command before applying it to
15
+ # the actual queue. the feature can also be used to make a hot backup of a
16
+ # queue - tough the Backer has some features that make this more convenient
17
+ #
18
+ class Snapshotter < MainHelper
19
+ #--{{{
20
+ def snapshot
21
+ #--{{{
22
+ set_q
23
+ qtmp = @argv.shift
24
+ raise "<#{ qtmp }> exists" if qtmp and test(?e, qtmp)
25
+ qss = @q.snapshot qtmp, @options['retries']
26
+ #info{ "created q snapshot <#{ qtmp }>" }
27
+
28
+ puts '---'
29
+ puts "q: #{ qss.path }"
30
+ puts "db: #{ qss.db.path }"
31
+ puts "schema: #{ qss.db.schema }"
32
+ puts "lock: #{ qss.db.lockfile }"
33
+ #--}}}
34
+ end
35
+ #--}}}
36
+ end # class Snapshotter
37
+ #--}}}
38
+ end # module RQ
39
+ $__rq_snapshotter__ = __FILE__
40
+ end
@@ -0,0 +1,286 @@
1
+ # --------------------------------------------------------------------------
2
+ # sqlite.rb -- ruby interface for enhancing the core SQLite routines
3
+ # Copyright (C) 2003,2004 Jamis Buck (jamis@jamisbuck.org)
4
+ # --------------------------------------------------------------------------
5
+ # This file is part of the SQLite ruby interface.
6
+ #
7
+ # The SQLite/Ruby Interface is free software; you can redistribute it and/or
8
+ # modify it under the terms of the BSD License as published by the Free
9
+ # Software Foundation. See also the rq LICENSE file.
10
+ #
11
+ # The SQLite/Ruby Interface is distributed in the hope that it will be useful,
12
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
13
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
14
+ # --------------------------------------------------------------------------
15
+ # This defines further enhancements to the SQLite::Database class.
16
+ #
17
+ # Author: Jamis Buck (jamis@jamisbuck.org)
18
+ # --------------------------------------------------------------------------
19
+
20
+ require '_sqlite'
21
+ require 'time'
22
+ require 'base64'
23
+
24
+ # The SQLite module defines the classes and objects needed to interface with a
25
+ # SQLite database.
26
+ module SQLite
27
+
28
+ # The Database class represents a single SQLite database.
29
+ class Database
30
+
31
+ # An alias for #new, with the exception that the mode parameter is optional and
32
+ # defaults to 0 if unspecified.
33
+ def self.open( name, mode=0 )
34
+ new( name, mode )
35
+ end
36
+
37
+ # Returns a string with special characters escaped, such that the string may
38
+ # be safely used as a string literal in an SQL query.
39
+ def self.quote( string )
40
+ return string.gsub( /'/, "''" )
41
+ end
42
+
43
+ # Returns a string that represents the serialization of the given object. The
44
+ # string may safely be used in an SQL statement.
45
+ def self.encode( object )
46
+ Base64.encode64( Marshal.dump( object ) ).strip
47
+ end
48
+
49
+ # Returns an object that was serialized in the given string.
50
+ def self.decode( string )
51
+ Marshal.load( Base64.decode64( string ) )
52
+ end
53
+
54
+ # This is a convenience method for querying the database. If the (optional)
55
+ # block is not specified, then an array of rows will be returned. Otherwise,
56
+ # the given block will be executed one for each row in the result set.
57
+ def execute( sql, arg=nil, &block )
58
+ if block_given?
59
+ exec( sql, block, arg )
60
+ else
61
+ rows = []
62
+ exec( sql, proc { |row| rows.push row }, arg )
63
+ return rows
64
+ end
65
+ end
66
+
67
+ # A convenience method for retrieving the first row of the result set returned
68
+ # by the given query.
69
+ def get_first_row( sql )
70
+ result = nil
71
+ execute( sql ) do |row|
72
+ result = row
73
+ SQLite::ABORT
74
+ end
75
+ result
76
+ end
77
+
78
+ # A convenience method for retrieving the first column of the first row of the
79
+ # result set returned by the given query.
80
+ def get_first_value( sql )
81
+ result = nil
82
+ execute( sql ) do |row|
83
+ result = row[0]
84
+ SQLite::ABORT
85
+ end
86
+ result
87
+ end
88
+
89
+ # Performs an integrity check on the database. If there is a problem, it will raise an
90
+ # exception, otherwise the database is fine.
91
+ def integrity_check
92
+ execute( "PRAGMA integrity_check" ) do |row|
93
+ raise DatabaseException, row[0] if row[0] != "ok"
94
+ end
95
+ end
96
+
97
+ # Defines a getter and setter for the named boolean pragma. A boolean pragma
98
+ # is one that is either true or false.
99
+ def self.define_boolean_pragma( name )
100
+ class_eval <<-EODEF
101
+ def #{name}
102
+ get_first_value( "PRAGMA #{name}" ) != "0"
103
+ end
104
+
105
+ def #{name}=( mode )
106
+ execute( "PRAGMA #{name}=\#{fix_pragma_parm(mode)}" )
107
+ end
108
+ EODEF
109
+ end
110
+
111
+ # Defines a method for invoking the named query pragma, with the given parameters.
112
+ # A query pragma is one that accepts an optional callback block and invokes it for
113
+ # each row that the pragma returns.
114
+ def self.define_query_pragma( name, *parms )
115
+ if parms.empty?
116
+ definition = <<-EODEF
117
+ def #{name}( &block )
118
+ execute( "PRAGMA #{name}", &block )
119
+ end
120
+ EODEF
121
+ else
122
+ definition = <<-EODEF
123
+ def #{name}( #{parms.join(',')}, &block )
124
+ execute( "PRAGMA #{name}( '\#{#{parms.join("}','\#{")}}' )", &block )
125
+ end
126
+ EODEF
127
+ end
128
+
129
+ class_eval definition
130
+ end
131
+
132
+ # Defines a getter and setter for an enumeration pragma, which is like the boolean pragma
133
+ # except that it accepts a range of discrete values.
134
+ def self.define_enum_pragma( name, *enums )
135
+ cases = ""
136
+ enums.each do |enum|
137
+ cases << "when \"" <<
138
+ enum.map { |i| i.to_s.downcase }.join( '", "' ) <<
139
+ "\": mode = \"" <<
140
+ enum.first.upcase << "\"\n"
141
+ end
142
+
143
+ class_eval <<-EODEF
144
+ def #{name}
145
+ get_first_value( "PRAGMA #{name}" )
146
+ end
147
+
148
+ def #{name}=( mode )
149
+ case mode.to_s.downcase
150
+ #{cases}
151
+ else
152
+ raise DatabaseException, "unrecognized #{name} '\#{mode}'"
153
+ end
154
+
155
+ execute( "PRAGMA #{name}='\#{mode}'" )
156
+ end
157
+ EODEF
158
+ end
159
+
160
+ # Defines a getter and setter for a pragma that accepts (or returns) an integer pragma.
161
+ def self.define_int_pragma( name, *enums )
162
+ class_eval <<-EODEF
163
+ def #{name}
164
+ get_first_value( "PRAGMA #{name}" ).to_i
165
+ end
166
+
167
+ def #{name}=( value )
168
+ execute( "PRAGMA #{name}=\#{value.to_i}" )
169
+ end
170
+ EODEF
171
+ end
172
+
173
+ # An internal method for converting the pragma parameter of the boolean pragmas to
174
+ # something that SQLite can understand.
175
+ def fix_pragma_parm( parm )
176
+ case parm
177
+ when String
178
+ case parm.downcase
179
+ when "on", "yes", "true", "y", "t": return "'ON'"
180
+ when "off", "no", "false", "n", "f": return "'OFF'"
181
+ else
182
+ raise DatabaseException, "unrecognized pragma parameter '#{parm}'"
183
+ end
184
+ when true, 1
185
+ return "ON"
186
+ when false, 0, nil
187
+ return "OFF"
188
+ else
189
+ raise DatabaseException, "unrecognized pragma parameter '#{parm.inspect}'"
190
+ end
191
+ end
192
+ private :fix_pragma_parm
193
+
194
+ define_int_pragma "cache_size"
195
+ define_int_pragma "default_cache_size"
196
+
197
+ define_enum_pragma "default_synchronous", [ 'full', 2 ], [ 'normal', 1 ], [ 'off', 0 ]
198
+ define_enum_pragma "default_temp_store", [ 'default', 0 ], [ 'file', 1 ], [ 'memory', 2 ]
199
+ define_enum_pragma "synchronous", [ 'full', 2 ], [ 'normal', 1 ], [ 'off', 0 ]
200
+ define_enum_pragma "temp_store", [ 'default', 0 ], [ 'file', 1 ], [ 'memory', 2 ]
201
+
202
+ define_boolean_pragma "empty_result_callbacks"
203
+ define_boolean_pragma "full_column_names"
204
+ define_boolean_pragma "parser_trace"
205
+ define_boolean_pragma "show_datatypes"
206
+ define_boolean_pragma "vdbe_trace"
207
+
208
+ define_query_pragma "database_list"
209
+ define_query_pragma "foreign_key_list", "table_name"
210
+ define_query_pragma "index_info", "index"
211
+ define_query_pragma "index_list", "table"
212
+ define_query_pragma "table_info", "table"
213
+ end
214
+
215
+ # The TypeTranslator is a singleton class that manages the routines that have
216
+ # been registered to convert particular types. The translator only manages
217
+ # conversions in queries (where data is coming out of the database), and not
218
+ # updates (where data is going into the database).
219
+ class TypeTranslator
220
+ @@default_translator = proc { |type,value| value }
221
+ @@translators = Hash.new( @@default_translator )
222
+
223
+ # Registers the given block to be used when a value of the given type needs
224
+ # to be translated from a string.
225
+ def self.add_translator( type, &block )
226
+ @@translators[ type_name( type ) ] = block
227
+ end
228
+
229
+ # Looks up the translator for the given type, and asks it to convert the
230
+ # given value.
231
+ def self.translate( type, value )
232
+ unless value.nil?
233
+ @@translators[ type_name( type ) ].call( type, value )
234
+ end
235
+ end
236
+
237
+ # Finds the base type name for the given type. Type names with parenthesis
238
+ # (like "VARCHAR(x)" and "DECIMAL(x,y)") will have the parenthesized portion
239
+ # removed.
240
+ def self.type_name( type )
241
+ type = $1 if type =~ /^(.*?)\(/
242
+ type.upcase
243
+ end
244
+ end
245
+
246
+ [ "date",
247
+ "datetime",
248
+ "time" ].each { |type| TypeTranslator.add_translator( type ) { |t,v| Time.parse( v ) } }
249
+
250
+ [ "decimal",
251
+ "float",
252
+ "numeric",
253
+ "double",
254
+ "real",
255
+ "dec",
256
+ "fixed" ].each { |type| TypeTranslator.add_translator( type ) { |t,v| v.to_f } }
257
+
258
+ [ "integer",
259
+ "smallint",
260
+ "mediumint",
261
+ "int",
262
+ "integer",
263
+ "bigint" ].each { |type| TypeTranslator.add_translator( type ) { |t,v| v.to_i } }
264
+
265
+ [ "bit",
266
+ "bool",
267
+ "boolean" ].each do |type|
268
+ TypeTranslator.add_translator( type ) do |t,v|
269
+ !( v.to_i == 0 ||
270
+ v.downcase == "false" ||
271
+ v.downcase == "f" ||
272
+ v.downcase == "no" ||
273
+ v.downcase == "n" )
274
+ end
275
+ end
276
+
277
+ TypeTranslator.add_translator( "timestamp" ) { |type, value| Time.at( value.to_i ) }
278
+ TypeTranslator.add_translator( "tinyint" ) do |type, value|
279
+ if type =~ /\(\s*1\s*\)/
280
+ value.to_i == 1
281
+ else
282
+ value.to_i
283
+ end
284
+ end
285
+
286
+ end