naf 2.0.4 → 2.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.
Files changed (61) hide show
  1. data/README.rdoc +28 -4
  2. data/RELEASE_NOTES.rdoc +8 -0
  3. data/app/assets/javascripts/dataTablesTemplates/application_schedules.js +72 -0
  4. data/app/assets/javascripts/dataTablesTemplates/applications.js +12 -21
  5. data/app/controllers/naf/application_schedule_affinity_tabs_controller.rb +5 -8
  6. data/app/controllers/naf/application_schedules_controller.rb +98 -0
  7. data/app/controllers/naf/applications_controller.rb +6 -98
  8. data/app/controllers/naf/historical_jobs_controller.rb +18 -8
  9. data/app/helpers/naf/application_helper.rb +47 -22
  10. data/app/models/logical/naf/application.rb +9 -126
  11. data/app/models/logical/naf/application_schedule.rb +186 -0
  12. data/app/models/logical/naf/construction_zone/boss.rb +4 -5
  13. data/app/models/logical/naf/construction_zone/foreman.rb +34 -13
  14. data/app/models/logical/naf/construction_zone/proletariat.rb +1 -0
  15. data/app/models/logical/naf/construction_zone/work_order.rb +11 -4
  16. data/app/models/logical/naf/job_fetcher.rb +1 -0
  17. data/app/models/naf/application.rb +5 -16
  18. data/app/models/naf/application_schedule.rb +117 -43
  19. data/app/models/naf/historical_job.rb +6 -3
  20. data/app/models/naf/queued_job.rb +3 -0
  21. data/app/models/naf/run_interval_style.rb +20 -0
  22. data/app/models/naf/running_job.rb +3 -0
  23. data/app/models/process/naf/data_migration/backfill_application_schedule_run_interval.rb +85 -0
  24. data/app/models/process/naf/log_archiver.rb +1 -1
  25. data/app/models/process/naf/log_reader.rb +141 -0
  26. data/app/models/process/naf/runner.rb +6 -49
  27. data/app/views/naf/application_schedule_affinity_tabs/_form.html.erb +1 -1
  28. data/app/views/naf/{applications → application_schedules}/_application_schedule_prerequisites.html.erb +1 -1
  29. data/app/views/naf/application_schedules/_form.html.erb +120 -0
  30. data/app/views/naf/application_schedules/edit.html.erb +11 -0
  31. data/app/views/naf/application_schedules/index.html.erb +47 -0
  32. data/app/views/naf/application_schedules/index.json.erb +8 -0
  33. data/app/views/naf/application_schedules/new.html.erb +11 -0
  34. data/app/views/naf/application_schedules/show.html.erb +130 -0
  35. data/app/views/naf/applications/_form.html.erb +44 -106
  36. data/app/views/naf/applications/_search_container.html.erb +29 -29
  37. data/app/views/naf/applications/index.html.erb +1 -10
  38. data/app/views/naf/applications/index.json.erb +22 -6
  39. data/app/views/naf/applications/show.html.erb +47 -97
  40. data/app/views/naf/logger_styles/_form.html.erb +0 -3
  41. data/app/views/naf/machines/_form.html.erb +3 -4
  42. data/config/routes.rb +3 -4
  43. data/db/migrate/20131219195439_add_run_interval_styles_table.rb +49 -0
  44. data/db/migrate/20140113183243_drop_run_start_minute_from_application_schedules.rb +18 -0
  45. data/lib/naf/version.rb +1 -1
  46. data/naf.gemspec +1 -1
  47. data/spec/controllers/naf/application_schedule_affinity_tabs_controller_spec.rb +34 -27
  48. data/spec/controllers/naf/applications_controller_spec.rb +0 -48
  49. data/spec/factories/naf.rb +14 -8
  50. data/spec/models/logical/naf/application_spec.rb +9 -37
  51. data/spec/models/logical/naf/machine_spec.rb +1 -1
  52. data/spec/models/naf/application_schedule_spec.rb +38 -50
  53. data/spec/models/naf/application_spec.rb +3 -3
  54. data/spec/models/naf/historical_job_spec.rb +4 -2
  55. data/spec/models/naf/queued_job_spec.rb +2 -0
  56. data/spec/models/naf/run_interval_style_spec.rb +28 -0
  57. data/spec/models/naf/running_job_spec.rb +2 -0
  58. metadata +19 -7
  59. data/app/models/logical/naf/job_creator.rb +0 -151
  60. data/app/views/naf/applications/_application_schedule.html.erb +0 -80
  61. data/spec/models/logical/naf/job_creator_spec.rb +0 -102
