osc_machete_rails 1.2.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 (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