osc_machete_rails 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (113) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +76 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +14 -0
  5. data/Rakefile +34 -0
  6. data/app/assets/javascripts/osc_machete_rails/application.js +13 -0
  7. data/app/assets/stylesheets/osc_machete_rails/application.css +13 -0
  8. data/app/controllers/osc_machete_rails/application_controller.rb +4 -0
  9. data/app/helpers/osc_machete_rails/application_helper.rb +4 -0
  10. data/app/views/layouts/osc_machete_rails/application.html.erb +14 -0
  11. data/config/routes.rb +2 -0
  12. data/lib/generators/active_record/job_model_generator.rb +13 -0
  13. data/lib/generators/active_record/orm_helpers.rb +6 -0
  14. data/lib/generators/active_record/templates/job_model.rb +17 -0
  15. data/lib/generators/active_record/templates/migration.rb +19 -0
  16. data/lib/generators/active_record/templates/module.rb +7 -0
  17. data/lib/generators/active_record/templates/workflow_model.rb +34 -0
  18. data/lib/generators/active_record/workflow_model_generator.rb +30 -0
  19. data/lib/generators/osc_machete_rails/USAGE +36 -0
  20. data/lib/generators/osc_machete_rails/erb/erb_generator.rb +5 -0
  21. data/lib/generators/osc_machete_rails/erb/templates/_form.html.erb +21 -0
  22. data/lib/generators/osc_machete_rails/erb/templates/edit.html.erb +5 -0
  23. data/lib/generators/osc_machete_rails/erb/templates/index.html.erb +41 -0
  24. data/lib/generators/osc_machete_rails/erb/templates/new.html.erb +5 -0
  25. data/lib/generators/osc_machete_rails/erb/templates/show.html.erb +13 -0
  26. data/lib/generators/osc_machete_rails/job_helpers.rb +10 -0
  27. data/lib/generators/osc_machete_rails/job_model/USAGE +28 -0
  28. data/lib/generators/osc_machete_rails/job_model/job_model_generator.rb +13 -0
  29. data/lib/generators/osc_machete_rails/resource_route/USAGE +3 -0
  30. data/lib/generators/osc_machete_rails/resource_route/resource_route_generator.rb +57 -0
  31. data/lib/generators/osc_machete_rails/scaffold_controller/USAGE +17 -0
  32. data/lib/generators/osc_machete_rails/scaffold_controller/scaffold_controller_generator.rb +24 -0
  33. data/lib/generators/osc_machete_rails/scaffold_controller/templates/controller.rb +120 -0
  34. data/lib/generators/osc_machete_rails/scaffold_generator.rb +35 -0
  35. data/lib/generators/osc_machete_rails/workflow_model/USAGE +38 -0
  36. data/lib/generators/osc_machete_rails/workflow_model/workflow_model_generator.rb +12 -0
  37. data/lib/generators/osc_machete_rails/workflow_template/USAGE +19 -0
  38. data/lib/generators/osc_machete_rails/workflow_template/templates/main.sh.mustache +18 -0
  39. data/lib/generators/osc_machete_rails/workflow_template/workflow_template_generator.rb +9 -0
  40. data/lib/osc_machete_rails/engine.rb +24 -0
  41. data/lib/osc_machete_rails/helper.rb +25 -0
  42. data/lib/osc_machete_rails/statusable.rb +199 -0
  43. data/lib/osc_machete_rails/version.rb +3 -0
  44. data/lib/osc_machete_rails/workflow.rb +249 -0
  45. data/lib/osc_machete_rails.rb +12 -0
  46. data/lib/tasks/osc_machete_rails_tasks.rake +4 -0
  47. data/test/dummy/README.rdoc +28 -0
  48. data/test/dummy/Rakefile +6 -0
  49. data/test/dummy/app/assets/javascripts/application.js +13 -0
  50. data/test/dummy/app/assets/javascripts/simulations.js +2 -0
  51. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  52. data/test/dummy/app/assets/stylesheets/simulations.css +4 -0
  53. data/test/dummy/app/controllers/application_controller.rb +5 -0
  54. data/test/dummy/app/controllers/simulations_controller.rb +103 -0
  55. data/test/dummy/app/helpers/application_helper.rb +2 -0
  56. data/test/dummy/app/helpers/simulations_helper.rb +2 -0
  57. data/test/dummy/app/models/simulation.rb +26 -0
  58. data/test/dummy/app/models/simulation_job.rb +15 -0
  59. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  60. data/test/dummy/app/views/simulations/_form.html.erb +15 -0
  61. data/test/dummy/app/views/simulations/edit.html.erb +5 -0
  62. data/test/dummy/app/views/simulations/index.html.erb +35 -0
  63. data/test/dummy/app/views/simulations/new.html.erb +5 -0
  64. data/test/dummy/app/views/simulations/show.html.erb +16 -0
  65. data/test/dummy/bin/bundle +3 -0
  66. data/test/dummy/bin/rails +4 -0
  67. data/test/dummy/bin/rake +4 -0
  68. data/test/dummy/config/application.rb +23 -0
  69. data/test/dummy/config/boot.rb +5 -0
  70. data/test/dummy/config/database.yml +25 -0
  71. data/test/dummy/config/environment.rb +5 -0
  72. data/test/dummy/config/environments/development.rb +29 -0
  73. data/test/dummy/config/environments/production.rb +80 -0
  74. data/test/dummy/config/environments/test.rb +36 -0
  75. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  76. data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  77. data/test/dummy/config/initializers/inflections.rb +16 -0
  78. data/test/dummy/config/initializers/mime_types.rb +5 -0
  79. data/test/dummy/config/initializers/secret_token.rb +12 -0
  80. data/test/dummy/config/initializers/session_store.rb +3 -0
  81. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  82. data/test/dummy/config/locales/en.yml +23 -0
  83. data/test/dummy/config/routes.rb +11 -0
  84. data/test/dummy/config.ru +4 -0
  85. data/test/dummy/db/development.sqlite3 +0 -0
  86. data/test/dummy/db/migrate/20151230193910_create_simulations.rb +11 -0
  87. data/test/dummy/db/migrate/20151230193911_create_simulation_jobs.rb +15 -0
  88. data/test/dummy/db/schema.rb +38 -0
  89. data/test/dummy/db/test.sqlite3 +0 -0
  90. data/test/dummy/jobs/simulation/main.sh.mustache +17 -0
  91. data/test/dummy/log/development.log +0 -0
  92. data/test/dummy/log/test.log +31198 -0
  93. data/test/dummy/public/404.html +58 -0
  94. data/test/dummy/public/422.html +58 -0
  95. data/test/dummy/public/500.html +57 -0
  96. data/test/dummy/public/favicon.ico +0 -0
  97. data/test/dummy/tmp/cache/assets/test/sprockets/0ff4cb4a6c217771a1336b307c51cf4e +0 -0
  98. data/test/dummy/tmp/cache/assets/test/sprockets/13fe41fee1fe35b49d145bcc06610705 +0 -0
  99. data/test/dummy/tmp/cache/assets/test/sprockets/2f5173deea6c795b8fdde723bb4b63af +0 -0
  100. data/test/dummy/tmp/cache/assets/test/sprockets/357970feca3ac29060c1e3861e2c0953 +0 -0
  101. data/test/dummy/tmp/cache/assets/test/sprockets/57261b7c75c19be33229517e39e47528 +0 -0
  102. data/test/dummy/tmp/cache/assets/test/sprockets/9e1e594361ba2561e5f6011db99503bb +0 -0
  103. data/test/dummy/tmp/cache/assets/test/sprockets/cffd775d018f68ce5dba1ee0d951a994 +0 -0
  104. data/test/dummy/tmp/cache/assets/test/sprockets/d771ace226fc8215a3572e0aa35bb0d6 +0 -0
  105. data/test/dummy/tmp/cache/assets/test/sprockets/dc292ec300d07016ebcc2de4806208c3 +0 -0
  106. data/test/dummy/tmp/cache/assets/test/sprockets/f7cbd26ba1d28d48de824f0e94586655 +0 -0
  107. data/test/integration/error_handling_test.rb +166 -0
  108. data/test/integration/statusable_update_status_test.rb +47 -0
  109. data/test/models/simulation_test.rb +4 -0
  110. data/test/osc_machete_rails_test.rb +7 -0
  111. data/test/test_helper.rb +19 -0
  112. data/test/unit/statusable_test.rb +124 -0
  113. metadata +320 -0
