taskr 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG.txt +0 -0
- data/GPLv3-LICENSE.txt +674 -0
- data/History.txt +0 -0
- data/Manifest.txt +25 -0
- data/README.txt +14 -0
- data/Rakefile +58 -0
- data/bin/taskr +35 -0
- data/bin/taskr-ctl +32 -0
- data/config.example.yml +100 -0
- data/examples/active_resource_client_example.rb +36 -0
- data/examples/php_client_example.php +85 -0
- data/lib/public/prototype.js +3271 -0
- data/lib/public/taskr.css +45 -0
- data/lib/taskr.rb +83 -0
- data/lib/taskr/actions.rb +230 -0
- data/lib/taskr/controllers.rb +164 -0
- data/lib/taskr/environment.rb +48 -0
- data/lib/taskr/helpers.rb +79 -0
- data/lib/taskr/models.rb +238 -0
- data/lib/taskr/version.rb +9 -0
- data/lib/taskr/views.rb +276 -0
- data/setup.rb +1585 -0
- data/test.rb +3 -0
- data/test/taskr_test.rb +11 -0
- data/test/test_helper.rb +2 -0
- metadata +102 -0
@@ -0,0 +1,48 @@
|
|
1
|
+
# This file is part of Taskr.
|
2
|
+
#
|
3
|
+
# Taskr is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# Taskr is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with Taskr. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
$: << File.dirname(File.expand_path(__FILE__))
|
17
|
+
|
18
|
+
# Try to load local versions of Picnic and Reststop if possible...
|
19
|
+
$: << File.dirname(File.expand_path(__FILE__))+"/../../../picnic/lib"
|
20
|
+
$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/picnic/lib"
|
21
|
+
$: << File.dirname(File.expand_path(__FILE__))+"/../../../reststop/lib"
|
22
|
+
$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/reststop/lib"
|
23
|
+
|
24
|
+
# active_resource needs newer versions of active_support, but this conflicts
|
25
|
+
# with active_record, so we need a newer version of that as well (yes, it's a mess)
|
26
|
+
#$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/activeresource/lib"
|
27
|
+
#$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/activesupport/lib"
|
28
|
+
#$: << File.dirname(File.expand_path(__FILE__))+"/../../vendor/activerecord/lib"
|
29
|
+
|
30
|
+
require 'rubygems'
|
31
|
+
|
32
|
+
require 'active_support'
|
33
|
+
#require 'active_resource'
|
34
|
+
require 'active_record'
|
35
|
+
|
36
|
+
|
37
|
+
# make things backwards-compatible for rubygems < 0.9.0
|
38
|
+
unless Object.method_defined? :gem
|
39
|
+
alias gem require_gem
|
40
|
+
end
|
41
|
+
|
42
|
+
require 'picnic.rb'
|
43
|
+
require 'camping/db'
|
44
|
+
|
45
|
+
require 'reststop'
|
46
|
+
|
47
|
+
gem 'openwferu-scheduler', '~> 0.9.16'
|
48
|
+
require 'openwfe/util/scheduler'
|
@@ -0,0 +1,79 @@
|
|
1
|
+
# This file is part of Taskr.
|
2
|
+
#
|
3
|
+
# Taskr is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# Taskr is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with Taskr. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
module Taskr::Helpers
|
17
|
+
def taskr_response_xml(result, &block)
|
18
|
+
instruct!
|
19
|
+
tag!("response", 'result' => result, 'xmlns:taskr' => "http://taskr.googlecode.com") do
|
20
|
+
yield
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def html_task_action_li(ta)
|
25
|
+
li ta.action_class_name
|
26
|
+
ul do
|
27
|
+
ta.action_parameters.each do |ap|
|
28
|
+
li do
|
29
|
+
label "#{ap.name}:"
|
30
|
+
pre ap.value
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def html_scaffold
|
37
|
+
html do
|
38
|
+
head do
|
39
|
+
title "Taskr"
|
40
|
+
link(:rel => 'stylesheet', :type => 'text/css', :href => '/public/taskr.css')
|
41
|
+
script(:type => 'text/javascript', :src => '/public/prototype.js')
|
42
|
+
end
|
43
|
+
body do
|
44
|
+
yield
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
# Taken from Rails
|
50
|
+
def distance_of_time_in_words(from_time, to_time = 0, include_seconds = false)
|
51
|
+
from_time = from_time.to_time if from_time.respond_to?(:to_time)
|
52
|
+
to_time = to_time.to_time if to_time.respond_to?(:to_time)
|
53
|
+
distance_in_minutes = (((to_time - from_time).abs)/60).round
|
54
|
+
distance_in_seconds = ((to_time - from_time).abs).round
|
55
|
+
|
56
|
+
case distance_in_minutes
|
57
|
+
when 0..1
|
58
|
+
return (distance_in_minutes == 0) ? 'less than a minute' : '1 minute' unless include_seconds
|
59
|
+
case distance_in_seconds
|
60
|
+
when 0..4 then 'less than 5 seconds'
|
61
|
+
when 5..9 then 'less than 10 seconds'
|
62
|
+
when 10..19 then 'less than 20 seconds'
|
63
|
+
when 20..39 then 'half a minute'
|
64
|
+
when 40..59 then 'less than a minute'
|
65
|
+
else '1 minute'
|
66
|
+
end
|
67
|
+
|
68
|
+
when 2..44 then "#{distance_in_minutes} minutes"
|
69
|
+
when 45..89 then 'about 1 hour'
|
70
|
+
when 90..1439 then "about #{(distance_in_minutes.to_f / 60.0).round} hours"
|
71
|
+
when 1440..2879 then '1 day'
|
72
|
+
when 2880..43199 then "#{(distance_in_minutes / 1440).round} days"
|
73
|
+
when 43200..86399 then 'about 1 month'
|
74
|
+
when 86400..525959 then "#{(distance_in_minutes / 43200).round} months"
|
75
|
+
when 525960..1051919 then 'about 1 year'
|
76
|
+
else "over #{(distance_in_minutes / 525960).round} years"
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
data/lib/taskr/models.rb
ADDED
@@ -0,0 +1,238 @@
|
|
1
|
+
# This file is part of Taskr.
|
2
|
+
#
|
3
|
+
# Taskr is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# Taskr is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with Taskr. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
require 'camping/db'
|
17
|
+
require 'openwfe/util/scheduler'
|
18
|
+
require 'date'
|
19
|
+
|
20
|
+
class OpenWFE::Scheduler
|
21
|
+
public :duration_to_f
|
22
|
+
end
|
23
|
+
|
24
|
+
|
25
|
+
module Taskr::Models
|
26
|
+
|
27
|
+
class Task < Base
|
28
|
+
has_many :task_actions,
|
29
|
+
:include => :action_parameters,
|
30
|
+
:dependent => :destroy
|
31
|
+
|
32
|
+
serialize :schedule_options
|
33
|
+
serialize :last_triggered_error
|
34
|
+
|
35
|
+
validates_presence_of :schedule_method
|
36
|
+
validates_presence_of :schedule_when
|
37
|
+
validates_presence_of :name
|
38
|
+
validates_uniqueness_of :name
|
39
|
+
validates_presence_of :task_actions
|
40
|
+
validates_associated :task_actions
|
41
|
+
|
42
|
+
def schedule!(scheduler)
|
43
|
+
case schedule_method
|
44
|
+
when 'cron'
|
45
|
+
method = :schedule
|
46
|
+
when 'at'
|
47
|
+
method = :schedule_at
|
48
|
+
when 'in'
|
49
|
+
method = :schedule_in
|
50
|
+
when 'every'
|
51
|
+
method = :schedule_every
|
52
|
+
end
|
53
|
+
|
54
|
+
if method == :schedule_at || method == :schedule_in
|
55
|
+
t = next_trigger_time
|
56
|
+
method = :schedule_at
|
57
|
+
if t < Time.now
|
58
|
+
$LOG.warn "Task #{name.inspect} will not be scheduled because its trigger time is in the past (#{t.inspect})."
|
59
|
+
return nil
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
$LOG.debug "Scheduling task #{name.inspect}: #{self.inspect}"
|
64
|
+
|
65
|
+
if task_actions.length == 1
|
66
|
+
ta = task_actions.first
|
67
|
+
|
68
|
+
parameters = {}
|
69
|
+
ta.action_parameters.each{|p| parameters[p.name] = p.value}
|
70
|
+
|
71
|
+
action = (ta.action_class.kind_of?(Class) ? ta.action_class : ta.action_class.constantize).new(parameters)
|
72
|
+
action.task = self
|
73
|
+
elsif task_actions.length > 1
|
74
|
+
action = Taskr::Actions::Multi.new
|
75
|
+
task_actions.each do |ta|
|
76
|
+
parameters = {}
|
77
|
+
ta.action_parameters.each{|p| parameters[p.name] = p.value}
|
78
|
+
|
79
|
+
a = (ta.action_class.kind_of?(Class) ? ta.action_class : ta.action_class.constantize).new(parameters)
|
80
|
+
a.task = self
|
81
|
+
|
82
|
+
action.actions << a
|
83
|
+
end
|
84
|
+
action.task = self
|
85
|
+
else
|
86
|
+
$LOG.warn "Task #{name.inspect} has no actions and as a result will not be scheduled!"
|
87
|
+
return false
|
88
|
+
end
|
89
|
+
|
90
|
+
job_id = scheduler.send(method, t || schedule_when, :schedulable => action)
|
91
|
+
|
92
|
+
if job_id
|
93
|
+
$LOG.debug "Task #{name.inspect} scheduled with job id #{job_id}"
|
94
|
+
else
|
95
|
+
$LOG.error "Task #{name.inspect} was NOT scheduled!"
|
96
|
+
return nil
|
97
|
+
end
|
98
|
+
|
99
|
+
self.update_attribute(:scheduler_job_id, job_id)
|
100
|
+
if method == :schedule_at || method == :schedule_in
|
101
|
+
job = scheduler.get_job(job_id)
|
102
|
+
at = job.schedule_info
|
103
|
+
self.update_attribute(:schedule_when, at)
|
104
|
+
self.update_attribute(:schedule_method, 'at')
|
105
|
+
end
|
106
|
+
|
107
|
+
return job_id
|
108
|
+
end
|
109
|
+
|
110
|
+
def next_trigger_time
|
111
|
+
# TODO: need to figure out how to calulate trigger_time for these.. for now return :unknown
|
112
|
+
return :unknown unless schedule_method == 'at' || schedule_method == 'in'
|
113
|
+
|
114
|
+
if schedule_method == 'in'
|
115
|
+
return (created_on || Time.now) + Taskr.scheduler.duration_to_f(schedule_when)
|
116
|
+
end
|
117
|
+
|
118
|
+
# Time parsing code from Rails
|
119
|
+
time_hash = Date._parse(schedule_when)
|
120
|
+
time_hash[:sec_fraction] = ((time_hash[:sec_fraction].to_f % 1) * 1_000_000).to_i
|
121
|
+
time_array = time_hash.values_at(:year, :mon, :mday, :hour, :min, :sec, :sec_fraction)
|
122
|
+
# treat 0000-00-00 00:00:00 as nil
|
123
|
+
Time.send(Base.default_timezone, *time_array) rescue DateTime.new(*time_array[0..5]) rescue nil
|
124
|
+
end
|
125
|
+
|
126
|
+
def to_s
|
127
|
+
"#<#{self.class}:#{self.id}>"
|
128
|
+
end
|
129
|
+
end
|
130
|
+
|
131
|
+
class TaskAction < Base
|
132
|
+
belongs_to :task
|
133
|
+
|
134
|
+
has_many :action_parameters,
|
135
|
+
:class_name => 'TaskActionParameter',
|
136
|
+
:foreign_key => :task_action_id,
|
137
|
+
:dependent => :destroy
|
138
|
+
|
139
|
+
validates_associated :action_parameters
|
140
|
+
|
141
|
+
def action_class=(class_name)
|
142
|
+
if class_name.kind_of? Class
|
143
|
+
self[:action_class_name] = class_name.to_s
|
144
|
+
else
|
145
|
+
self[:action_class_name] = class_name
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def action_class
|
150
|
+
self[:action_class_name].constantize
|
151
|
+
end
|
152
|
+
|
153
|
+
def to_xml(options = {})
|
154
|
+
options[:indent] ||= 2
|
155
|
+
xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
|
156
|
+
xml.instruct! unless options[:skip_instruct]
|
157
|
+
xml.tag!('task-action', :type => self.class) do
|
158
|
+
xml.tag!('id', {:type => 'integer'}, id)
|
159
|
+
xml.tag!('action-class-name', action_class_name)
|
160
|
+
xml.tag!('order', {:type => 'integer'}, order) unless order.blank?
|
161
|
+
xml.tag!('task-id', {:type => 'integer'}, task_id)
|
162
|
+
xml.tag!('action-parameters', {:type => 'array'}) do
|
163
|
+
action_parameters.each {|ap| ap.to_xml(options)}
|
164
|
+
end
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def to_s
|
169
|
+
"#<#{self.class}:#{self.id}>"
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
class TaskActionParameter < Base
|
174
|
+
belongs_to :task_action
|
175
|
+
serialize :value
|
176
|
+
|
177
|
+
def to_xml(options = {})
|
178
|
+
options[:indent] ||= 2
|
179
|
+
xml = options[:builder] ||= Builder::XmlMarkup.new(:indent => options[:indent])
|
180
|
+
xml.instruct! unless options[:skip_instruct]
|
181
|
+
xml.tag!('action-parameter', :type => self.class) do
|
182
|
+
xml.tag!('id', {:type => 'integer'}, id)
|
183
|
+
xml.tag!('name', name)
|
184
|
+
xml.tag!('value') do
|
185
|
+
xml.cdata!(value)
|
186
|
+
end
|
187
|
+
end
|
188
|
+
end
|
189
|
+
|
190
|
+
def to_s
|
191
|
+
"#<#{self.class}:#{self.id}>"
|
192
|
+
end
|
193
|
+
end
|
194
|
+
|
195
|
+
class CreateTaskr < V 0.01
|
196
|
+
def self.up
|
197
|
+
$LOG.info("Migrating database")
|
198
|
+
|
199
|
+
create_table :taskr_tasks, :force => true do |t|
|
200
|
+
t.column :name, :string, :null => false
|
201
|
+
t.column :created_on, :timestamp, :null => false
|
202
|
+
t.column :created_by, :string
|
203
|
+
|
204
|
+
t.column :schedule_method, :string, :null => false
|
205
|
+
t.column :schedule_when, :string, :null => false
|
206
|
+
t.column :schedule_options, :text
|
207
|
+
|
208
|
+
t.column :scheduler_job_id, :integer
|
209
|
+
t.column :last_triggered, :datetime
|
210
|
+
t.column :last_triggered_error, :text
|
211
|
+
end
|
212
|
+
|
213
|
+
add_index :taskr_tasks, [:name], :unique => true
|
214
|
+
|
215
|
+
create_table :taskr_task_actions, :force => true do |t|
|
216
|
+
t.column :task_id, :integer, :null => false
|
217
|
+
t.column :action_class_name, :string, :null => false
|
218
|
+
t.column :order, :integer
|
219
|
+
end
|
220
|
+
|
221
|
+
add_index :taskr_task_actions, [:task_id]
|
222
|
+
|
223
|
+
create_table :taskr_task_action_parameters, :force => true do |t|
|
224
|
+
t.column :task_action_id, :integer, :null => false
|
225
|
+
t.column :name, :string, :null => false
|
226
|
+
t.column :value, :text
|
227
|
+
end
|
228
|
+
|
229
|
+
add_index :taskr_task_action_parameters, [:task_action_id]
|
230
|
+
add_index :taskr_task_action_parameters, [:task_action_id, :name]
|
231
|
+
end
|
232
|
+
|
233
|
+
def self.down
|
234
|
+
drop_table :taskr_task_action_parameters
|
235
|
+
drop_table :taskr_tasks
|
236
|
+
end
|
237
|
+
end
|
238
|
+
end
|
data/lib/taskr/views.rb
ADDED
@@ -0,0 +1,276 @@
|
|
1
|
+
# This file is part of Taskr.
|
2
|
+
#
|
3
|
+
# Taskr is free software: you can redistribute it and/or modify
|
4
|
+
# it under the terms of the GNU General Public License as published by
|
5
|
+
# the Free Software Foundation, either version 3 of the License, or
|
6
|
+
# (at your option) any later version.
|
7
|
+
#
|
8
|
+
# Taskr is distributed in the hope that it will be useful,
|
9
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
10
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
11
|
+
# GNU General Public License for more details.
|
12
|
+
#
|
13
|
+
# You should have received a copy of the GNU General Public License
|
14
|
+
# along with Taskr. If not, see <http://www.gnu.org/licenses/>.
|
15
|
+
|
16
|
+
# need auto_validation off to render non-XHTML XML
|
17
|
+
Markaby::Builder.set(:auto_validation, false)
|
18
|
+
Markaby::Builder.set(:indent, 2)
|
19
|
+
|
20
|
+
module Taskr::Views
|
21
|
+
module XML
|
22
|
+
include Taskr::Models
|
23
|
+
|
24
|
+
CONTENT_TYPE = 'text/xml'
|
25
|
+
|
26
|
+
def tasks_list
|
27
|
+
@tasks.to_xml(:root => 'tasks', :include => [:task_actions])
|
28
|
+
end
|
29
|
+
|
30
|
+
def view_task
|
31
|
+
@task.to_xml(:root => 'task', :include => [:task_actions])
|
32
|
+
end
|
33
|
+
|
34
|
+
def create_task_result
|
35
|
+
taskr_response_xml(@task.valid? ? 'success' : 'failure') do
|
36
|
+
text @task.to_xml
|
37
|
+
text @task.errors.to_xml unless @task.valid?
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
module HTML
|
43
|
+
CONTENT_TYPE = 'text/html'
|
44
|
+
|
45
|
+
def tasks_list
|
46
|
+
html_scaffold do
|
47
|
+
h1 {"Tasks"}
|
48
|
+
|
49
|
+
p{a(:href => R(Taskr::Controllers::Tasks, 'new')) {"Schedule New Task"}}
|
50
|
+
|
51
|
+
table do
|
52
|
+
thead do
|
53
|
+
tr do
|
54
|
+
th "Name"
|
55
|
+
th "Schedule"
|
56
|
+
th "Last Triggered"
|
57
|
+
th "Job ID"
|
58
|
+
th "Created On"
|
59
|
+
th "Created By"
|
60
|
+
end
|
61
|
+
end
|
62
|
+
tbody do
|
63
|
+
@tasks.each do |t|
|
64
|
+
tr_css = []
|
65
|
+
tr_css << "error" if t.last_triggered_error
|
66
|
+
tr_css << "expired" if t.next_trigger_time != :unknown && t.next_trigger_time < Time.now
|
67
|
+
|
68
|
+
tr(:class => tr_css.join(" ")) do
|
69
|
+
td {a(:href => R(t)) {strong{t.name}}}
|
70
|
+
td "#{t.schedule_method} #{t.schedule_when}"
|
71
|
+
td do
|
72
|
+
if t.last_triggered
|
73
|
+
"#{distance_of_time_in_words(t.last_triggered, Time.now, true)} ago"
|
74
|
+
else
|
75
|
+
em "Not yet triggered"
|
76
|
+
end
|
77
|
+
end
|
78
|
+
td(:class => "job-id") {t.scheduler_job_id}
|
79
|
+
td t.created_on
|
80
|
+
td t.created_by
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
br
|
87
|
+
div {scheduler_status}
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def new_task
|
92
|
+
html_scaffold do
|
93
|
+
script(:type => 'text/javascript') do
|
94
|
+
%{
|
95
|
+
function show_action_parameters(num) {
|
96
|
+
new Ajax.Updater('parameters_'+num, '/actions', {
|
97
|
+
method: 'get',
|
98
|
+
parameters: {
|
99
|
+
id: $F('action_class_name_'+num),
|
100
|
+
action: 'parameters_form',
|
101
|
+
num: num
|
102
|
+
}
|
103
|
+
});
|
104
|
+
}
|
105
|
+
}
|
106
|
+
end
|
107
|
+
|
108
|
+
form :method => 'post', :action => self/"/tasks?format=#{@format}" do
|
109
|
+
h1 "New Task"
|
110
|
+
input :type => 'hidden', :name => '_method', :value => 'post'
|
111
|
+
|
112
|
+
p do
|
113
|
+
label 'name'
|
114
|
+
br
|
115
|
+
input :type => 'text', :name => 'name', :size => 40
|
116
|
+
end
|
117
|
+
|
118
|
+
p do
|
119
|
+
label 'schedule'
|
120
|
+
br
|
121
|
+
select(:name => 'schedule_method') do
|
122
|
+
['every','at','in','cron'].each do |method|
|
123
|
+
option(:value => method) {method}
|
124
|
+
end
|
125
|
+
end
|
126
|
+
input :type => 'text', :name => 'schedule_when', :size => 30
|
127
|
+
end
|
128
|
+
|
129
|
+
action_form
|
130
|
+
|
131
|
+
p do
|
132
|
+
a(:id => 'add_action', :href => '#'){'Add another action'}
|
133
|
+
end
|
134
|
+
script(:type => 'text/javascript') do
|
135
|
+
%{
|
136
|
+
Event.observe('add_action', 'click', function() {
|
137
|
+
new Ajax.Updater('add_action', '#{self/'/actions'}', {
|
138
|
+
method: 'get',
|
139
|
+
parameters: { action: 'new', num: $$('select.action_class_name').size() },
|
140
|
+
insertion: Insertion.Before
|
141
|
+
});
|
142
|
+
return false;
|
143
|
+
})
|
144
|
+
}
|
145
|
+
end
|
146
|
+
|
147
|
+
button(:type => 'submit') {"submit"}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
151
|
+
|
152
|
+
def view_task
|
153
|
+
html_scaffold do
|
154
|
+
form(:method => 'delete', :style => 'display: inline') do
|
155
|
+
button(:type => 'submit', :value => 'delete') {"Delete"}
|
156
|
+
end
|
157
|
+
br
|
158
|
+
a(:href => self/'tasks') {"Back to Task List"}
|
159
|
+
|
160
|
+
h1 "Task #{@task.id}"
|
161
|
+
table do
|
162
|
+
tr do
|
163
|
+
th "Name:"
|
164
|
+
td @task.name
|
165
|
+
end
|
166
|
+
tr do
|
167
|
+
th "Schedule:"
|
168
|
+
td "#{@task.schedule_method} #{@task.schedule_when}"
|
169
|
+
end
|
170
|
+
tr do
|
171
|
+
th "Triggered:"
|
172
|
+
td do
|
173
|
+
if @task.last_triggered
|
174
|
+
span "#{distance_of_time_in_words(@task.last_triggered, Time.now, true)} ago"
|
175
|
+
span(:style => 'font-size: 8pt; color: #bbb'){"(#{@task.last_triggered})"}
|
176
|
+
else
|
177
|
+
em "Not yet triggered"
|
178
|
+
end
|
179
|
+
end
|
180
|
+
end
|
181
|
+
if @task.last_triggered_error
|
182
|
+
th "Error:"
|
183
|
+
td(:style => 'color: #e00;') do
|
184
|
+
strong "#{@task.last_triggered_error[:type]}"
|
185
|
+
br
|
186
|
+
span @task.last_triggered_error[:message]
|
187
|
+
end
|
188
|
+
end
|
189
|
+
tr do
|
190
|
+
th "Actions:"
|
191
|
+
td do
|
192
|
+
if @task.task_actions.length > 1
|
193
|
+
ol(:style => 'padding-left: 20px') do
|
194
|
+
@task.task_actions.each do |ta|
|
195
|
+
html_task_action_li(ta)
|
196
|
+
end
|
197
|
+
end
|
198
|
+
else
|
199
|
+
html_task_action_li(@task.task_actions.first)
|
200
|
+
end
|
201
|
+
end
|
202
|
+
end
|
203
|
+
tr do
|
204
|
+
th "Created By:"
|
205
|
+
td @task.created_by
|
206
|
+
end
|
207
|
+
tr do
|
208
|
+
th "Created On:"
|
209
|
+
td @task.created_on
|
210
|
+
end
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
|
215
|
+
def scheduler_status
|
216
|
+
s = Taskr.scheduler
|
217
|
+
h3(:style => "margin-bottom: 8px;") {"Scheduler Status"}
|
218
|
+
strong "Running?"
|
219
|
+
span(:style => 'margin-right: 10px') {s.instance_variable_get(:@stopped) ? "NO" : "Yes"}
|
220
|
+
strong "Precision:"
|
221
|
+
span(:style => 'margin-right: 10px') {"#{s.instance_variable_get(:@precision)}s"}
|
222
|
+
strong "Pending Jobs:"
|
223
|
+
span(:style => 'margin-right: 10px') {s.instance_variable_get(:@pending_jobs).size}
|
224
|
+
strong "Thread Status:"
|
225
|
+
span(:style => 'margin-right: 10px') {s.instance_variable_get(:@scheduler_thread).status}
|
226
|
+
end
|
227
|
+
|
228
|
+
def action_list
|
229
|
+
h1 "Actions"
|
230
|
+
ul do
|
231
|
+
@actions.each do |a|
|
232
|
+
li a
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
|
237
|
+
def action_parameters_form
|
238
|
+
@num ||= 0
|
239
|
+
|
240
|
+
p {em @action.description}
|
241
|
+
|
242
|
+
@action.parameters.each do |param|
|
243
|
+
p do
|
244
|
+
label param
|
245
|
+
br
|
246
|
+
input :type => 'textarea', :name => "action[#{@num}][#{param}]", :size => 40
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
def action_form
|
252
|
+
@num ||= 0
|
253
|
+
|
254
|
+
p do
|
255
|
+
label 'action_class_name'
|
256
|
+
br
|
257
|
+
select(:name => "action[#{@num}][action_class_name]",
|
258
|
+
:id => "action_class_name_#{@num}",
|
259
|
+
:class => "action_class_name",
|
260
|
+
:onchange => "show_action_parameters(#{@num})") do
|
261
|
+
option(:value => "")
|
262
|
+
@actions.each do |a|
|
263
|
+
a.to_s =~ /Taskr::Actions::([^:]*?)$/
|
264
|
+
option(:value => $~[1]) {$~[1]}
|
265
|
+
end
|
266
|
+
end
|
267
|
+
end
|
268
|
+
|
269
|
+
div(:id => "parameters_#{@num}")
|
270
|
+
|
271
|
+
end
|
272
|
+
|
273
|
+
end
|
274
|
+
|
275
|
+
default_format :HTML
|
276
|
+
end
|