say_when 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/Gemfile +4 -0
- data/Rakefile +1 -0
- data/generators/say_when_migration/say_when_migration_generator.rb +11 -0
- data/generators/say_when_migration/templates/migration.rb +48 -0
- data/lib/generators/.DS_Store +0 -0
- data/lib/generators/say_when/migration/migration_generator.rb +13 -0
- data/lib/generators/say_when/migration/templates/migration.rb +47 -0
- data/lib/say_when/base_job.rb +96 -0
- data/lib/say_when/cron_expression.rb +621 -0
- data/lib/say_when/processor/active_messaging.rb +22 -0
- data/lib/say_when/processor/base.rb +17 -0
- data/lib/say_when/processor/simple.rb +15 -0
- data/lib/say_when/scheduler.rb +129 -0
- data/lib/say_when/storage/active_record/acts.rb +85 -0
- data/lib/say_when/storage/active_record/job.rb +85 -0
- data/lib/say_when/storage/active_record/job_execution.rb +17 -0
- data/lib/say_when/storage/memory/base.rb +34 -0
- data/lib/say_when/storage/memory/job.rb +48 -0
- data/lib/say_when/storage/mongoid/job.rb +15 -0
- data/lib/say_when/tasks.rb +22 -0
- data/lib/say_when/triggers/base.rb +11 -0
- data/lib/say_when/triggers/cron_strategy.rb +22 -0
- data/lib/say_when/triggers/once_strategy.rb +30 -0
- data/lib/say_when/version.rb +3 -0
- data/lib/say_when.rb +28 -0
- data/lib/tasks/say_when.rake +2 -0
- data/say_when.gemspec +26 -0
- data/spec/active_record_spec_helper.rb +11 -0
- data/spec/db/schema.rb +36 -0
- data/spec/db/test.db +0 -0
- data/spec/mongoid_spec_helper.rb +7 -0
- data/spec/say_when/cron_expression_spec.rb +72 -0
- data/spec/say_when/scheduler_spec.rb +76 -0
- data/spec/say_when/storage/active_record/job_spec.rb +84 -0
- data/spec/say_when/storage/memory/job_spec.rb +31 -0
- data/spec/say_when/storage/memory/trigger_spec.rb +54 -0
- data/spec/say_when/storage/mongoid/trigger_spec.rb +57 -0
- data/spec/spec.opts +4 -0
- data/spec/spec_helper.rb +46 -0
- data/spec/support/models.rb +31 -0
- metadata +224 -0
@@ -0,0 +1,129 @@
|
|
1
|
+
module SayWhen
|
2
|
+
|
3
|
+
class Scheduler
|
4
|
+
|
5
|
+
DEFAULT_PROCESSOR_CLASS = SayWhen::Processor::Simple
|
6
|
+
DEFAULT_STORAGE_STRATEGY = :memory
|
7
|
+
|
8
|
+
@@scheduler = nil
|
9
|
+
@@lock = nil
|
10
|
+
|
11
|
+
attr_accessor :storage_strategy, :processor_class, :tick_length
|
12
|
+
|
13
|
+
attr_accessor :running
|
14
|
+
|
15
|
+
# support for a singleton scheduler, but you are not restricted to this
|
16
|
+
class << self
|
17
|
+
|
18
|
+
def scheduler
|
19
|
+
self.lock.synchronize {
|
20
|
+
if @@scheduler.nil?
|
21
|
+
@@scheduler = self.new
|
22
|
+
end
|
23
|
+
}
|
24
|
+
@@scheduler
|
25
|
+
end
|
26
|
+
|
27
|
+
def configure
|
28
|
+
yield self.scheduler
|
29
|
+
self.scheduler
|
30
|
+
end
|
31
|
+
|
32
|
+
def lock
|
33
|
+
@@lock ||= Mutex.new
|
34
|
+
end
|
35
|
+
|
36
|
+
def schedule(job)
|
37
|
+
self.scheduler.schedule(job)
|
38
|
+
end
|
39
|
+
|
40
|
+
def start
|
41
|
+
self.scheduler.start
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
def initialize
|
47
|
+
self.tick_length = 1
|
48
|
+
end
|
49
|
+
|
50
|
+
def processor
|
51
|
+
if @processor.nil?
|
52
|
+
@processor_class ||= DEFAULT_PROCESSOR_CLASS
|
53
|
+
@processor = @processor_class.new(self)
|
54
|
+
end
|
55
|
+
@processor
|
56
|
+
end
|
57
|
+
|
58
|
+
def start
|
59
|
+
logger.info "SayWhen::Scheduler starting"
|
60
|
+
|
61
|
+
[$stdout, $stderr].each{|s| s.sync = true; s.flush}
|
62
|
+
trap("TERM", "EXIT")
|
63
|
+
trap("QUIT") { stop }
|
64
|
+
|
65
|
+
begin
|
66
|
+
self.running = true
|
67
|
+
|
68
|
+
logger.info "SayWhen::Scheduler running"
|
69
|
+
job = nil
|
70
|
+
while running
|
71
|
+
begin
|
72
|
+
time_now = Time.now
|
73
|
+
logger.debug "SayWhen:: Looking for job that should be ready to fire before #{time_now}"
|
74
|
+
job = job_class.acquire_next(time_now)
|
75
|
+
if job.nil?
|
76
|
+
logger.debug "SayWhen:: no jobs to acquire, sleep"
|
77
|
+
sleep(tick_length)
|
78
|
+
else
|
79
|
+
logger.debug "SayWhen:: got a job: #{job.inspect}"
|
80
|
+
# delegate processing the trigger to the processor
|
81
|
+
self.processor.process(job)
|
82
|
+
logger.debug "SayWhen:: job processed"
|
83
|
+
|
84
|
+
# this should update next fire at, and put back in list of scheduled jobs
|
85
|
+
job.fired
|
86
|
+
logger.debug "SayWhen:: job fired complete"
|
87
|
+
|
88
|
+
end
|
89
|
+
rescue Object=>ex
|
90
|
+
begin
|
91
|
+
job_msg = job && " job:'#{job.inspect}'"
|
92
|
+
logger.error "SayWhen:: Failure to process#{job_msg}: #{ex.message}\n\t#{ex.backtrace.join("\t\n")}"
|
93
|
+
job.release if job
|
94
|
+
rescue
|
95
|
+
puts ex
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
logger.info "SayWhen::Scheduler stopped"
|
102
|
+
end
|
103
|
+
|
104
|
+
def stop
|
105
|
+
logger.info "SayWhen::Scheduler stopping..."
|
106
|
+
self.running = false
|
107
|
+
end
|
108
|
+
|
109
|
+
def job_class
|
110
|
+
@job_class ||= load_job_class
|
111
|
+
end
|
112
|
+
|
113
|
+
def load_job_class
|
114
|
+
strategy = @storage_strategy || DEFAULT_STORAGE_STRATEGY
|
115
|
+
require "say_when/storage/#{strategy}/job"
|
116
|
+
job_class_name = "SayWhen::Storage::#{strategy.to_s.camelize}::Job"
|
117
|
+
job_class_name.constantize
|
118
|
+
end
|
119
|
+
|
120
|
+
def schedule(job)
|
121
|
+
job_class.create(job)
|
122
|
+
end
|
123
|
+
|
124
|
+
def logger
|
125
|
+
SayWhen::logger
|
126
|
+
end
|
127
|
+
|
128
|
+
end
|
129
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module SayWhen #:nodoc:
|
2
|
+
module Storage #:nodoc:
|
3
|
+
module ActiveRecord #:nodoc:
|
4
|
+
module Acts #:nodoc:
|
5
|
+
|
6
|
+
def self.included(base) # :nodoc:
|
7
|
+
base.extend ClassMethods
|
8
|
+
end
|
9
|
+
|
10
|
+
module ClassMethods
|
11
|
+
def acts_as_scheduled
|
12
|
+
include SayWhen::Storage::ActiveRecord::Acts::InstanceMethods
|
13
|
+
|
14
|
+
has_many :scheduled_jobs, :as=>:scheduled, :class_name=>'SayWhen::Storage::ActiveRecord::Job'
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
module InstanceMethods
|
19
|
+
|
20
|
+
def schedule_cron(expression, time_zone, job={})
|
21
|
+
options = job_options(job)
|
22
|
+
options[:trigger_strategy] = :cron
|
23
|
+
options[:trigger_options] = {:expression => expression, :time_zone => time_zone}
|
24
|
+
Scheduler.schedule(options)
|
25
|
+
end
|
26
|
+
|
27
|
+
def schedule_once(time, job={})
|
28
|
+
options = job_options(job)
|
29
|
+
options[:trigger_strategy] = :once
|
30
|
+
options[:trigger_options] = time
|
31
|
+
Scheduler.schedule(options)
|
32
|
+
end
|
33
|
+
|
34
|
+
def schedule_in(after, job={})
|
35
|
+
options = job_options(job)
|
36
|
+
options[:trigger_strategy] = :once
|
37
|
+
options[:trigger_options] = Time.now + after
|
38
|
+
Scheduler.schedule(options)
|
39
|
+
end
|
40
|
+
|
41
|
+
# helpers
|
42
|
+
|
43
|
+
def job_options(job)
|
44
|
+
{ :scheduled => self,
|
45
|
+
:job_class => extract_job_class(job),
|
46
|
+
:job_method => extract_job_method(job),
|
47
|
+
:data => extract_data(job) }
|
48
|
+
end
|
49
|
+
|
50
|
+
def extract_job_class(job)
|
51
|
+
if job.is_a?(Hash)
|
52
|
+
job[:class]
|
53
|
+
elsif job.is_a?(Class)
|
54
|
+
job.name
|
55
|
+
elsif job.is_a?(String)
|
56
|
+
job
|
57
|
+
else
|
58
|
+
raise "Could not identify job class from: #{job}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def extract_job_method(job)
|
63
|
+
if job.is_a?(Hash)
|
64
|
+
job[:method]
|
65
|
+
else
|
66
|
+
'execute'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def extract_data(job)
|
71
|
+
if job.is_a?(Hash)
|
72
|
+
job[:data]
|
73
|
+
else
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
end # InstanceMethods
|
79
|
+
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
ActiveRecord::Base.send(:include, SayWhen::Storage::ActiveRecord::Acts) unless ActiveRecord::Base.include?(SayWhen::Storage::ActiveRecord::Acts)
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'say_when/base_job'
|
3
|
+
require 'say_when/storage/active_record/job_execution'
|
4
|
+
require 'say_when/storage/active_record/acts'
|
5
|
+
|
6
|
+
module SayWhen
|
7
|
+
module Storage
|
8
|
+
module ActiveRecord
|
9
|
+
|
10
|
+
class Job < ::ActiveRecord::Base
|
11
|
+
|
12
|
+
include SayWhen::BaseJob
|
13
|
+
|
14
|
+
self.table_name = "say_when_jobs"
|
15
|
+
|
16
|
+
|
17
|
+
serialize :trigger_options
|
18
|
+
serialize :data
|
19
|
+
belongs_to :scheduled, :polymorphic => true
|
20
|
+
has_many :job_executions, :class_name=>'SayWhen::Storage::ActiveRecord::JobExecution'
|
21
|
+
|
22
|
+
def self.acquire_next(no_later_than)
|
23
|
+
SayWhen::Storage::ActiveRecord::Job.transaction do
|
24
|
+
# select and lock the next job that needs executin' (status waiting, and after no_later_than)
|
25
|
+
next_job = find(:first,
|
26
|
+
:lock => true,
|
27
|
+
:order => 'next_fire_at ASC',
|
28
|
+
:conditions => ['status = ? and ? >= next_fire_at',
|
29
|
+
STATE_WAITING,
|
30
|
+
no_later_than.in_time_zone('UTC')])
|
31
|
+
|
32
|
+
# make sure there is a job ready to run
|
33
|
+
return nil if next_job.nil?
|
34
|
+
|
35
|
+
# set status to acquired to take it out of rotation
|
36
|
+
next_job.update_attribute(:status, STATE_ACQUIRED)
|
37
|
+
|
38
|
+
return next_job
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def before_create
|
43
|
+
self.status = STATE_WAITING
|
44
|
+
self.next_fire_at = self.trigger.next_fire_at(Time.now)
|
45
|
+
end
|
46
|
+
|
47
|
+
def fired
|
48
|
+
Job.transaction {
|
49
|
+
super
|
50
|
+
self.save!
|
51
|
+
}
|
52
|
+
end
|
53
|
+
|
54
|
+
def release
|
55
|
+
Job.transaction {
|
56
|
+
super
|
57
|
+
self.save!
|
58
|
+
}
|
59
|
+
end
|
60
|
+
|
61
|
+
|
62
|
+
# default impl with some error handling and result recording
|
63
|
+
def execute
|
64
|
+
result = nil
|
65
|
+
execution = SayWhen::Storage::ActiveRecord::JobExecution.create(:job=>self, :status=>'executing', :start_at=>Time.now)
|
66
|
+
|
67
|
+
begin
|
68
|
+
result = self.execute_job(data)
|
69
|
+
execution.result = result
|
70
|
+
execution.status = 'complete'
|
71
|
+
rescue Object=>ex
|
72
|
+
execution.result = "#{ex.class.name}: #{ex.message}\n\t#{ex.backtrace.join("\n\t")}"
|
73
|
+
execution.status = 'error'
|
74
|
+
end
|
75
|
+
|
76
|
+
execution.end_at = Time.now
|
77
|
+
execution.save!
|
78
|
+
result
|
79
|
+
end
|
80
|
+
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
|
3
|
+
module SayWhen
|
4
|
+
module Storage
|
5
|
+
module ActiveRecord
|
6
|
+
|
7
|
+
class JobExecution < ::ActiveRecord::Base
|
8
|
+
|
9
|
+
self.table_name = "say_when_job_executions"
|
10
|
+
|
11
|
+
belongs_to :job, :class_name=>'SayWhen::Storage::ActiveRecord::Job'
|
12
|
+
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module SayWhen
|
2
|
+
module Storage
|
3
|
+
module Memory
|
4
|
+
|
5
|
+
module Base
|
6
|
+
|
7
|
+
attr_accessor :props
|
8
|
+
|
9
|
+
def has_properties(*args)
|
10
|
+
@props ||= []
|
11
|
+
args.each do |a|
|
12
|
+
unless @props.member?(a.to_s)
|
13
|
+
@props << a.to_s
|
14
|
+
class_eval { attr_accessor(a.to_sym) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(base)
|
20
|
+
base.extend self
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(args={})
|
24
|
+
args.each do |k,v|
|
25
|
+
if self.class.props.member?(k.to_s)
|
26
|
+
self.send("#{k}=", v)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'say_when/storage/memory/base'
|
2
|
+
|
3
|
+
module SayWhen
|
4
|
+
module Store
|
5
|
+
module Memory
|
6
|
+
|
7
|
+
# define a trigger class
|
8
|
+
class Job
|
9
|
+
|
10
|
+
cattr_accessor :jobs
|
11
|
+
@@jobs = SortedSet.new
|
12
|
+
|
13
|
+
include SayWhen::Storage::Memory::Base
|
14
|
+
include SayWhen::BaseJob
|
15
|
+
|
16
|
+
has_properties :group, :name, :status, :start_at, :end_at
|
17
|
+
has_properties :trigger_strategy, :trigger_options, :last_fire_at, :next_fire_at
|
18
|
+
has_properties :job_class, :job_method, :data
|
19
|
+
has_properties :scheduled
|
20
|
+
|
21
|
+
def self.acquire_next(no_later_than)
|
22
|
+
self.lock.synchronize {
|
23
|
+
|
24
|
+
next_job = jobs.detect(nil) do |j|
|
25
|
+
(j.status == STATE_WAITING) && (j.next_fire_at.to_i <= no_later_than.to_i)
|
26
|
+
end
|
27
|
+
|
28
|
+
next_job.status = STATE_ACQUIRED if next_job
|
29
|
+
next_job
|
30
|
+
}
|
31
|
+
end
|
32
|
+
|
33
|
+
def initialize(options={})
|
34
|
+
super
|
35
|
+
self.status = STATE_WAITING unless self.status
|
36
|
+
self.next_fire_at = self.trigger.next_fire_at(Time.now)
|
37
|
+
self.class.jobs << self
|
38
|
+
end
|
39
|
+
|
40
|
+
def <=>(job)
|
41
|
+
self.next_fire_at.to_i <=> job.next_fire_at.to_i
|
42
|
+
end
|
43
|
+
|
44
|
+
end
|
45
|
+
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
namespace :say_when do
|
2
|
+
task :setup
|
3
|
+
|
4
|
+
desc "Start the SayWhen Scheduler"
|
5
|
+
task :start => [ :preload ] do
|
6
|
+
require 'say_when'
|
7
|
+
SayWhen::Scheduler.start
|
8
|
+
end
|
9
|
+
|
10
|
+
# Preload app files if this is Rails
|
11
|
+
# thanks resque
|
12
|
+
task :preload => :setup do
|
13
|
+
if defined?(Rails) && Rails.respond_to?(:application)
|
14
|
+
# Rails 3
|
15
|
+
Rails.application.eager_load!
|
16
|
+
elsif defined?(Rails::Initializer)
|
17
|
+
# Rails 2.3
|
18
|
+
$rails_rake_task = false
|
19
|
+
Rails::Initializer.run :load_application_classes
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
require 'say_when/triggers/base'
|
2
|
+
require 'say_when/cron_expression'
|
3
|
+
|
4
|
+
module SayWhen
|
5
|
+
module Triggers
|
6
|
+
class CronStrategy
|
7
|
+
|
8
|
+
include SayWhen::Triggers::Base
|
9
|
+
|
10
|
+
attr_accessor :cron_expression
|
11
|
+
|
12
|
+
def initialize(options={})
|
13
|
+
@cron_expression = SayWhen::CronExpression.new(options)
|
14
|
+
end
|
15
|
+
|
16
|
+
def next_fire_at(time=Time.now)
|
17
|
+
cron_expression.next_fire_at(time)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'say_when/triggers/base'
|
2
|
+
|
3
|
+
module SayWhen
|
4
|
+
module Triggers
|
5
|
+
class OnceStrategy
|
6
|
+
|
7
|
+
include SayWhen::Triggers::Base
|
8
|
+
|
9
|
+
attr_accessor :once_at
|
10
|
+
|
11
|
+
def initialize(options=nil)
|
12
|
+
options ||= Time.now
|
13
|
+
# if it's a hash, pull out the time
|
14
|
+
@once_at = if options.is_a?(Time) || options.acts_like_time?
|
15
|
+
options
|
16
|
+
elsif options.is_a?(Hash) && options[:at]
|
17
|
+
options[:at]
|
18
|
+
else
|
19
|
+
Time.now
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
|
24
|
+
def next_fire_at(time=Time.now)
|
25
|
+
once_at if once_at >= time
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
data/lib/say_when.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'active_support'
|
2
|
+
|
3
|
+
require "say_when/version"
|
4
|
+
require 'say_when/base_job'
|
5
|
+
require 'say_when/cron_expression'
|
6
|
+
require 'say_when/processor/base'
|
7
|
+
require 'say_when/processor/simple'
|
8
|
+
require 'say_when/scheduler'
|
9
|
+
|
10
|
+
require 'say_when/processor/active_messaging' if defined?(ActiveMessaging)
|
11
|
+
|
12
|
+
require 'say_when/storage/active_record/job' if defined?(ActiveRecord)
|
13
|
+
|
14
|
+
module SayWhen
|
15
|
+
|
16
|
+
def SayWhen.logger=(logger)
|
17
|
+
@@logger = logger
|
18
|
+
end
|
19
|
+
|
20
|
+
def SayWhen.logger
|
21
|
+
unless defined?(@@logger)
|
22
|
+
@@logger = Rails.logger if defined?(Rails.logger) && Rails.logger
|
23
|
+
@@logger = Logger.new(STDOUT) unless defined?(@@logger)
|
24
|
+
end
|
25
|
+
@@logger
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
data/say_when.gemspec
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "say_when/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "say_when"
|
7
|
+
s.version = SayWhen::VERSION
|
8
|
+
s.authors = ["Andrew Kuklewicz"]
|
9
|
+
s.email = ["andrew@prx.org"]
|
10
|
+
s.homepage = "http://labs.prx.org"
|
11
|
+
s.summary = %q{Scheduling system for programmatically defined and stored jobs.}
|
12
|
+
|
13
|
+
s.files = `git ls-files`.split("\n")
|
14
|
+
s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
|
15
|
+
s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
|
16
|
+
s.require_paths = ["lib"]
|
17
|
+
|
18
|
+
s.add_development_dependency "activemessaging", '~> 0.9.0'
|
19
|
+
s.add_development_dependency "activesupport", '~> 2.3.14'
|
20
|
+
s.add_development_dependency "activerecord", '~> 2.3.14'
|
21
|
+
s.add_development_dependency "mongoid", '~> 1.9.5'
|
22
|
+
s.add_development_dependency 'rspec', "~> 1.3"
|
23
|
+
s.add_development_dependency 'sqlite3'
|
24
|
+
s.add_development_dependency 'rake'
|
25
|
+
|
26
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'sqlite3'
|
3
|
+
|
4
|
+
ActiveRecord::Base.logger = Logger.new(STDOUT)
|
5
|
+
|
6
|
+
ActiveRecord::Base.establish_connection(
|
7
|
+
:adapter => "sqlite3",
|
8
|
+
:database => (File.dirname(__FILE__) + "/db/test.db")
|
9
|
+
)
|
10
|
+
|
11
|
+
require (File.dirname(__FILE__) + "/db/schema.rb")
|
data/spec/db/schema.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
ActiveRecord::Schema.define(:version => 0) do
|
2
|
+
|
3
|
+
create_table :say_when_jobs, :force => true do |t|
|
4
|
+
|
5
|
+
t.string :status
|
6
|
+
|
7
|
+
t.string :trigger_strategy
|
8
|
+
t.text :trigger_options
|
9
|
+
t.string :time_zone
|
10
|
+
|
11
|
+
t.timestamp :last_fire_at
|
12
|
+
t.timestamp :next_fire_at
|
13
|
+
|
14
|
+
t.timestamp :start_at
|
15
|
+
t.timestamp :end_at
|
16
|
+
|
17
|
+
t.string :job_class
|
18
|
+
t.string :job_method
|
19
|
+
t.text :data
|
20
|
+
|
21
|
+
t.timestamps
|
22
|
+
end
|
23
|
+
|
24
|
+
create_table :say_when_job_executions, :force => true do |t|
|
25
|
+
t.integer :job_id
|
26
|
+
t.string :status
|
27
|
+
t.text :result
|
28
|
+
t.datetime :start_at
|
29
|
+
t.datetime :end_at
|
30
|
+
end
|
31
|
+
|
32
|
+
add_index :say_when_jobs, :status
|
33
|
+
add_index :say_when_jobs, :next_fire_at
|
34
|
+
|
35
|
+
|
36
|
+
end
|
data/spec/db/test.db
ADDED
Binary file
|