ThiagoLelis-backgroundjob 1.0.4 → 1.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/bj/stdext.rb CHANGED
@@ -1,86 +1,86 @@
1
- class Hash
2
- begin
3
- method "to_options"
4
- rescue
5
- def to_options
6
- inject(Hash.new){|h, kv| h.update kv.first.to_s.to_sym => kv.last}
7
- end
8
- def to_options!
9
- replace to_options
10
- end
11
- end
12
-
13
- begin
14
- method "to_string_options"
15
- rescue
16
- def to_string_options
17
- inject(Hash.new){|h, kv| h.update kv.first.to_s => kv.last}
18
- end
19
- def to_string_options!
20
- replace to_string_options
21
- end
22
- end
23
-
24
- begin
25
- method "reverse_merge"
26
- rescue
27
- def reverse_merge other
28
- other.merge self
29
- end
30
- def reverse_merge! other
31
- replace reverse_merge(other)
32
- end
33
- end
34
-
35
- begin
36
- method "slice"
37
- rescue
38
- def slice(*keys)
39
- allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
40
- reject { |key,| !allowed.include?(key) }
41
- end
42
- end
43
-
44
- begin
45
- method "slice!"
46
- rescue
47
- def slice!(*keys)
48
- replace(slice(*keys))
49
- end
50
- end
51
- end
52
-
53
- class Object
54
- begin
55
- method "returning"
56
- rescue
57
- def returning value, &block
58
- block.call value
59
- value
60
- end
61
- end
62
- end
63
-
64
- class Object
65
- def singleton_class &block
66
- @singleton_class ||=
67
- class << self
68
- self
69
- end
70
- block ? @singleton_class.module_eval(&block) : @singleton_class
71
- end
72
- end
73
-
74
- class String
75
- begin
76
- method 'underscore'
77
- rescue
78
- def underscore
79
- gsub(/::/, '/').
80
- gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
81
- gsub(/([a-z\d])([A-Z])/,'\1_\2').
82
- tr("-", "_").
83
- downcase
84
- end
85
- end
86
- end
1
+ class Hash
2
+ begin
3
+ method "to_options"
4
+ rescue
5
+ def to_options
6
+ inject(Hash.new){|h, kv| h.update kv.first.to_s.to_sym => kv.last}
7
+ end
8
+ def to_options!
9
+ replace to_options
10
+ end
11
+ end
12
+
13
+ begin
14
+ method "to_string_options"
15
+ rescue
16
+ def to_string_options
17
+ inject(Hash.new){|h, kv| h.update kv.first.to_s => kv.last}
18
+ end
19
+ def to_string_options!
20
+ replace to_string_options
21
+ end
22
+ end
23
+
24
+ begin
25
+ method "reverse_merge"
26
+ rescue
27
+ def reverse_merge other
28
+ other.merge self
29
+ end
30
+ def reverse_merge! other
31
+ replace reverse_merge(other)
32
+ end
33
+ end
34
+
35
+ begin
36
+ method "slice"
37
+ rescue
38
+ def slice(*keys)
39
+ allowed = Set.new(respond_to?(:convert_key) ? keys.map { |key| convert_key(key) } : keys)
40
+ reject { |key,| !allowed.include?(key) }
41
+ end
42
+ end
43
+
44
+ begin
45
+ method "slice!"
46
+ rescue
47
+ def slice!(*keys)
48
+ replace(slice(*keys))
49
+ end
50
+ end
51
+ end
52
+
53
+ class Object
54
+ begin
55
+ method "returning"
56
+ rescue
57
+ def returning value, &block
58
+ block.call value
59
+ value
60
+ end
61
+ end
62
+ end
63
+
64
+ class Object
65
+ def singleton_class &block
66
+ @singleton_class ||=
67
+ class << self
68
+ self
69
+ end
70
+ block ? @singleton_class.module_eval(&block) : @singleton_class
71
+ end
72
+ end
73
+
74
+ class String
75
+ begin
76
+ method 'underscore'
77
+ rescue
78
+ def underscore
79
+ gsub(/::/, '/').
80
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
81
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
82
+ tr("-", "_").
83
+ downcase
84
+ end
85
+ end
86
+ end
data/lib/bj/table.rb CHANGED
@@ -1,404 +1,404 @@
1
- class Bj
2
- #
3
- # table base class
4
- #
5
- class Table < ActiveRecord::Base
6
- module ClassMethods
7
- attribute("list"){ Array.new }
8
- attribute("migration"){}
9
-
10
- def migration_code classname = "BjMigration"
11
- <<-code
12
- class #{ classname } < ActiveRecord::Migration
13
- def self.up
14
- Bj::Table.each{|table| table.up}
15
- end
16
- def self.down
17
- Bj::Table.reverse_each{|table| table.down}
18
- end
19
- end
20
- code
21
- end
22
-
23
- def up
24
- migration_class.up
25
- end
26
-
27
- def down
28
- migration_class.down
29
- end
30
-
31
- def migration_class
32
- table = self
33
- @migration_class ||=
34
- Class.new(ActiveRecord::Migration) do
35
- sc =
36
- class << self
37
- self
38
- end
39
- sc.module_eval{ attribute :table => table }
40
- sc.module_eval &table.migration
41
- end
42
- end
43
-
44
- def content_column_names
45
- @content_column_names = content_columns.map{|column| column.name}
46
- end
47
-
48
- def create_hash_for options
49
- options.to_options!
50
- hash = {}
51
- content_column_names.each do |key|
52
- key = key.to_s.to_sym
53
- hash[key] = options[key]
54
- end
55
- hash
56
- end
57
-
58
- def each *a, &b
59
- list.each *a, &b
60
- end
61
-
62
- def reverse_each *a, &b
63
- list.reverse.each *a, &b
64
- end
65
-
66
- # find(:first, :lock => true) does not work in oracle because
67
- # of the implicit usage of :limit. instead we have to find :all
68
- # and grab the first element returned. this is okay because the
69
- # conditions supplied to the find will return a small result set,
70
- # thus minimizing superfluous row locks.
71
- def find_first_and_lock(options = {})
72
- options[:lock] = true
73
- db_config = configurations[Bj.rails_env.to_s]
74
- case db_config['adapter']
75
- when 'oracle','oci'
76
- options.delete(:limit)
77
- find(:all, options).first
78
- else
79
- find(:first, options)
80
- end
81
- end
82
-
83
- end
84
- send :extend, ClassMethods
85
-
86
- module InstanceMethods
87
- def to_hash
88
- oh = OrderedHash.new
89
- self.class.content_column_names.each{|c| oh[c] = self[c]}
90
- oh
91
- end
92
- end
93
- send :include, InstanceMethods
94
-
95
- module RecursivelyInherited
96
- def inherited other
97
- super
98
- ensure
99
- (Table.list << other).uniq!
100
- basename = other.name.split(%r/::/).last.underscore
101
- Table.singleton_class{ attribute basename => other }
102
- other.send :extend, RecursivelyInherited
103
- end
104
- end
105
- send :extend, RecursivelyInherited
106
-
107
- #
108
- # table classes
109
- #
110
- class Job < Table
111
- set_table_name "bj_job"
112
- set_primary_key "#{ table_name }_id"
113
-
114
- migration {
115
- define_method :up do
116
- create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
117
-
118
- t.column "command" , :string, :limit => 2000
119
-
120
- t.column "state" , :string, :limit => 255
121
- t.column "priority" , :integer
122
- t.column "tag" , :string, :limit => 1000
123
- t.column "is_restartable" , :integer
124
-
125
- t.column "submitter" , :string, :limit => 255
126
- t.column "runner" , :string, :limit => 255
127
- t.column "pid" , :integer
128
-
129
- t.column "submitted_at" , :datetime
130
- t.column "started_at" , :datetime
131
- t.column "finished_at" , :datetime
132
-
133
- t.column "env" , :string, :limit => 4000
134
- t.column "stdin" , :string, :limit => 4000
135
- t.column "stdout" , :string, :limit => 4000
136
- t.column "stderr" , :string, :limit => 4000
137
- t.column "exit_status" , :integer
138
- end
139
- end
140
-
141
- define_method :down do
142
- drop_table table.table_name
143
- end
144
- }
145
-
146
- module ClassMethods
147
- def submit jobs, options = {}, &block
148
- jobs = Joblist.for jobs, options
149
- returned = []
150
- transaction do
151
- jobs.each do |job|
152
- job = create_hash_for(job.reverse_merge(submit_defaults))
153
- job = create! job
154
- returned << (block ? block.call(job) : job)
155
- end
156
- end
157
- returned
158
- end
159
-
160
- def submit_defaults
161
- {
162
- :state => "pending",
163
- :priority => 0,
164
- :tag => "",
165
- :is_restartable => true,
166
- :submitter => Bj.hostname,
167
- :submitted_at => Time.now
168
- }
169
- end
170
-
171
- end
172
- send :extend, ClassMethods
173
-
174
- module InstanceMethods
175
- def title
176
- "job[#{ id }](#{ command })"
177
- end
178
- def finished
179
- reload
180
- exit_status
181
- end
182
- alias_method "finished?", "finished"
183
- end
184
- send :include, InstanceMethods
185
- end
186
-
187
- class JobArchive < Job
188
- set_table_name "bj_job_archive"
189
- set_primary_key "#{ table_name }_id"
190
-
191
- migration {
192
- define_method(:up) do
193
- create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
194
- t.column "command" , :string, :limit => 2000
195
-
196
- t.column "state" , :string, :limit => 255
197
- t.column "priority" , :integer
198
- t.column "tag" , :string, :limit => 1000
199
- t.column "is_restartable" , :integer
200
-
201
- t.column "submitter" , :string, :limit => 255
202
- t.column "runner" , :string, :limit => 255
203
- t.column "pid" , :integer
204
-
205
- t.column "submitted_at" , :datetime
206
- t.column "started_at" , :datetime
207
- t.column "finished_at" , :datetime
208
- t.column "archived_at" , :datetime
209
-
210
- t.column "env" , :string, :limit => 4000
211
- t.column "stdin" , :string, :limit => 4000
212
- t.column "stdout" , :string, :limit => 4000
213
- t.column "stderr" , :string, :limit => 4000
214
- t.column "exit_status" , :integer
215
- end
216
- end
217
-
218
- define_method(:down) do
219
- drop_table table.table_name
220
- end
221
- }
222
- end
223
-
224
- # TODO - initialize with a set of global defaults and fallback to those on perhaps '* * key'
225
- class Config < Table
226
- set_table_name "bj_config"
227
- set_primary_key "#{ table_name }_id"
228
-
229
- migration {
230
- define_method(:up) do
231
- create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
232
- t.column "hostname" , :string, :limit => 255
233
- t.column "key" , :string, :limit => 255
234
- t.column "value" , :string, :limit => 4000
235
- t.column "cast" , :string, :limit => 255
236
- end
237
-
238
- begin
239
- add_index table.table_name, %w[ hostname key ], :unique => true
240
- rescue Exception
241
- STDERR.puts "WARNING: your database does not support unique indexes on text fields!?"
242
- end
243
- end
244
-
245
- define_method(:down) do
246
- begin
247
- remove_index table.table_name, :column => %w[ hostname key ]
248
- rescue Exception
249
- end
250
- drop_table table.table_name
251
- end
252
- }
253
-
254
- module ClassMethods
255
- def [] key
256
- get key
257
- end
258
-
259
- def get key, options = {}
260
- transaction do
261
- options.to_options!
262
- hostname = options[:hostname] || Bj.hostname
263
- record = find :first, :conditions => conditions(:key => key, :hostname => hostname)
264
- record ? record.value : default_for(key)
265
- end
266
- end
267
-
268
- def conditions options = {}
269
- options.to_options!
270
- options.reverse_merge!(
271
- :hostname => Bj.hostname
272
- )
273
- options
274
- end
275
-
276
- def default_for key
277
- record = find :first, :conditions => conditions(:key => key, :hostname => '*')
278
- record ? record.value : nil
279
- end
280
-
281
- def []= key, value
282
- set key, value
283
- end
284
-
285
- def set key, value, options = {}
286
- transaction do
287
- options.to_options!
288
- hostname = options[:hostname] || Bj.hostname
289
- record = find_first_and_lock :conditions => conditions(:key => key, :hostname => hostname), :lock => true
290
- cast = options[:cast] || cast_for(value)
291
- key = key.to_s
292
- value = value.to_s
293
- if record
294
- record["value"] = value
295
- record["cast"] = cast
296
- record.save!
297
- else
298
- create! :hostname => hostname, :key => key, :value => value, :cast => cast
299
- end
300
- value
301
- end
302
- end
303
-
304
- def delete key
305
- transaction do
306
- record = find_first_and_lock :conditions => conditions(:key => key), :lock => true
307
- if record
308
- record.destroy
309
- record
310
- else
311
- nil
312
- end
313
- end
314
- end
315
-
316
- def has_key? key
317
- record = find :first, :conditions => conditions(:key => key)
318
- record ? record : false
319
- end
320
- alias_method "has_key", "has_key?"
321
-
322
- def keys
323
- find(:all, :conditions => conditions).map(&:key)
324
- end
325
-
326
- def values
327
- find(:all, :conditions => conditions).map(&:value)
328
- end
329
-
330
- def for options = {}
331
- oh = OrderedHash.new
332
- find(:all, :conditions => conditions(options)).each do |record|
333
- oh[record.key] = record.value
334
- end
335
- oh
336
- end
337
-
338
- def cast_for value
339
- case value
340
- when TrueClass, FalseClass
341
- 'to_bool'
342
- when NilClass
343
- 'to_nil'
344
- when Fixnum, Bignum
345
- 'to_i'
346
- when Float
347
- 'to_f'
348
- when Time
349
- 'to_time'
350
- when Symbol
351
- 'to_sym'
352
- else
353
- case value.to_s
354
- when %r/^\d+$/
355
- 'to_i'
356
- when %r/^\d+\.\d+$/
357
- 'to_f'
358
- when %r/^nil$|^$/
359
- 'to_nil'
360
- when %r/^true|false$/
361
- 'to_bool'
362
- else
363
- 'to_s'
364
- end
365
- end
366
- end
367
-
368
- def casts
369
- @casts ||= {
370
- 'to_bool' => lambda do |value|
371
- value.to_s =~ %r/^true$/i ? true : false
372
- end,
373
- 'to_i' => lambda do |value|
374
- Integer value.to_s.gsub(%r/^(-)?0*/,'\1')
375
- end,
376
- 'to_f' => lambda do |value|
377
- Float value.to_s.gsub(%r/^0*/,'')
378
- end,
379
- 'to_time' => lambda do |value|
380
- Time.parse(value.to_s)
381
- end,
382
- 'to_sym' => lambda do |value|
383
- value.to_s.to_sym
384
- end,
385
- 'to_nil' => lambda do |value|
386
- value.to_s =~ %r/^nil$|^$/i ? nil : value.to_s
387
- end,
388
- 'to_s' => lambda do |value|
389
- value.to_s
390
- end,
391
- }
392
- end
393
- end
394
- send :extend, ClassMethods
395
-
396
- module InstanceMethods
397
- def value
398
- self.class.casts[cast][self["value"]]
399
- end
400
- end
401
- send :include, InstanceMethods
402
- end
403
- end
404
- end
1
+ class Bj
2
+ #
3
+ # table base class
4
+ #
5
+ class Table < ActiveRecord::Base
6
+ module ClassMethods
7
+ attribute("list"){ Array.new }
8
+ attribute("migration"){}
9
+
10
+ def migration_code classname = "BjMigration"
11
+ <<-code
405
12
  class #{ classname } < ActiveRecord::Migration