@@ -2,6 +2,7 @@ module Naf
2
2
  class QueuedJob < NafBase
3
3
  # Protect from mass-assignment issue
4
4
  attr_accessible :application_id,
5
+ :application_schedule_id,
5
6
  :application_type_id,
6
7
  :command,
7
8
  :application_run_group_restriction_id,
@@ -19,6 +20,8 @@ module Naf
19
20
  foreign_key: :id
20
21
  belongs_to :application,
21
22
  class_name: "::Naf::Application"
23
+ belongs_to :application_schedule,
24
+ class_name: '::Naf::ApplicationSchedule'
22
25
  belongs_to :application_type,
23
26
  class_name: '::Naf::ApplicationType'
24
27
  belongs_to :application_run_group_restriction,
@@ -0,0 +1,20 @@
1
+ module Naf
2
+ class RunIntervalStyle < NafBase
3
+ # Protect from mass-assignment issue
4
+ attr_accessible :name
5
+
6
+ #---------------------
7
+ # *** Associations ***
8
+ #+++++++++++++++++++++
9
+
10
+ has_many :application_schedules,
11
+ class_name: '::Naf::ApplicationSchedule'
12
+
13
+ #--------------------
14
+ # *** Validations ***
15
+ #++++++++++++++++++++
16
+
17
+ validates :name, presence: true
18
+
19
+ end
20
+ end
@@ -2,6 +2,7 @@ module Naf
2
2
  class RunningJob < NafBase
3
3
  # Protect from mass-assignment issue
4
4
  attr_accessible :application_id,
5
+ :application_schedule_id,
5
6
  :application_type_id,
6
7
  :command,
7
8
  :application_run_group_restriction_id,
@@ -24,6 +25,8 @@ module Naf
24
25
  foreign_key: :id
25
26
  belongs_to :application,
26
27
  class_name: "::Naf::Application"
28
+ belongs_to :application_schedule,
29
+ class_name: '::Naf::ApplicationSchedule'
27
30
  belongs_to :application_type,
28
31
  class_name: '::Naf::ApplicationType'
29
32
  belongs_to :application_run_group_restriction,
@@ -0,0 +1,85 @@
1
+ module Process::Naf::DataMigration
2
+ class BackfillApplicationScheduleRunInterval < ::Process::Naf::Application
3
+
4
+ opt :update, 'update run interval styles id'
5
+ opt :rollback, 'rollback changes'
6
+ opt_select :update_or_rollback, one_of: [:update, :rollback]
7
+
8
+ attr_reader :run_interval_styles
9
+
10
+ def work
11
+ logger.info 'Starting to update application schedules...'
12
+
13
+ if @update.present?
14
+ setup
15
+ update_run_interval_styles_ids
16
+ logger.info 'Finished updating application schedules'
17
+ elsif @rollback.present?
18
+ if validate_applications
19
+ rollback_changes
20
+ logger.info 'Finished updating application schedules'
21
+ end
22
+ end
23
+ end
24
+
25
+ private
26
+
27
+ def setup
28
+ @run_interval_styles = {}
29
+ ::Naf::RunIntervalStyle.all.each do |ris|
30
+ @run_interval_styles[ris.name] = ris.id
31
+ end
32
+ end
33
+
34
+ def update_run_interval_styles_ids
35
+ ::Naf::ApplicationSchedule.all.each do |schedule|
36
+ logger.info "Updating application schedule: #{schedule.id}"
37
+
38
+ if schedule.run_start_minute.present?
39
+ schedule.run_interval = schedule.run_start_minute
40
+ schedule.run_interval_style_id = run_interval_styles['at beginning of day']
41
+ elsif schedule.run_interval.present?
42
+ schedule.run_interval_style_id = run_interval_styles['after previous run']
43
+ else
44
+ schedule.run_interval = 0
45
+ schedule.run_interval_style_id = run_interval_styles['at beginning of day']
46
+ end
47
+
48
+ schedule.save!
49
+ end
50
+ end
51
+
52
+ def validate_applications
53
+ applications = []
54
+ ::Naf::Application.all.each do |application|
55
+ if application.application_schedules.count > 1
56
+ applications << application.title
57
+ end
58
+ end
59
+
60
+ if applications.present?
61
+ logger.warn "The following applications have more than one schedule: #{applications.join(', ')}. " +
62
+ "In order to rollback successfully, an application can have at most one schedule."
63
+ return false
64
+ else
65
+ return true
66
+ end
67
+ end
68
+
69
+ def rollback_changes
70
+ ::Naf::ApplicationSchedule.all.each do |schedule|
71
+ logger.info "Updating application schedule: #{schedule.id}"
72
+
73
+ if schedule.run_interval_style.name == 'at beginning of day'
74
+ schedule.run_start_minute = schedule.run_interval
75
+ schedule.run_interval = nil
76
+ else
77
+ schedule.run_start_minute = nil
78
+ end
79
+
80
+ schedule.save!
81
+ end
82
+ end
83
+
84
+ end
85
+ end
@@ -4,7 +4,7 @@ module Process::Naf
4
4
  class LogArchiver < ::Process::Naf::Application
