rq-ruby1.8 3.4.3

Sign up to get free protection for your applications and to get access to all the features.
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