taskr 0.1.0
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.
- 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
|