5
5
 
6
6
  NAF_JOBS_LOG_PATH = "#{::Naf::PREFIX_PATH}/#{::Naf.schema_name}/jobs/"
7
- NAF_RUNNERS_LOG_PATH = "#{::Naf::PREFIX_PATH}/#{::Naf.schema_name}/runners/*/*"
7
+ NAF_RUNNERS_LOG_PATH = "#{::Naf::PREFIX_PATH}/#{::Naf.schema_name}/runners/"
8
8
  DATE_REGEX = /\d{8}_\d{6}/
9
9
  LOG_RETENTION = 1
10
10
 
@@ -0,0 +1,141 @@
1
+ require 'yajl'
2
+
3
+ module Process::Naf
4
+ class LogReader < ::Process::Naf::Application
5
+
6
+ opt :job_id, type: :int
7
+ opt :runner_id, type: :int
8
+ opt_select :job_id_or_runner_id, one_of: [:job_id, :runner_id]
9
+
10
+ UUID_REGEX = /[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}/
11
+
12
+ attr_accessor :read_from_s3,
13
+ :s3_log_reader,
14
+ :jsons
15
+
16
+ def work
17
+ @jsons = []
18
+ if @job_id.present?
19
+ job_log_files.each do |file|
20
+ # Use Yajl JSON library to parse the log files, as they contain multiple JSON blocks
21
+ parser = Yajl::Parser.new
22
+ json = get_json_from_log_file(file)
23
+ parser.parse(json) do |log|
24
+ @jsons << log
25
+ end
26
+
27
+ sort_jsons
28
+ end
29
+
30
+ if jsons.present?
31
+ jsons.each do |elem|
32
+ puts insert_log_line(elem)
33
+ end
34
+ else
35
+ puts 'No logs found'
36
+ end
37
+ elsif @runner_id.present?
38
+ runner_log_files.each do |file|
39
+ # Use Yajl JSON library to parse the log files, as they contain multiple JSON blocks
40
+ parser = Yajl::Parser.new
41
+ json = get_json_from_log_file(file)
42
+ parser.parse(json) do |log|
43
+ log['id'] = get_invocation_id(file.scan(UUID_REGEX).first)
44
+ @jsons << log
45
+ end
46
+
47
+ sort_jsons
48
+ end
49
+
50
+ if jsons.present?
51
+ jsons.each do |elem|
52
+ puts insert_log_line(elem)
53
+ end
54
+ else
55
+ puts 'No logs found'
56
+ end
57
+ end
58
+ end
59
+
60
+ private
61
+
62
+ def job_log_files
63
+ files = Dir["#{::Naf::PREFIX_PATH}/#{::Naf.schema_name}/jobs/#{@job_id}/*"]
64
+ if files.present?
65
+ @read_from_s3 = false
66
+ # Sort log files based on time
67
+ return files.sort { |x, y| Time.parse(y.scan(::Logical::Naf::LogParser::Base::DATE_REGEX)[0][0]) <=> Time.parse(x.scan(::Logical::Naf::LogParser::Base::DATE_REGEX)[0][0]) }
68
+ else
69
+ get_s3_files do
70
+ @read_from_s3 = true
71
+ @s3_log_reader = ::Logical::Naf::LogReader.new
72
+ return s3_log_reader.retrieve_job_files(@job_id)
73
+ end
74
+ end
75
+ end
76
+
77
+ def runner_log_files
78
+ invocation_uuids = ::Naf::MachineRunnerInvocation.where(machine_runner_id: @runner_id).map(&:uuid)
79
+ files = []
80
+ invocation_uuids.each do |uuid|
81
+ if Dir["#{::Naf::PREFIX_PATH}/#{::Naf.schema_name}/runners/#{uuid}/*"].present?
82
+ files += Dir["#{::Naf::PREFIX_PATH}/#{::Naf.schema_name}/runners/#{uuid}/*"]
83
+ end
84
+ end
85
+
86
+ if files.present?
87
+ @read_from_s3 = false
88
+ # Sort log files based on time
89
+ return files.sort { |x, y| Time.parse(y.scan(::Logical::Naf::LogParser::Base::DATE_REGEX)[0][0]) <=> Time.parse(x.scan(::Logical::Naf::LogParser::Base::DATE_REGEX)[0][0]) }
90
+ else
91
+ get_s3_files do
92
+ @read_from_s3 = true
93
+ @s3_log_reader = ::Logical::Naf::LogReader.new
94
+ s3_log_reader.runner_log_files.each do |file|
95
+ if invocation_uuids.include?(file.scan(UUID_REGEX).first)
96
+ files << file
97
+ end
98
+ end
99
+
100
+ return files
101
+ end
102
+ end
103
+ end
104
+
105
+ def get_s3_files
106
+ begin
107
+ yield
108
+ rescue
109
+ logger.info 'AWS S3 Access Denied. Please check your permissions.'
110
+ return []
111
+ end
112
+ end
113
+
114
+ def get_json_from_log_file(file)
115
+ if read_from_s3 == true
116
+ @s3_log_reader.retrieve_file(file)
117
+ else
118
+ File.new(file, 'r')
119
+ end
120
+ end
121
+
122
+ def sort_jsons
123
+ # Sort log lines based on timestamp
124
+ @jsons = jsons.sort { |x, y| x['line_number'] <=> y['line_number'] }
125
+ end
126
+
127
+ def insert_log_line(elem)
128
+ if @job_id
129
+ "#{elem['line_number']} #{elem['output_time']}: #{elem['message']}"
130
+ elsif @runner_id
131
+ "#{elem['output_time']} invocation(#{elem['id']}): #{elem['message']}"
132
+ end
133
+ end
134
+
135
+ def get_invocation_id(uuid)
136
+ ::Naf::MachineRunnerInvocation.find_by_uuid(uuid).id
137
+ end
138
+
139
+
140
+ end
141
+ end
@@ -18,9 +18,6 @@ module Process::Naf
18
18
  "time between checking schedules",