@@ -0,0 +1,120 @@
1
+ <% if namespaced? -%>
2
+ require_dependency "<%= namespaced_path %>/application_controller"
3
+
4
+ <% end -%>
5
+ <% module_namespacing do -%>
6
+ class <%= controller_class_name %>Controller < ApplicationController
7
+ before_action :set_<%= singular_table_name %>, only: [:show, :edit, :update, :destroy, :submit, :copy]
8
+
9
+ # GET <%= route_url %>
10
+ # GET <%= route_url %>.json
11
+ def index
12
+ @<%= plural_table_name %> = <%= "#{class_name}.preload(:#{job.plural_name})" %>
13
+ end
14
+
15
+ # GET <%= route_url %>/1
16
+ # GET <%= route_url %>/1.json
17
+ def show
18
+ end
19
+
20
+ # GET <%= route_url %>/new
21
+ def new
22
+ @<%= singular_table_name %> = <%= orm_class.build(class_name) %>
23
+ end
24
+
25
+ # GET <%= route_url %>/1/edit
26
+ def edit
27
+ end
28
+
29
+ # POST <%= route_url %>
30
+ # POST <%= route_url %>.json
31
+ def create
32
+ @<%= singular_table_name %> = <%= orm_class.build(class_name, "#{singular_table_name}_params") %>
33
+
34
+ respond_to do |format|
35
+ if @<%= orm_instance.save %>
36
+ format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully created.'" %> }
37
+ format.json { render :show, status: :created, location: @<%= singular_table_name %> }
38
+ else
39
+ format.html { render :new }
40
+ format.json { render json: @<%= orm_instance.errors %>, status: :unprocessable_entity }
41
+ end
42
+ end
43
+ end
44
+
45
+ # PATCH/PUT <%= route_url %>/1
46
+ # PATCH/PUT <%= route_url %>/1.json
47
+ def update
48
+ respond_to do |format|
49
+ if @<%= orm_instance.update("#{singular_table_name}_params") %>
50
+ format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully updated.'" %> }
51
+ format.json { render :show, status: :ok, location: @<%= singular_table_name %> }
52
+ else
53
+ format.html { render :edit }
54
+ format.json { render json: @<%= orm_instance.errors %>, status: :unprocessable_entity }
55
+ end
56
+ end
57
+ end
58
+
59
+ # DELETE <%= route_url %>/1
60
+ # DELETE <%= route_url %>/1.json
61
+ def destroy
62
+ respond_to do |format|
63
+ if @<%= orm_instance.destroy %>
64
+ format.html { redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully destroyed.'" %> }
65
+ format.json { head :no_content }
66
+ else
67
+ format.html { redirect_to <%= index_helper %>_url, alert: <%= "\"#{human_name} failed to be destroyed: \#{@#{orm_instance.errors}.to_a}\"" %> }
68
+ format.json { render json: @<%= orm_instance.errors %>, status: :internal_server_error }
69
+ end
70
+ end
71
+ end
72
+
73
+ # PUT <%= route_url %>/1/submit
74
+ # PUT <%= route_url %>/1/submit.json
75
+ def submit
76
+ respond_to do |format|
77
+ if @<%= singular_table_name %>.submitted?
78
+ format.html { redirect_to <%= index_helper %>_url, alert: <%= "'#{human_name} has already been submitted.'" %> }
79
+ format.json { head :no_content }
80
+ elsif @<%= singular_table_name %>.submit
81
+ format.html { redirect_to <%= index_helper %>_url, notice: <%= "'#{human_name} was successfully submitted.'" %> }
82
+ format.json { head :no_content }
83
+ else
84
+ format.html { redirect_to <%= index_helper %>_url, alert: <%= "\"#{human_name} failed to be submitted: \#{@#{orm_instance.errors}.to_a}\"" %> }
85
+ format.json { render json: @<%= orm_instance.errors %>, status: :internal_server_error }
86
+ end
87
+ end
88
+ end
89
+
90
+ # PUT <%= route_url %>/1/copy
91
+ def copy
92
+ @<%= singular_table_name %> = @<%= singular_table_name %>.copy
93
+
94
+ respond_to do |format|
95
+ if @<%= orm_instance.save %>
96
+ format.html { redirect_to @<%= singular_table_name %>, notice: <%= "'#{human_name} was successfully copied.'" %> }
97
+ format.json { render :show, status: :created, location: @<%= singular_table_name %> }
98
+ else
99
+ format.html { redirect_to <%= index_helper %>_url, alert: <%= "\"#{human_name} failed to be copied: \#{@#{orm_instance.errors}.to_a}\"" %> }
100
+ format.json { render json: @<%= orm_instance.errors %>, status: :unprocessable_entity }
101
+ end
102
+ end
103
+ end
104
+
105
+ private
106
+ # Use callbacks to share common setup or constraints between actions.
107
+ def set_<%= singular_table_name %>
108
+ @<%= singular_table_name %> = <%= orm_class.find("#{class_name}.preload(:#{job.plural_name})", "params[:id]") %>
109
+ end
110
+
111
+ # Only allow a trusted parameter "white list" through.
112
+ def <%= "#{singular_table_name}_params" %>
113
+ <%- if attributes_names.empty? -%>
114
+ params.fetch(:<%= singular_table_name %>, {})
115
+ <%- else -%>
116
+ params.require(:<%= singular_table_name %>).permit!
117
+ <%- end -%>
118
+ end
119
+ end
120
+ <% end -%>
@@ -0,0 +1,35 @@
1
+ require 'rails/generators/rails/scaffold/scaffold_generator'
2
+
3
+ class OscMacheteRails::ScaffoldGenerator < Rails::Generators::ScaffoldGenerator
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ attr_reader :orig_args
7
+
8
+ def initialize(args, *options)
9
+ @orig_args = args
10
+ super
11
+ end
12
+
13
+ # override ModelGenerator
14
+ remove_hook_for :orm
15
+
16
+ # hook for workflow model/migration
17
+ hook_for :workflow_model, type: :boolean
18
+
19
+ # hook for job model/migration
20
+ hook_for :job_model, type: :boolean do |model|
21
+ invoke model, %W[#{singular_table_name}_job #{singular_table_name}:references]
22
+ end
23
+
24
+ # override hook for adding config/routes
25
+ hook_for :resource_route, required: true
26
+
27
+ # hook for workflow batch script template
28
+ hook_for :workflow_template, type: :boolean
29
+
30
+ # override ScaffoldGenerator
31
+ hook_for :scaffold_controller, required: true
32
+
33
+ # remove scaffold_stylesheet
34
+ remove_hook_for :stylesheet_engine
35
+ end
@@ -0,0 +1,38 @@
1
+ Description:
2
+ Stubs out a new workflow model. Pass the model name, either CamelCased or
3
+ under_scored, and an optional list of attribute pairs as arguments.
4
+
5
+ Attribute pairs are field:type arguments specifying the
6
+ model's attributes. Timestamps are added by default, so you don't have to
7
+ specify them by hand as 'created_at:datetime updated_at:datetime'.
8
+
9
+ You don't have to think up every attribute up front, but it helps to
10
+ sketch out a few so you can start working with the model immediately.
11
+
12
+ This generator invokes your configured ORM and test framework, which
13
+ defaults to ActiveRecord and TestUnit.
14
+
15
+ Available field types:
16
+
17
+ For more information see:
18
+
19
+ `rails generate model --help`
20
+
21
+ Example:
22
+ `rails generate osc_machete_rails:workflow_model Thing`
23
+
24
+ For ActiveRecord and TestUnit it creates:
25
+ Model: app/models/thing.rb
26
+ Test: test/models/thing_test.rb
27
+ Fixtures: test/fixtures/things.yml
28
+ Migration: db/migrate/XXX_create_things.rb
29
+
30
+ Note:
31
+ It will default to using the Job model for its Machete jobs.
32
+
33
+ `rails generate osc_machete_rails:workflow_model Container
34
+ name:string pressure:decimal container_job:jobs`
35
+
36
+ Note:
37
+ This will do all of above as well as set the ContainerJob as its
38
+ Machete jobs.
@@ -0,0 +1,12 @@
1
+ class OscMacheteRails::WorkflowModelGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path("../templates", __FILE__)
3
+
4
+ def initialize(args, *options)
5
+ args |= %w(staged_dir:string)
6
+
7
+ super
8
+ end
9
+
10
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
11
+ hook_for :orm, required: true
12
+ end
@@ -0,0 +1,19 @@
1
+ Description:
2
+ Stubs out a new workflow template. Pass the template name, either
3
+ CamelCased or under_scored, and an optional list of attribute pairs as
4
+ arguments.
5
+
6
+ Attribute pairs are field:type arguments specifying the
7
+ model's attributes. Timestamps are added by default, so you don't have to
8
+ specify them by hand as 'created_at:datetime updated_at:datetime'.
9
+
10
+ You don't have to think up every attribute up front, but it helps to
11
+ sketch out a few so you can start working with the model immediately.
12
+
13
+ This generates a batch script in jobs/.
14
+
15
+ Example:
16
+ `rails generate osc_machete_rails:workflow_template Thing pressure:decimal`
17
+
18
+ Creates:
19
+ Template: jobs/thing/main.sh.mustache
@@ -0,0 +1,18 @@
1
+ #PBS -N <%= file_name %>
2
+ #PBS -l nodes=1:ppn=12
3
+ #PBS -l walltime=01:00:00
4
+ #PBS -q @oak-batch.osc.edu
5
+ #PBS -j oe
6
+ #PBS -S /bin/bash
7
+
8
+ echo "---Job started at:"
9
+ date
10
+
11
+ <% attributes.each do |attribute| -%>
12
+ echo "<%= attribute.name %> = {{{<%= attribute.name %>}}}"
13
+ <% end -%>
14
+
15
+ sleep 30
16
+
17
+ echo "---Job finished at:"
18
+ date
@@ -0,0 +1,9 @@
1
+ class OscMacheteRails::WorkflowTemplateGenerator < Rails::Generators::NamedBase
2
+ source_root File.expand_path('../templates', __FILE__)
3
+
4
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
5
+
6
+ def copy_batch_script
7
+ template "main.sh.mustache", "jobs/#{file_name}/main.sh.mustache"
8
+ end
9
+ end
@@ -0,0 +1,24 @@
1
+ module OscMacheteRails
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace OscMacheteRails
4
+
5
+ config.app_generators do |g|
6
+ g.workflow_model true
7
+ g.job_model true
8
+ g.workflow_template true
9
+ end
10
+
11
+ config.before_initialize do
12
+ OscMacheteRails.update_status_of_all_active_jobs_on_each_request = true
13
+ end
14
+
15
+ config.after_initialize do
16
+ if OscMacheteRails.update_status_of_all_active_jobs_on_each_request
17
+ # set before action on both Engine controllers and main App controllers
18
+ # to update the status of all the active jobs
19
+ ::ApplicationController.before_action -> { OscMacheteRails::Statusable.update_status_of_all_active_jobs }
20
+ ApplicationController.before_action -> { OscMacheteRails::Statusable.update_status_of_all_active_jobs }
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,25 @@
1
+ module OscMacheteRails
2
+ module Helper
3
+ def status_label(job, tag = :span)
4
+ job ||= OpenStruct.new status: OSC::Machete::Status.not_submitted
5
+ text = job.status.to_s
6
+
7
+ label_class = 'label-default'
8
+ if job.failed?
9
+ label_class = 'label-danger'
10
+ elsif job.passed?
11
+ label_class = 'label-success'
12
+ text = "Completed"
13
+ elsif job.active?
14
+ label_class = 'label-primary'
15
+ end
16
+
17
+ content_tag tag, class: %I(status-label label #{label_class}) do
18
+ text
19
+ end
20
+ end
21
+ alias_method :job_status_label, :status_label
22
+ end
23
+ end
24
+
25
+ ActionView::Base.send :include, OscMacheteRails::Helper
@@ -0,0 +1,199 @@
1
+ module OscMacheteRails
2
+ # Methods that deal with pbs batch job status management
3
+ # within a Rails ActiveRecord model
4
+ module Statusable
5
+
6
+
7
+ delegate :not_submitted?, :submitted?, :completed?, :passed?, :failed?, :active?, to: :status
8
+
9
+ def status=(s)
10
+ super(OSC::Machete::Status.new(s).char)
11
+ end
12
+
13
+ # getter returns a Status value from CHAR or a Status value
14
+ def status
15
+ OSC::Machete::Status.new(super)
16
+ end
17
+
18
+ # delete the batch job and update status
19
+ # may raise PBS::Error as it is unhandled here!
20
+ def stop(update: true)
21
+ return unless status.active?
22
+
23
+ job.delete
24
+ update(status: OSC::Machete::Status.failed) if update
25
+ end
26
+
27
+ def self.included(obj)
28
+ # track the classes that include this module
29
+ self.classes << Kernel.const_get(obj.name) unless obj.name.nil?
30
+
31
+ # add class methods
32
+ obj.send(:extend, ClassMethods)
33
+
34
+ begin
35
+ obj.class_eval do
36
+ # Store job object info in a JSON column and replace old column methods
37
+ if respond_to?(:table_exists?) && table_exists? && respond_to?(:column_names) && column_names.include?('job_cache')
38
+ store :job_cache, accessors: [ :script, :pbsid, :host ], coder: JSON
39
+ delegate :script_name, to: :job
40
+ define_method :job_path do
41
+ job.path
42
+ end
43
+ else
44
+ define_method(:job_cache) do
45
+ {
46
+ script: (job_path && script_name) ? Pathname.new(job_path).join(script_name) : nil,
47
+ pbsid: pbsid,
48
+ host: nil
49
+ }
50
+ end
51
+ end
52
+ end
53
+ rescue StandardError => e
54
+ msg = "\nIf you are precompiling assets, you can ignore this:\n\tError thrown: #{e.inspect}"
55
+ msg += "\n\tError thrown in OscMacheteRails::Statusable.included when trying to add job_cache to the class including Statusable: #{obj.name}."
56
+ msg += "\n\tjob_cache method was not added to class." unless obj.method_defined? :job_cache
57
+ msg += "\n\n"
58
+ STDERR.puts msg
59
+ end
60
+ end
61
+
62
+
63
+ def self.classes
64
+ @classes ||= []
65
+ end
66
+
67
+ # for each Statusable, call update_status! on active jobs
68
+ def self.update_status_of_all_active_jobs
69
+ # to eager load classes, set config.eager_load to true or execute Rails.application.eager_load!
70
+ if self.classes.empty?
71
+ Rails.logger.warn "Statusable.classes Array is empty. This should contain a list of all the classes that include Statusable. " \
72
+ "This could occur if the Rails app is not configured to eager load classes."
73
+ else
74
+ Rails.logger.debug "Updating active job statuses via Statusable.update_status_of_all_active_jobs."
75
+ end
76
+
77
+ self.classes.each do |klass|
78
+ klass.active.to_a.each(&:update_status!) if klass.respond_to?(:active)
79
+ end
80
+ end
81
+
82
+ # class methods to extend a model with
83
+ module ClassMethods
84
+ # scope to get all of the jobs that are in an active state
85
+ # or have a pbsid
86
+ def active
87
+ # FIXME: what about OR i.e. where
88
+ #
89
+ # status in active_values OR (pbsid != null and status == null)
90
+ #
91
+ # will need to use STRING for the sql instead of this.
92
+ where(status: OSC::Machete::Status.active_values.map(&:char))
93
+ end
94
+ end
95
+
96
+ # Setter that accepts an OSC::Machete::Job instance
97
+ #
98
+ # @param [Job] new_job The Job object to be assigned to the Statusable instance.
99
+ def job=(new_job)
100
+ if self.has_attribute?(:job_cache)
101
+ self.script = new_job.script_path.to_s
102
+ self.pbsid = new_job.pbsid
103
+ self.host = new_job.host if new_job.respond_to?(:host)
104
+ else
105
+ self.script_name = new_job.script_name
106
+ self.job_path = new_job.path.to_s
107
+ self.pbsid = new_job.pbsid
108
+ end
109
+
110
+ begin
111
+ self.status = new_job.status
112
+ rescue PBS::Error => e
113
+ # a safe default
114
+ self.status = OSC::Machete::Status.queued
115
+
116
+ # log the error
117
+ Rails.logger.error("After submitting the job with pbsid: #{pbsid}," \
118
+ " checking the status raised a PBS::Error: #{e.message}")
119
+ end
120
+ end
121
+
122
+ # Returns associated OSC::Machete::Job instance
123
+ def job
124
+ OSC::Machete::Job.new(job_cache.symbolize_keys)
125
+ end
126
+
127
+ # Build the results validation method name from script_name attr
128
+ # using ActiveSupport methods
129
+ #
130
+ # Call this using the Rails console to see what method you should implement
131
+ # to support results validation for that job.
132
+ #
133
+ # @return [String] A string representing a validation method name from script_name attr
134
+ # using ActiveSupport methods
135
+ def results_validation_method_name
136
+ File.basename(script_name, ".*").underscore.parameterize('_') + "_results_valid?"
137
+ end
138
+
139
+ # A hook that can be overidden with custom code
140
+ # also looks for default validation methods for existing
141
+ # WARNING: THIS USES ActiveSupport::Inflector methods underscore and parameterize
142
+ #
143
+ # @return [Boolean] true if the results script is present
144
+ def results_valid?
145
+ valid = true
146
+
147
+ if self.respond_to? :script_name && !script_name.nil?
148
+ if self.respond_to?(results_validation_method_name)
149
+ valid = self.send(results_validation_method_name)
150
+ end
151
+ end
152
+
153
+ valid
154
+ end
155
+
156
+ #FIXME: should have a unit test for this!
157
+ # job.update_status! will update and save object
158
+ # if submitted? and ! completed? and status changed from previous state
159
+ # force will cause status to update regardless of completion status,
160
+ # redoing the validations. This way, if you are fixing validation methods
161
+ # you can use the Rails console to update the status of a Workflow by doing this:
162
+ #
163
+ # Container.last.jobs.each {|j| j.update_status!(force: true) }
164
+ #
165
+ # Or for a single statusable such as job:
166
+ #
167
+ # job.update_status!(force: true)
168
+ #
169
+ # FIXME: should log whether a validation method was called or
170
+ # throw a warning that no validation method was found (the one that would have been called)
171
+ #
172
+ # @param [Boolean, nil] force Force the update. (Default: false)
173
+ def update_status!(force: false)
174
+ # this will make it easier to differentiate from current_status
175
+ cached_status = status
176
+
177
+ # by default only update if its an active job
178
+ if (cached_status.not_submitted? && pbsid) || cached_status.active? || force
179
+ # get the current status from the system
180
+ current_status = job.status
181
+
182
+ # if job is done, lets re-validate
183
+ if current_status.completed?
184
+ current_status = results_valid? ? OSC::Machete::Status.passed : OSC::Machete::Status.failed
185
+ end
186
+
187
+ if (current_status != cached_status) || force
188
+ self.status = current_status
189
+ self.save
190
+ end
191
+ end
192
+
193
+ rescue PBS::Error => e
194
+ # we log the error but we just don't update the status
195
+ Rails.logger.error("During update_status! call on job with pbsid #{pbsid} and id #{id}" \
196
+ " a PBS::Error was thrown: #{e.message}")
197
+ end
198
+ end
199
+ end
@@ -0,0 +1,3 @@
1
+ module OscMacheteRails
2
+ VERSION = "1.2.0"
3
+ end