13
+ require 'bj'
14
+ def self.up
15
+ Bj::Table.each{|table| table.up}
16
+ end
17
+ def self.down
18
+ Bj::Table.reverse_each{|table| table.down}
19
+ end
20
+ end
21
+ code
22
+ end
23
+
24
+ def up
25
+ migration_class.up
26
+ end
27
+
28
+ def down
29
+ migration_class.down
30
+ end
31
+
32
+ def migration_class
33
+ table = self
34
+ @migration_class ||=
35
+ Class.new(ActiveRecord::Migration) do
36
+ sc =
37
+ class << self
38
+ self
39
+ end
40
+ sc.module_eval{ attribute :table => table }
41
+ sc.module_eval &table.migration
42
+ end
43
+ end
44
+
45
+ def content_column_names
46
+ @content_column_names = content_columns.map{|column| column.name}
47
+ end
48
+
49
+ def create_hash_for options
50
+ options.to_options!
51
+ hash = {}
52
+ content_column_names.each do |key|
53
+ key = key.to_s.to_sym
54
+ hash[key] = options[key]
55
+ end
56
+ hash
57
+ end
58
+
59
+ def each *a, &b
60
+ list.each *a, &b
61
+ end
62
+
63
+ def reverse_each *a, &b
64
+ list.reverse.each *a, &b
65
+ end
66
+
67
+ # find(:first, :lock => true) does not work in oracle because
68
+ # of the implicit usage of :limit. instead we have to find :all
69
+ # and grab the first element returned. this is okay because the
70
+ # conditions supplied to the find will return a small result set,
71
+ # thus minimizing superfluous row locks.
72
+ def find_first_and_lock(options = {})
73
+ options[:lock] = true
74
+ db_config = configurations[Bj.rails_env.to_s]
75
+ case db_config['adapter']
76
+ when 'oracle','oci'
77
+ options.delete(:limit)
78
+ find(:all, options).first
79
+ else
80
+ find(:first, options)
81
+ end
82
+ end
83
+
84
+ end
85
+ send :extend, ClassMethods
86
+
87
+ module InstanceMethods
88
+ def to_hash
89
+ oh = OrderedHash.new
90
+ self.class.content_column_names.each{|c| oh[c] = self[c]}
91
+ oh
92
+ end
93
+ end
94
+ send :include, InstanceMethods
95
+
96
+ module RecursivelyInherited
97
+ def inherited other
98
+ super
99
+ ensure
100
+ (Table.list << other).uniq!
101
+ basename = other.name.split(%r/::/).last.underscore
102
+ Table.singleton_class{ attribute basename => other }
103
+ other.send :extend, RecursivelyInherited
104
+ end
105
+ end
106
+ send :extend, RecursivelyInherited
107
+
108
+ #
109
+ # table classes
110
+ #
111
+ class Job < Table
112
+ set_table_name "bj_job"
113
+ set_primary_key "#{ table_name }_id"
114
+
115
+ migration {
116
+ define_method :up do
117
+ create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
118
+
119
+ t.column "command" , :string, :limit => 2000
120
+
121
+ t.column "state" , :string, :limit => 255
122
+ t.column "priority" , :integer
123
+ t.column "tag" , :string, :limit => 1000
124
+ t.column "is_restartable" , :integer
125
+
126
+ t.column "submitter" , :string, :limit => 255
127
+ t.column "runner" , :string, :limit => 255
128
+ t.column "pid" , :integer
129
+
130
+ t.column "submitted_at" , :datetime
131
+ t.column "started_at" , :datetime
132
+ t.column "finished_at" , :datetime
133
+
134
+ t.column "env" , :string, :limit => 4000
135
+ t.column "stdin" , :string, :limit => 4000
136
+ t.column "stdout" , :string, :limit => 4000
137
+ t.column "stderr" , :string, :limit => 4000
138
+ t.column "exit_status" , :integer
139
+ end
140
+ end
141
+
142
+ define_method :down do
143
+ drop_table table.table_name
144
+ end
145
+ }
146
+
147
+ module ClassMethods
148
+ def submit jobs, options = {}, &block
149
+ jobs = Joblist.for jobs, options
150
+ returned = []
151
+ transaction do
152
+ jobs.each do |job|
153
+ job = create_hash_for(job.reverse_merge(submit_defaults))
154
+ job = create! job
155
+ returned << (block ? block.call(job) : job)
156
+ end
157
+ end
158
+ returned
159
+ end
160
+
161
+ def submit_defaults
162
+ {
163
+ :state => "pending",
164
+ :priority => 0,
165
+ :tag => "",
166
+ :is_restartable => true,
167
+ :submitter => Bj.hostname,
168
+ :submitted_at => Time.now
169
+ }
170
+ end
171
+
172
+ end
173
+ send :extend, ClassMethods
174
+
175
+ module InstanceMethods
176
+ def title
177
+ "job[#{ id }](#{ command })"
178
+ end
179
+ def finished
180
+ reload
181
+ exit_status
182
+ end
183
+ alias_method "finished?", "finished"
184
+ end
185
+ send :include, InstanceMethods
186
+ end
187
+
188
+ class JobArchive < Job
189
+ set_table_name "bj_job_archive"
190
+ set_primary_key "#{ table_name }_id"
191
+
192
+ migration {
193
+ define_method(:up) do
194
+ create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
195
+ t.column "command" , :string, :limit => 2000
196
+
197
+ t.column "state" , :string, :limit => 255
198
+ t.column "priority" , :integer
199
+ t.column "tag" , :string, :limit => 1000
200
+ t.column "is_restartable" , :integer
201
+
202
+ t.column "submitter" , :string, :limit => 255
203
+ t.column "runner" , :string, :limit => 255
204
+ t.column "pid" , :integer
205
+
206
+ t.column "submitted_at" , :datetime
207
+ t.column "started_at" , :datetime
208
+ t.column "finished_at" , :datetime
209
+ t.column "archived_at" , :datetime
210
+
211
+ t.column "env" , :string, :limit => 4000
212
+ t.column "stdin" , :string, :limit => 4000
213
+ t.column "stdout" , :string, :limit => 4000
214
+ t.column "stderr" , :string, :limit => 4000
215
+ t.column "exit_status" , :integer
216
+ end
217
+ end
218
+
219
+ define_method(:down) do
220
+ drop_table table.table_name
221
+ end
222
+ }
223
+ end
224
+
225
+ # TODO - initialize with a set of global defaults and fallback to those on perhaps '* * key'
226
+ class Config < Table
227
+ set_table_name "bj_config"
228
+ set_primary_key "#{ table_name }_id"
229
+
230
+ migration {
231
+ define_method(:up) do
232
+ create_table table.table_name, :primary_key => table.primary_key, :force => true do |t|
233
+ t.column "hostname" , :string, :limit => 255
234
+ t.column "key" , :string, :limit => 255
235
+ t.column "value" , :string, :limit => 4000
236
+ t.column "cast" , :string, :limit => 255
237
+ end
238
+
239
+ begin
240
+ add_index table.table_name, %w[ hostname key ], :unique => true
241
+ rescue Exception
242
+ STDERR.puts "WARNING: your database does not support unique indexes on text fields!?"
243
+ end
244
+ end
245
+
246
+ define_method(:down) do
247
+ begin
248
+ remove_index table.table_name, :column => %w[ hostname key ]
249
+ rescue Exception
250
+ end
251
+ drop_table table.table_name
252
+ end
253
+ }
254
+
255
+ module ClassMethods
256
+ def [] key
257
+ get key
258
+ end
259
+
260
+ def get key, options = {}
261
+ transaction do
262
+ options.to_options!
263
+ hostname = options[:hostname] || Bj.hostname
264
+ record = find :first, :conditions => conditions(:key => key, :hostname => hostname)
265
+ record ? record.value : default_for(key)
266
+ end
267
+ end
268
+
269
+ def conditions options = {}
270
+ options.to_options!
271
+ options.reverse_merge!(
272
+ :hostname => Bj.hostname
273
+ )
274
+ options
275
+ end
276
+
277
+ def default_for key
278
+ record = find :first, :conditions => conditions(:key => key, :hostname => '*')
279
+ record ? record.value : nil
280
+ end
281
+
282
+ def []= key, value
283
+ set key, value
284
+ end
285
+
286
+ def set key, value, options = {}
287
+ transaction do
288
+ options.to_options!
289
+ hostname = options[:hostname] || Bj.hostname
290
+ record = find_first_and_lock :conditions => conditions(:key => key, :hostname => hostname), :lock => true
291
+ cast = options[:cast] || cast_for(value)
292
+ key = key.to_s
293
+ value = value.to_s
294
+ if record
295
+ record["value"] = value
296
+ record["cast"] = cast
297
+ record.save!
298
+ else
299
+ create! :hostname => hostname, :key => key, :value => value, :cast => cast
300
+ end
301
+ value
302
+ end
303
+ end
304
+
305
+ def delete key
306
+ transaction do
307
+ record = find_first_and_lock :conditions => conditions(:key => key), :lock => true
308
+ if record
309
+ record.destroy
310
+ record
311
+ else
312
+ nil
313
+ end
314
+ end
315
+ end
316
+
317
+ def has_key? key
318
+ record = find :first, :conditions => conditions(:key => key)
319
+ record ? record : false
320
+ end
321
+ alias_method "has_key", "has_key?"
322
+
323
+ def keys
324
+ find(:all, :conditions => conditions).map(&:key)
325
+ end
326
+
327
+ def values
328
+ find(:all, :conditions => conditions).map(&:value)
329
+ end
330
+
331
+ def for options = {}
332
+ oh = OrderedHash.new
333
+ find(:all, :conditions => conditions(options)).each do |record|
334
+ oh[record.key] = record.value
335
+ end
336
+ oh
337
+ end
338
+
339
+ def cast_for value
340
+ case value
341
+ when TrueClass, FalseClass
342
+ 'to_bool'
343
+ when NilClass
344
+ 'to_nil'
345
+ when Fixnum, Bignum
346
+ 'to_i'
347
+ when Float
348
+ 'to_f'
349
+ when Time
350
+ 'to_time'
351
+ when Symbol
352
+ 'to_sym'
353
+ else
354
+ case value.to_s
355
+ when %r/^\d+$/
356
+ 'to_i'
357
+ when %r/^\d+\.\d+$/
358
+ 'to_f'
359
+ when %r/^nil$|^$/
360
+ 'to_nil'
361
+ when %r/^true|false$/
362
+ 'to_bool'
363
+ else
364
+ 'to_s'
365
+ end
366
+ end
367
+ end
368
+
369
+ def casts
370
+ @casts ||= {
371
+ 'to_bool' => lambda do |value|
372
+ value.to_s =~ %r/^true$/i ? true : false
373
+ end,
374
+ 'to_i' => lambda do |value|
375
+ Integer value.to_s.gsub(%r/^(-)?0*/,'\1')
376
+ end,
377
+ 'to_f' => lambda do |value|
378
+ Float value.to_s.gsub(%r/^0*/,'')
379
+ end,
380
+ 'to_time' => lambda do |value|
381
+ Time.parse(value.to_s)
382
+ end,
383
+ 'to_sym' => lambda do |value|
384
+ value.to_s.to_sym
385
+ end,
386
+ 'to_nil' => lambda do |value|
387
+ value.to_s =~ %r/^nil$|^$/i ? nil : value.to_s
388
+ end,
389
+ 'to_s' => lambda do |value|
390
+ value.to_s
391
+ end,
392
+ }
393
+ end
394
+ end
395
+ send :extend, ClassMethods
396
+
397
+ module InstanceMethods
398
+ def value
399
+ self.class.casts[cast][self["value"]]
400
+ end
401
+ end
402
+ send :include, InstanceMethods
403
+ end
404
+ end
405
+ end