19
19
  argument_note: "MINUTES",
20
20
  default: 1
21
- opt :schedule_fudge_scale,
22
- "amount of time to look back in schedule for run_start_minute schedules (scaled to --check-schedule-period)",
23
- default: 5
24
21
  opt :runner_stale_period,
25
22
  "amount of time to consider a machine out of touch if it hasn't updated its machine entry",
26
23
  argument_note: "MINUTES",
@@ -253,12 +250,12 @@ module Process::Naf
253
250
  ::Naf::ApplicationSchedule.unlock_schedules
254
251
 
255
252
  # check scheduled tasks
256
- should_be_queued.each do |application_schedule|
253
+ ::Naf::ApplicationSchedule.should_be_queued.each do |application_schedule|
257
254
  logger.info escape_html("scheduled application: #{application_schedule}")
258
255
  begin
259
256
  naf_boss = ::Logical::Naf::ConstructionZone::Boss.new
260
257
  # this doesn't work very well for run_group_limits in the thousands
261
- Range.new(0, application_schedule.application_run_group_limit || 1, true).each do
258
+ Range.new(0, application_schedule.application_run_group_quantum || 1, true).each do
262
259
  naf_boss.enqueue_application_schedule(application_schedule)
263
260
  end
264
261
  rescue ::Naf::HistoricalJob::JobPrerequisiteLoop => jpl
@@ -608,48 +605,6 @@ module Process::Naf
608
605
  end
609
606
  end
610
607
 
