tengine_job 0.6.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (133) hide show
  1. data/.document +5 -0
  2. data/.rspec +1 -0
  3. data/Gemfile +23 -0
  4. data/Gemfile.lock +109 -0
  5. data/README.rdoc +20 -0
  6. data/Rakefile +42 -0
  7. data/VERSION +1 -0
  8. data/examples/0004_retry_one_layer.rb +24 -0
  9. data/examples/0004_retry_one_layer.sh +38 -0
  10. data/examples/0005_retry_two_layer.rb +54 -0
  11. data/examples/0005_retry_two_layer.sh +80 -0
  12. data/examples/0006_retry_three_layer.rb +58 -0
  13. data/examples/0006_retry_three_layer.sh +74 -0
  14. data/examples/0007_simple_jobnet.rb +7 -0
  15. data/examples/0021_dynamic_env.rb +20 -0
  16. data/examples/VERSION +1 -0
  17. data/examples/tengine_job_test.sh +10 -0
  18. data/lib/tengine/job.rb +94 -0
  19. data/lib/tengine/job/category.rb +54 -0
  20. data/lib/tengine/job/connectable.rb +43 -0
  21. data/lib/tengine/job/drivers/job_control_driver.rb +82 -0
  22. data/lib/tengine/job/drivers/job_execution_driver.rb +30 -0
  23. data/lib/tengine/job/drivers/jobnet_control_driver.rb +117 -0
  24. data/lib/tengine/job/drivers/schedule_driver.rb +30 -0
  25. data/lib/tengine/job/dsl_binder.rb +12 -0
  26. data/lib/tengine/job/dsl_evaluator.rb +18 -0
  27. data/lib/tengine/job/dsl_loader.rb +180 -0
  28. data/lib/tengine/job/edge.rb +150 -0
  29. data/lib/tengine/job/element_selector_notation.rb +169 -0
  30. data/lib/tengine/job/end.rb +32 -0
  31. data/lib/tengine/job/executable.rb +74 -0
  32. data/lib/tengine/job/execution.rb +141 -0
  33. data/lib/tengine/job/expansion.rb +37 -0
  34. data/lib/tengine/job/fork.rb +6 -0
  35. data/lib/tengine/job/job.rb +23 -0
  36. data/lib/tengine/job/jobnet.rb +173 -0
  37. data/lib/tengine/job/jobnet/builder.rb +150 -0
  38. data/lib/tengine/job/jobnet/job_state_transition.rb +167 -0
  39. data/lib/tengine/job/jobnet/jobnet_state_transition.rb +110 -0
  40. data/lib/tengine/job/jobnet/state_transition.rb +37 -0
  41. data/lib/tengine/job/jobnet_actual.rb +55 -0
  42. data/lib/tengine/job/jobnet_template.rb +10 -0
  43. data/lib/tengine/job/join.rb +6 -0
  44. data/lib/tengine/job/junction.rb +29 -0
  45. data/lib/tengine/job/killing.rb +30 -0
  46. data/lib/tengine/job/mm_compatibility.rb +6 -0
  47. data/lib/tengine/job/mm_compatibility/connectable.rb +13 -0
  48. data/lib/tengine/job/name_path.rb +31 -0
  49. data/lib/tengine/job/root.rb +16 -0
  50. data/lib/tengine/job/root_jobnet_actual.rb +39 -0
  51. data/lib/tengine/job/root_jobnet_template.rb +49 -0
  52. data/lib/tengine/job/script_executable.rb +235 -0
  53. data/lib/tengine/job/signal.rb +121 -0
  54. data/lib/tengine/job/start.rb +20 -0
  55. data/lib/tengine/job/stoppable.rb +15 -0
  56. data/lib/tengine/job/vertex.rb +172 -0
  57. data/lib/tengine_job.rb +3 -0
  58. data/spec/fixtures/rjn_0001_simple_jobnet_builder.rb +42 -0
  59. data/spec/fixtures/rjn_0002_simple_parallel_jobnet_builder.rb +42 -0
  60. data/spec/fixtures/rjn_0003_fork_join_jobnet_builder.rb +61 -0
  61. data/spec/fixtures/rjn_0004_parallel_jobnet_with_finally_fixture.rb +62 -0
  62. data/spec/fixtures/rjn_0005_retry_two_layer_fixture.rb +153 -0
  63. data/spec/fixtures/rjn_0008_expansion_fixture.rb +32 -0
  64. data/spec/fixtures/rjn_0009_tree_sequential_jobnet_builder.rb +174 -0
  65. data/spec/fixtures/rjn_0010_2jobs_and_1job_parallel_jobnet_builder.rb +39 -0
  66. data/spec/fixtures/rjn_0011_nested_fork_jobnet_builder.rb +96 -0
  67. data/spec/fixtures/rjn_0012_nested_and_finally_builder.rb +157 -0
  68. data/spec/fixtures/rjn_1004_hadoop_job_in_jobnet_fixture.rb +105 -0
  69. data/spec/fixtures/rjn_means_root_jobnet +0 -0
  70. data/spec/fixtures/test_credential_fixture.rb +12 -0
  71. data/spec/fixtures/test_server_fixture.rb +28 -0
  72. data/spec/mongoid.yml +35 -0
  73. data/spec/spec_helper.rb +56 -0
  74. data/spec/sshd/.gitignore +1 -0
  75. data/spec/sshd/id_rsa +51 -0
  76. data/spec/sshd/id_rsa.pub +1 -0
  77. data/spec/sshd/ssh_host_rsa_key +51 -0
  78. data/spec/sshd/ssh_host_rsa_key.pub +1 -0
  79. data/spec/sshd/sshd_config +10 -0
  80. data/spec/sshd/sshd_config.erb +11 -0
  81. data/spec/sshd/tengine_job_test.sh +6 -0
  82. data/spec/support/jobnet_fixture_builder.rb +145 -0
  83. data/spec/support/mongo_index_key_log.rb +91 -0
  84. data/spec/tengine/job/category_spec.rb +193 -0
  85. data/spec/tengine/job/connectable_spec.rb +94 -0
  86. data/spec/tengine/job/drivers/job_controll_driver/connection_error_spec.rb +236 -0
  87. data/spec/tengine/job/drivers/job_controll_driver/duplicated_job_start_spec.rb +302 -0
  88. data/spec/tengine/job/drivers/job_controll_driver/expansion_spec.rb +120 -0
  89. data/spec/tengine/job/drivers/job_controll_driver/stop_spec.rb +159 -0
  90. data/spec/tengine/job/drivers/job_controll_driver_spec.rb +623 -0
  91. data/spec/tengine/job/drivers/job_execution_driver_spec.rb +88 -0
  92. data/spec/tengine/job/drivers/jobnet_control_driver/nested_and_finally_spec.rb +472 -0
  93. data/spec/tengine/job/drivers/jobnet_control_driver/nested_jobnet_spec.rb +231 -0
  94. data/spec/tengine/job/drivers/jobnet_control_driver/stop_jobnet_spec.rb +202 -0
  95. data/spec/tengine/job/drivers/jobnet_control_driver_spec.rb +446 -0
  96. data/spec/tengine/job/drivers/schedule_driver_spec.rb +202 -0
  97. data/spec/tengine/job/dsl_binder_spec.rb +36 -0
  98. data/spec/tengine/job/dsl_loader_spec.rb +403 -0
  99. data/spec/tengine/job/dsls/0013_hadoop_job_run.rb +29 -0
  100. data/spec/tengine/job/dsls/0014_join_and_join.rb +19 -0
  101. data/spec/tengine/job/dsls/0015_fork_and_fork.rb +18 -0
  102. data/spec/tengine/job/dsls/0016_complex_fork_and_join.rb +20 -0
  103. data/spec/tengine/job/dsls/0017_finally.rb +15 -0
  104. data/spec/tengine/job/dsls/0018_expansion.rb +23 -0
  105. data/spec/tengine/job/dsls/0019_execute_job_on_event.rb +16 -0
  106. data/spec/tengine/job/dsls/0020_duplicated_jobnet_name.rb +16 -0
  107. data/spec/tengine/job/dsls/1060_test_dir1/1060_test_dir2/0013_hadoop_job_run.rb +29 -0
  108. data/spec/tengine/job/dsls/2003_expansion/expansion_5.rb +11 -0
  109. data/spec/tengine/job/dsls/VERSION +1 -0
  110. data/spec/tengine/job/dynamic_env_spec.rb +95 -0
  111. data/spec/tengine/job/edge_spec.rb +241 -0
  112. data/spec/tengine/job/element_selector_notation_spec.rb +354 -0
  113. data/spec/tengine/job/examples_spec.rb +62 -0
  114. data/spec/tengine/job/execution_spec.rb +100 -0
  115. data/spec/tengine/job/expansion_spec.rb +116 -0
  116. data/spec/tengine/job/hadoop_job_run_spec.rb +65 -0
  117. data/spec/tengine/job/job_spec.rb +4 -0
  118. data/spec/tengine/job/jobnet/1015_complecated_jobnet_spec.rb +72 -0
  119. data/spec/tengine/job/jobnet_actual_spec.rb +175 -0
  120. data/spec/tengine/job/jobnet_spec.rb +399 -0
  121. data/spec/tengine/job/jobnet_template_spec.rb +240 -0
  122. data/spec/tengine/job/killing_spec.rb +91 -0
  123. data/spec/tengine/job/reset_spec.rb +958 -0
  124. data/spec/tengine/job/reset_spec/4056_1_dump.txt +1 -0
  125. data/spec/tengine/job/root_jobnet_actual_spec.rb +89 -0
  126. data/spec/tengine/job/root_jobnet_template_spec.rb +248 -0
  127. data/spec/tengine/job/script_executable_spec.rb +132 -0
  128. data/spec/tengine/job/stoppable_spec.rb +176 -0
  129. data/spec/tengine/job/vertex_spec.rb +25 -0
  130. data/spec/tengine_job_spec.rb +4 -0
  131. data/tengine_job.gemspec +197 -0
  132. data/tmp/log/.gitignore +1 -0
  133. metadata +296 -0