611
- def should_be_queued
612
- not_finished_applications = ::Naf::HistoricalJob.
613
- queued_between(Time.zone.now - Naf::HistoricalJob::JOB_STALE_TIME, Time.zone.now).
614
- where("finished_at IS NULL AND request_to_terminate = false").
615
- find_all{ |job| job.application_id.present? }.
616
- index_by{ |job| job.application_id }
617
-
618
- application_last_runs = ::Naf::HistoricalJob.application_last_runs.
619
- index_by{ |job| job.application_id }
620
-
621
- # find the run_interval based schedules that should be queued
622
- # select anything that isn't currently running and completed
623
- # running more than run_interval minutes ago
624
- relative_schedules_what_need_queuin = ::Naf::ApplicationSchedule.where(enabled: true).relative_schedules.select do |schedule|
625
- (not_finished_applications[schedule.application_id].nil? &&
626
- (application_last_runs[schedule.application_id].nil? ||
627
- (Time.zone.now - application_last_runs[schedule.application_id].finished_at) > (schedule.run_interval.minutes)))
628
- end
629
-
630
- # find the run_start_minute based schedules
631
- # select anything that
632
- # isn't currently running (or queued) AND
633
- # hasn't run since run_start_time AND
634
- # should have been run by now AND
635
- # that should have run within fudge period AND
636
- exact_schedules_what_need_queuin = ::Naf::ApplicationSchedule.where(enabled: true).exact_schedules.select do |schedule|
637
- (not_finished_applications[schedule.application_id].nil? &&
638
- (application_last_runs[schedule.application_id].nil? ||
639
- ((Time.zone.now.to_date + schedule.run_start_minute.minutes) >= application_last_runs[schedule.application_id].finished_at)) &&
640
- (Time.zone.now - (Time.zone.now.to_date + schedule.run_start_minute.minutes)) >= 0.seconds &&
641
- ((Time.zone.now - (Time.zone.now.to_date + schedule.run_start_minute.minutes)) <= (@check_schedules_period * @schedule_fudge_scale).minutes)
642
- )
643
- end
644
-
645
- foreman = ::Logical::Naf::ConstructionZone::Foreman.new()
646
- return (relative_schedules_what_need_queuin + exact_schedules_what_need_queuin).select do |schedule|
647
- schedule.enqueue_backlogs || !foreman.limited_by_run_group?(schedule.application_run_group_restriction,
648
- schedule.application_run_group_name,
649
- schedule.application_run_group_limit)
650
- end
651
- end
652
-
653
608
  def memory_available_to_spawn?
654
609
  Facter.clear
655
610
  memory_size = Facter.memorysize_mb.to_f
@@ -657,10 +612,12 @@ module Process::Naf
657
612
  memory_free_percentage = (memory_free / memory_size) * 100.0
658
613
 
659
614
  if (memory_free_percentage >= @minimum_memory_free)
660
- logger.detail "memory available: #{memory_free_percentage}% (free) >= #{@minimum_memory_free}% (min percent)"
615
+ logger.detail "memory available: #{memory_free_percentage}% (free) >= " +
616
+ "#{@minimum_memory_free}% (min percent)"
661
617
  return true
662
618
  end
663
- logger.alarm "#{Facter.hostname}.#{Facter.domain}: not enough memory to spawn: #{memory_free_percentage}% (free) < #{@minimum_memory_free}% (min percent)"
619
+ logger.alarm "#{Facter.hostname}.#{Facter.domain}: not enough memory to spawn: " +
620
+ "#{memory_free_percentage}% (free) < #{@minimum_memory_free}% (min percent)"
664
621
 
665
622
  return false
666
623
  end
@@ -1,4 +1,4 @@
1
- <%= form_for([@application, @application_schedule, @tab]) do |f| %>
1
+ <%= form_for([@application_schedule, @tab]) do |f| %>
2
2
 
3
3
  <% if @tab.errors.any? %>
4
4
  <div id="errorExplanation">
@@ -7,7 +7,7 @@
7
7
  <div class="field">
8
8
  <%= f.label :prerequisite_application_schedule_id, "Prerequisite application schedule*" %>
9
9
  <br/>
10
- <% schedules = ::Naf::ApplicationSchedule.all.map { |a| [a.title, a.id] unless a.id == @application.application_schedule.id }.compact %>
10
+ <% schedules = ::Naf::ApplicationSchedule.all.map { |a| [a.to_s, a.id] unless @application_schedule.id == a.id }.compact %>
11
11
  <%= f.select(:prerequisite_application_schedule_id, schedules, include_blank: true) %>
12
12
  </div>
13
13
  <br/>