@@ -0,0 +1,32 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/job'
3
+
4
+ # ジョブネットの終端を表すVertex。特に状態は持たない。
5
+ class Tengine::Job::End < Tengine::Job::Vertex
6
+
7
+ # https://cacoo.com/diagrams/hdLgrzYsTBBpV3Wj#D26C1
8
+ def transmit(signal)
9
+ activate(signal)
10
+ end
11
+
12
+ def activate(signal)
13
+ complete_origin_edge(signal, :except_closed => true)
14
+ parent = self.parent # Endのparentであるジョブネット
15
+ parent_finally = parent.finally_vertex
16
+ if parent_finally && (parent.phase_key != :dying)
17
+ parent_finally.transmit(signal)
18
+ else
19
+ parent.finish(signal)
20
+ end
21
+ end
22
+
23
+ def reset(signal)
24
+ parent = self.parent # Endのparentであるジョブネット
25
+ if signal.execution.in_scope?(parent)
26
+ if f = parent.finally_vertex
27
+ f.reset(signal)
28
+ end
29
+ end
30
+ end
31
+
32
+ end
@@ -0,0 +1,74 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/job'
3
+ require 'selectable_attr'
4
+
5
+ # ジョブ/ジョブネットを実行する際の情報に関するモジュール
6
+ # Tengine::Job::JobnetActual, Tengine::Job::JobnetTemplateがこのモジュールをincludeします
7
+ module Tengine::Job::Executable
8
+ extend ActiveSupport::Concern
9
+
10
+ class PhaseError < StandardError
11
+ end
12
+
13
+ included do
14
+ field :phase_cd , :type => Integer, :default => 20 # 進行状況。とりうる値は以下を参照してください。詳しくは「tengine_jobパッケージ設計書」の「ジョブ/ジョブネット状態遷移」を参照してください
15
+ field :started_at , :type => Time # 開始時刻。以前はDateTimeでしたが、実績ベースの予定終了時刻の計算のためにTimeにしました
16
+ field :finished_at, :type => Time # 終了時刻。強制終了時にも設定されます。
17
+
18
+ include Tengine::Core::SelectableAttr
19
+ selectable_attr :phase_cd do
20
+ entry 20, :initialized, 'initialized'
21
+ entry 30, :ready , "ready"
22
+ entry 50, :starting , "starting"
23
+ entry 60, :running , "running"
24
+ entry 70, :dying , "dying"
25
+ entry 40, :success , "success"
26
+ entry 80, :error , "error"
27
+ entry 90, :stuck , "stuck"
28
+ end
29
+
30
+ def phase_key=(phase_key)
31
+ element_type = nil
32
+ case self.class
33
+ when Tengine::Job::Execution then element_type = "execution"
34
+ when Tengine::Job::RootJobnetActual then element_type = "root_jobnet"
35
+ when Tengine::Job::JobnetActual then element_type = self.script_executable? ? "job" :
36
+ self.jobnet_type_key == :normal ? "jobnet" : self.jobnet_type_name
37
+ end
38
+ Tengine.logger.debug("#{element_type} phase changed. <#{ self.id.to_s}> #{self.phase_name} -> #{ self.class.phase_name_by_key(phase_key)}")
39
+ if is_a?(Tengine::Job::JobnetActual)
40
+ children.each{|child| child.phase_key = phase_key if child.respond_to?(:chained_box?) && child.chained_box?}
41
+ end
42
+ self.write_attribute(:phase_cd, self.class.phase_id_by_key(phase_key))
43
+ end
44
+
45
+
46
+ unless defined?(HUMAN_PHASE_KEYS_HASH)
47
+ HUMAN_PHASE_KEYS_HASH = {
48
+ 'user_stop' => { :dying => :stopping_by_user, :error => :stopped_by_user }.freeze,
49
+ 'timeout' => { :dying => :stopping_by_timeout, :error => :stopped_by_timeout }.freeze,
50
+ }.freeze
51
+
52
+ HUMAN_PHASE_KEYS_ADDITIONAL = HUMAN_PHASE_KEYS_HASH.values.map{|hash| hash.values}.flatten.freeze
53
+ # HUMAN_PHASE_KEYS = (phase_keys + HUMAN_PHASE_KEYS_ADDITIONAL).freeze
54
+ end
55
+
56
+ # 可読可能なphase_keyを返します。
57
+ # 具体的にはphase_keyが:dying、:errorの場合は、stop_reasonを考慮した値を返します。
58
+ def human_phase_key
59
+ case phase_key
60
+ when :dying, :error then
61
+ hash = HUMAN_PHASE_KEYS_HASH[self.stop_reason]
62
+ hash ? hash[phase_key] : phase_key
63
+ else phase_key
64
+ end
65
+ end
66
+
67
+ # human_phase_keyの表示用の文字列を返します。
68
+ def human_phase_name
69
+ I18n.t(human_phase_key, :scope => "selectable_attrs.tengine/job/jobnet_actual.human_phase_name")
70
+ end
71
+
72
+ end
73
+
74
+ end
@@ -0,0 +1,141 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/core'
3
+
4
+ class Tengine::Job::Execution
5
+ include Mongoid::Document
6
+ include Mongoid::Timestamps
7
+ include Tengine::Job::Executable
8
+ include Tengine::Core::CollectionAccessible
9
+
10
+ field :target_actual_ids, :type => Array
11
+ array_text_accessor :target_actual_ids
12
+
13
+ field :preparation_command, :type => String
14
+ field :actual_base_timeout_alert, :type => Integer
15
+ field :actual_base_timeout_termination, :type => Integer
16
+ field :estimated_time, :type => Integer
17
+ field :keeping_stdout, :type => Boolean
18
+ field :keeping_stderr, :type => Boolean
19
+
20
+ # 1.0.0で想定している実行は 再実行でない実行時にスポット実行は想定していないが
21
+ # そういう利用も考えられるので、対応できるように属性はわけておきます
22
+ field :retry, :type => Boolean, :default => false # 再実行かどうか
23
+ field :spot , :type => Boolean, :default => false # スポット実行かどうか
24
+
25
+ belongs_to :root_jobnet, :class_name => "Tengine::Job::RootJobnetActual", :index => true, :inverse_of => :executions
26
+
27
+ attr_accessor :signal # runを実行して、ackを返す際に一時的にsignalを記録しておく属性です。それ以外には使用しないでください。
28
+
29
+ # 実開始日時から求める予定終了時刻
30
+ def actual_estimated_end
31
+ return nil unless started_at
32
+ (started_at + (estimated_time || 0)).utc
33
+ end
34
+
35
+ def name_as_resource
36
+ root_jobnet.name_as_resource.sub(/^job:/, 'execution:')
37
+ end
38
+
39
+ def target_actuals
40
+ r = self.root_jobnet
41
+ if target_actual_ids.nil? || target_actual_ids.empty?
42
+ [r]
43
+ else
44
+ target_actual_ids.map do |target_actual_id|
45
+ r.vertex(target_actual_id)
46
+ end
47
+ end
48
+ end
49
+
50
+ def scope_root
51
+ unless @scope_root
52
+ actual = target_actuals.first
53
+ @scope_root = spot ? actual : actual.parent || actual
54
+ unless @scope_root
55
+ raise "@scope_root must not be nil"
56
+ end
57
+ end
58
+ @scope_root
59
+ end
60
+
61
+ def in_scope?(vertex)
62
+ return false if vertex.nil?
63
+ return true if target_actual_ids.nil? || target_actual_ids.empty?
64
+ (vertex.id == scope_root.id) || vertex.ancestors.map(&:id).include?(scope_root.id)
65
+ end
66
+
67
+ def transmit(signal)
68
+ case phase_key
69
+ when :initialized then
70
+ if self.retry
71
+ target_actuals.each do |target|
72
+ target.reset(signal)
73
+ end
74
+ end
75
+ self.phase_key = :ready
76
+ activate(signal)
77
+ else
78
+ raise "Unsupported phase_key for transmit: #{phase_key.inspect}"
79
+ end
80
+ end
81
+
82
+ def activate(signal)
83
+ case phase_key
84
+ when :ready then
85
+ self.phase_key = :starting
86
+ if self.retry
87
+ target_actuals.each do |target|
88
+ target.transmit(signal)
89
+ end
90
+ else
91
+ root_jobnet.transmit(signal)
92
+ end
93
+ else
94
+ raise "Unsupported phase_key for activate: #{phase_key.inspect}"
95
+ end
96
+ end
97
+
98
+ def ack(signal)
99
+ case phase_key
100
+ when :ready then
101
+ raise Tengine::Job::Executable::PhaseError, "ack not available on #{phase_key.inspect}"
102
+ when :starting then
103
+ self.phase_key = :running
104
+ end
105
+ end
106
+
107
+ def succeed(signal)
108
+ case phase_key
109
+ when :initialized, :ready, :error then
110
+ raise Tengine::Job::Executable::PhaseError, "succeed not available on #{phase_key.inspect}"
111
+ when :starting, :running, :dying, :stuck then
112
+ self.phase_key = :success
113
+ signal.fire(self, :"success.execution.job.tengine")
114
+ end
115
+ end
116
+
117
+ def fail(signal)
118
+ case phase_key
119
+ when :initialized, :ready, :success then
120
+ raise Tengine::Job::Executable::PhaseError, "fail not available on #{phase_key.inspect}"
121
+ when :starting, :running, :dying, :stuck then
122
+ self.phase_key = :error
123
+ signal.fire(self, :"error.execution.job.tengine")
124
+ end
125
+ end
126
+
127
+ # def fire_stop(signal)
128
+ # return if self.phase_key == :initialized
129
+ # signal.fire(self, :"stop.execution.job.tengine", {
130
+ # :execution_id => self.id,
131
+ # :root_jobnet_id => root_jobnet.id,
132
+ # :target_jobnet_id => root_jobnet.id,
133
+ # })
134
+ # end
135
+
136
+ def stop(signal)
137
+ self.phase_key = :dying
138
+ root_jobnet.fire_stop(signal)
139
+ end
140
+
141
+ end
@@ -0,0 +1,37 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/job'
3
+
4
+ # ルートジョブネットを他のジョブネット内に展開するための特殊なテンプレート用Vertex。
5
+ class Tengine::Job::Expansion < Tengine::Job::Job
6
+ def actual_class
7
+ Tengine::Job::JobnetActual
8
+ end
9
+ def root_jobnet_template
10
+ unless @root_jobnet_template
11
+ cond = {:dsl_version => root.dsl_version, :name => name}
12
+ @root_jobnet_template = Tengine::Job::RootJobnetTemplate.first(:conditions => cond)
13
+ end
14
+ @root_jobnet_template
15
+ end
16
+
17
+ IGNORED_FIELD_NAMES = (Tengine::Job::Vertex::IGNORED_FIELD_NAMES + %w[name dsl_version jobnet_type_cd version updated_at created_at children edges]).freeze
18
+
19
+ def generating_attrs
20
+ result = super
21
+ attrs = root_jobnet_template.attributes.dup
22
+ if template = root_jobnet_template
23
+ attrs[:template_id] = template.id
24
+ end
25
+ attrs.delete_if{|attr, value| IGNORED_FIELD_NAMES.include?(attr)}
26
+ result.update(attrs)
27
+ result
28
+ end
29
+ def generating_children; root_jobnet_template.children; end
30
+ def generating_edges; root_jobnet_template.edges; end
31
+
32
+ def generate(klass = actual_class)
33
+ result = super
34
+ result.was_expansion = true
35
+ result
36
+ end
37
+ end
@@ -0,0 +1,6 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/job'
3
+
4
+ # 一つのVertexから複数のVertexへSignalを通知する分岐のVertex。
5
+ class Tengine::Job::Fork < Tengine::Job::Junction
6
+ end
@@ -0,0 +1,23 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/job'
3
+
4
+ # 処理を意味するVertex。実際に実行を行うTengine::Job::Scriptやジョブネットである
5
+ # Tengine::Job::Jobnetの継承元である。
6
+ class Tengine::Job::Job < Tengine::Job::Vertex
7
+ include Tengine::Job::Connectable
8
+ include Tengine::Job::Killing
9
+
10
+ field :name, :type => String # ジョブの名称。
11
+
12
+ validates :name, :presence => true
13
+
14
+ # リソース識別子を返します
15
+ def name_as_resource
16
+ @name_as_resource ||= "job:#{Tengine::Event.host_name}/#{Process.pid.to_s}/#{root.id.to_s}/#{id.to_s}"
17
+ end
18
+
19
+ def short_inspect
20
+ "#<%%%-30s id: %s name: %s>" % [self.class.name, self.id.to_s, name]
21
+ end
22
+
23
+ end
@@ -0,0 +1,173 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'tengine/job'
3
+
4
+ require 'selectable_attr'
5
+
6
+ # ジョブの始端から終端までを持ち、VertexとEdgeを組み合わせてジョブネットを構成することができるVertex。
7
+ # 自身もジョブネットを構成するVertexの一部として扱われる。
8
+ class Tengine::Job::Jobnet < Tengine::Job::Job
9
+ include Tengine::Core::SelectableAttr
10
+ include Tengine::Job::ElementSelectorNotation
11
+
12
+ autoload :Builder, "tengine/job/jobnet/builder"
13
+ autoload :StateTransition, 'tengine/job/jobnet/state_transition'
14
+ autoload :JobStateTransition, 'tengine/job/jobnet/job_state_transition'
15
+ autoload :JobnetStateTransition, 'tengine/job/jobnet/jobnet_state_transition'
16
+
17
+ field :script , :type => String # 実行されるスクリプト(本来Tengine::Job::Scriptが保持しますが、子要素を保持してかつスクリプトを実行するhadoop_job_runもある)
18
+ field :description , :type => String # ジョブネットの説明
19
+ field :jobnet_type_cd, :type => Integer, :default => 1 # ジョブネットの種類。後述の定義を参照してください。
20
+
21
+ selectable_attr :jobnet_type_cd do
22
+ entry 1, :normal , "normal"
23
+ entry 2, :finally , "finally", :alternative => true
24
+ # entry 3, :recover , "recover", :alternative => true
25
+ entry 4, :hadoop_job_run, "hadoop job run"
26
+ entry 5, :hadoop_job , "hadoop job" , :chained_box => true
27
+ entry 6, :map_phase , "map phase" , :chained_box => true
28
+ entry 7, :reduce_phase , "reduce phase" , :chained_box => true
29
+ end
30
+ def chained_box?; jobnet_type_entry[:chained_box]; end
31
+
32
+ embeds_many :edges, :class_name => "Tengine::Job::Edge", :inverse_of => :owner
33
+
34
+ class << self
35
+ def by_name(name)
36
+ first(:conditions => {:name => name})
37
+ end
38
+ end
39
+
40
+ def script_executable?
41
+ !script.blank?
42
+ end
43
+
44
+ def start_vertex
45
+ self.children.detect{|child| child.is_a?(Tengine::Job::Start)}
46
+ end
47
+
48
+ def end_vertex
49
+ self.children.detect{|child| child.is_a?(Tengine::Job::End)}
50
+ end
51
+
52
+ def finally_vertex
53
+ self.children.detect{|child| child.is_a?(Tengine::Job::Jobnet) && (child.jobnet_type_key == :finally)}
54
+ end
55
+ alias_method :finally_jobnet, :finally_vertex
56
+
57
+ def with_start
58
+ self.children << Tengine::Job::Start.new
59
+ self
60
+ end
61
+
62
+ def prepare_end
63
+ if self.children.last.is_a?(Tengine::Job::End)
64
+ _end = self.children.last
65
+ yield(_end) if block_given?
66
+ else
67
+ _end = Tengine::Job::End.new
68
+ yield(_end) if block_given?
69
+ self.children << _end
70
+ end
71
+ _end
72
+ end
73
+
74
+ def child_by_name(str)
75
+ case str
76
+ when '..' then parent
77
+ when '.' then self
78
+ when 'start' then start_vertex
79
+ when 'end' then end_vertex
80
+ when 'finally' then finally_vertex
81
+ else
82
+ self.children.detect{|c| c.is_a?(Tengine::Job::Job) && (c.name == str)}
83
+ end
84
+ end
85
+
86
+ def build_edges(auto_sequence, boot_job_names, redirections)
87
+ if self.children.length == 1 # 最初に追加したStartだけなら。
88
+ self.children.delete_all
89
+ return
90
+ end
91
+ if auto_sequence || boot_job_names.empty?
92
+ prepare_end
93
+ build_sequencial_edges
94
+ else
95
+ Builder.new(self, boot_job_names, redirections).process
96
+ end
97
+ end
98
+
99
+ def build_sequencial_edges
100
+ self.edges.clear
101
+ current = nil
102
+ self.children.each do |child|
103
+ next if child.is_a?(Tengine::Job::Jobnet) && !!child.jobnet_type_entry[:alternative]
104
+ if current
105
+ edge = self.new_edge(current, child)
106
+ yield(edge) if block_given?
107
+ end
108
+ current = child
109
+ end
110
+ end
111
+
112
+ def new_edge(origin, destination)
113
+ origin_id = origin.is_a?(Tengine::Job::Vertex) ? origin.id : origin
114
+ destination_id = destination.is_a?(Tengine::Job::Vertex) ? destination.id : destination
115
+ edges.new(:origin_id => origin_id, :destination_id => destination_id)
116
+ end
117
+
118
+ def find_descendant_edge(edge_id)
119
+ edge_id = String(edge_id)
120
+ visitor = Tengine::Job::Vertex::AnyVisitor.new do |vertex|
121
+ if vertex.respond_to?(:edges)
122
+ vertex.edges.detect{|edge| edge.id.to_s == edge_id}
123
+ else
124
+ nil
125
+ end
126
+ end
127
+ visitor.visit(self)
128
+ end
129
+ alias_method :edge, :find_descendant_edge
130
+
131
+ def find_descendant(vertex_id)
132
+ vertex_id = String(vertex_id)
133
+ return nil if vertex_id == self.id.to_s
134
+ vertex(vertex_id)
135
+ end
136
+
137
+ def vertex(vertex_id)
138
+ vertex_id = String(vertex_id)
139
+ return self if vertex_id == self.id.to_s
140
+ visitor = Tengine::Job::Vertex::AnyVisitor.new{|v| vertex_id == v.id.to_s ? v : nil }
141
+ visitor.visit(self)
142
+ end
143
+
144
+ def find_descendant_by_name_path(name_path)
145
+ return nil if name_path == self.name_path
146
+ vertex_by_name_path(name_path)
147
+ end
148
+
149
+ def vertex_by_name_path(name_path)
150
+ Tengine::Job::NamePath.absolute?(name_path) ?
151
+ root.vertex_by_absolute_name_path(name_path) :
152
+ vertex_by_relative_name_path(name_path)
153
+ end
154
+
155
+ def vertex_by_absolute_name_path(name_path)
156
+ return self if name_path.to_s == self.name_path
157
+ visitor = Tengine::Job::Vertex::AnyVisitor.new do |vertex|
158
+ if name_path == (vertex.respond_to?(:name_path) ? vertex.name_path : nil)
159
+ vertex
160
+ else
161
+ nil
162
+ end
163
+ end
164
+ visitor.visit(self)
165
+ end
166
+
167
+ def vertex_by_relative_name_path(name_path)
168
+ head, tail = *name_path.split(Tengine::Job::NamePath::SEPARATOR, 2)
169
+ child = child_by_name(head)
170
+ tail ? child.vertex_by_relative_name_path(tail) : child
171
+ end
172
+
173
+ end