@@ -0,0 +1,120 @@
1
+ <%= form_for @application_schedule do |f| %>
2
+ <% if @application_schedule.errors.any? %>
3
+ <div id='errorExplanation'>
4
+ <h2><%= pluralize(@application_schedule.errors.count, 'error') %> prohibited this application schedule from being saved:</h2>
5
+ </div>
6
+ <% end %>
7
+
8
+ <%= f.hidden_field :application_id, value: @application_schedule.application_id %>
9
+
10
+ <div class="field">
11
+ <%= f.label :application_run_group_restriction_id, "Application run group restriction*" %>
12
+ <%= f.select(:application_run_group_restriction_id,
13
+ ::Naf::ApplicationRunGroupRestriction.all.map { |a| [a.application_run_group_restriction_name, a.id] },
14
+ include_blank: true) %>
15
+ </div>
16
+ </br/>
17
+
18
+ <div class="field">
19
+ <%= f.label :application_run_group_name %>
20
+ <%= select_tag(:run_group_name_type, options_for_select([["custom", "custom"],
21
+ ["command", "command"],
22
+ ["not set", "not set"]],
23
+ @run_group_name_type)) %><br/>
24
+ <%= f.text_area :application_run_group_name, rows: 4, cols: 32, placeholder: "Please input Application Run Group Name" %>
25
+ </div>
26
+ </br/>
27
+
28
+ <div class="field">
29
+ <%= f.label :run_interval, "Run Interval (minutes)" %>
30
+ <%= f.text_field :run_interval %>
31
+ </div>
32
+ </br/>
33
+
34
+ <div class="field">
35
+ <%= f.label :run_interval_style_id, "Run Interval Style*" %>
36
+ <%= f.select(:run_interval_style_id,
37
+ ::Naf::RunIntervalStyle.all.map { |s| [s.name, s.id] },
38
+ include_blank: true) %>
39
+ </div>
40
+ </br/>
41
+
42
+ <div class="field">
43
+ <%= f.label :priority %>
44
+ <%= f.text_field :priority %>
45
+ </div>
46
+ </br/>
47
+
48
+ <div class="field">
49
+ <%= f.label :application_run_group_limit %>
50
+ <%= f.text_field :application_run_group_limit %>
51
+ </div>
52
+ </br/>
53
+
54
+ <div class="field">
55
+ <%= f.label :application_run_group_quantum%>
56
+ <%= f.text_field :application_run_group_quantum %>
57
+ </div>
58
+ </br/>
59
+
60
+ <div class="field">
61
+ <%= f.label :enabled %>
62
+ <%= f.check_box :enabled %>
63
+ </div>
64
+ </br/>
65
+
66
+ <div class="field">
67
+ <%= f.label :visible %>
68
+ <%= f.check_box :visible %>
69
+ </div>
70
+ <br/>
71
+
72
+ <div class="field">
73
+ <%= f.label :enqueue_backlogs %>
74
+ <%= f.check_box :enqueue_backlogs %>
75
+ </div>
76
+ <br/>
77
+
78
+ <% if @show_prerequisite %>
79
+ <%= f.fields_for :application_schedule_prerequisites do |builder| %>
80
+ <%= render "application_schedule_prerequisites", f: builder %>
81
+ <% end %>
82
+ <% else %>
83
+ <% f.fields_for :application_schedule_prerequisites do |builder| %>
84
+ <%= render "application_schedule_prerequisites", f: builder %>
85
+ <% end %>
86
+ <% end %>
87
+
88
+ <%= naf_link_to_add_fields "Add Schedule Prerequisite", f, :application_schedule_prerequisites %>
89
+
90
+ <div class='actions'>
91
+ <br/>
92
+ <%= f.submit %>
93
+ </div>
94
+ <% end %>
95
+
96
+ <% content_for :javascripts do %>
97
+ <script type='text/javascript'>
98
+ jQuery(document).ready(function() {
99
+ var run_group = jQuery("#application_schedule_application_run_group_name");
100
+
101
+ if(jQuery("#run_group_name_type").val() != 'custom') {
102
+ run_group.hide();
103
+ run_group.val(null);
104
+ }
105
+
106
+ jQuery("#run_group_name_type").change(function () {
107
+ var type = jQuery("#run_group_name_type").val();
108
+ if(type == 'command') {
109
+ run_group.hide();
110
+ run_group.val(null);
111
+ } else if(type == 'not set') {
112
+ run_group.hide();
113
+ run_group.val(null);
114
+ } else {
115
+ run_group.slideToggle("slow");
116
+ }
117
+ });
118
+ });
119
+ </script>
120
+ <% end %>