patriot-workflow-scheduler 0.6.1

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 (79) hide show
  1. checksums.yaml +15 -0
  2. data/bin/patriot +8 -0
  3. data/bin/patriot-init +35 -0
  4. data/lib/patriot.rb +11 -0
  5. data/lib/patriot/command.rb +71 -0
  6. data/lib/patriot/command/base.rb +199 -0
  7. data/lib/patriot/command/command_group.rb +43 -0
  8. data/lib/patriot/command/command_macro.rb +141 -0
  9. data/lib/patriot/command/composite.rb +49 -0
  10. data/lib/patriot/command/parser.rb +78 -0
  11. data/lib/patriot/command/sh_command.rb +42 -0
  12. data/lib/patriot/controller.rb +2 -0
  13. data/lib/patriot/controller/package_controller.rb +81 -0
  14. data/lib/patriot/controller/worker_admin_controller.rb +159 -0
  15. data/lib/patriot/job_store.rb +66 -0
  16. data/lib/patriot/job_store/base.rb +159 -0
  17. data/lib/patriot/job_store/factory.rb +19 -0
  18. data/lib/patriot/job_store/in_memory_store.rb +252 -0
  19. data/lib/patriot/job_store/job.rb +118 -0
  20. data/lib/patriot/job_store/job_ticket.rb +30 -0
  21. data/lib/patriot/job_store/rdb_job_store.rb +353 -0
  22. data/lib/patriot/tool.rb +2 -0
  23. data/lib/patriot/tool/batch_parser.rb +102 -0
  24. data/lib/patriot/tool/patriot_command.rb +48 -0
  25. data/lib/patriot/tool/patriot_commands/execute.rb +92 -0
  26. data/lib/patriot/tool/patriot_commands/job.rb +62 -0
  27. data/lib/patriot/tool/patriot_commands/plugin.rb +41 -0
  28. data/lib/patriot/tool/patriot_commands/register.rb +77 -0
  29. data/lib/patriot/tool/patriot_commands/upgrade.rb +24 -0
  30. data/lib/patriot/tool/patriot_commands/validate.rb +84 -0
  31. data/lib/patriot/tool/patriot_commands/worker.rb +35 -0
  32. data/lib/patriot/tool/patriot_commands/worker_admin.rb +60 -0
  33. data/lib/patriot/util.rb +14 -0
  34. data/lib/patriot/util/config.rb +58 -0
  35. data/lib/patriot/util/config/base.rb +22 -0
  36. data/lib/patriot/util/config/inifile_config.rb +63 -0
  37. data/lib/patriot/util/cron_format_parser.rb +104 -0
  38. data/lib/patriot/util/date_util.rb +200 -0
  39. data/lib/patriot/util/db_client.rb +65 -0
  40. data/lib/patriot/util/db_client/base.rb +142 -0
  41. data/lib/patriot/util/db_client/hash_record.rb +53 -0
  42. data/lib/patriot/util/db_client/record.rb +25 -0
  43. data/lib/patriot/util/logger.rb +24 -0
  44. data/lib/patriot/util/logger/facade.rb +33 -0
  45. data/lib/patriot/util/logger/factory.rb +59 -0
  46. data/lib/patriot/util/logger/log4r_factory.rb +111 -0
  47. data/lib/patriot/util/logger/webrick_log_factory.rb +47 -0
  48. data/lib/patriot/util/param.rb +73 -0
  49. data/lib/patriot/util/retry.rb +30 -0
  50. data/lib/patriot/util/script.rb +52 -0
  51. data/lib/patriot/util/system.rb +120 -0
  52. data/lib/patriot/worker.rb +35 -0
  53. data/lib/patriot/worker/base.rb +153 -0
  54. data/lib/patriot/worker/info_server.rb +90 -0
  55. data/lib/patriot/worker/job_store_server.rb +32 -0
  56. data/lib/patriot/worker/multi_node_worker.rb +157 -0
  57. data/lib/patriot/worker/servlet.rb +23 -0
  58. data/lib/patriot/worker/servlet/job_servlet.rb +128 -0
  59. data/lib/patriot/worker/servlet/worker_status_servlet.rb +44 -0
  60. data/skel/batch/sample/daily/test.pbc +4 -0
  61. data/skel/config/patriot.ini +21 -0
  62. data/skel/public/css/bootstrap.css +2495 -0
  63. data/skel/public/css/original.css +54 -0
  64. data/skel/public/js/bootstrap-alerts.js +124 -0
  65. data/skel/public/js/bootstrap-buttons.js +64 -0
  66. data/skel/public/js/bootstrap-dropdown.js +55 -0
  67. data/skel/public/js/bootstrap-modal.js +260 -0
  68. data/skel/public/js/bootstrap-popover.js +90 -0
  69. data/skel/public/js/bootstrap-scrollspy.js +107 -0
  70. data/skel/public/js/bootstrap-tabs.js +80 -0
  71. data/skel/public/js/bootstrap-twipsy.js +321 -0
  72. data/skel/public/js/jquery-1.6.4.min.js +4 -0
  73. data/skel/public/templates/_jobs.erb +97 -0
  74. data/skel/public/templates/job.erb +119 -0
  75. data/skel/public/templates/jobs.erb +21 -0
  76. data/skel/public/templates/jobs_deleted.erb +6 -0
  77. data/skel/public/templates/layout.erb +103 -0
  78. data/skel/public/templates/state_updated.erb +6 -0
  79. metadata +235 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MjRlNzEwZGJjMmY4MTRjMDU1NjBhYTg5OTRlNWYzYmUyZjQ5MWE3MQ==
5
+ data.tar.gz: !binary |-
6
+ ZTA3YjNiN2JjMDczMjZkYWEzNTc3NWJhOWZjYmNkOTdhZTU1MzM5ZQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ ODY5NzRjNWZjYmU2MzU4N2E4M2U5YThlODRkN2I2NThlZjI3ZTUxYzcwZTg0
10
+ MDJmOGZhZTE5ZTE4ZWYzNWIwYzkyZmRhYTkwOTBmZWYxYWE0Mjg3YmQ1MWZl
11
+ ODgyMTljODVjNGU0ZmE5OTJlZGM5ODkxMzQ4MzIxY2I4ZmZjMGY=
12
+ data.tar.gz: !binary |-
13
+ YzMzYmU3YzU3ZGQ5YWUxNGRmNTYyNmM0NTMwMzI4OWVhNDgyODFiNDZkZTI5
14
+ NmExNTNjOGI0NmY3YTNiYzE1MTk4N2NhYTYxNGExNDQyNWM5NzhmNDA3NmU1
15
+ ZTM1NzIwZmNhMDZlNGIwZmE5NjdkNzczMmM3MzI4Njg4NDcyMDY=
data/bin/patriot ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ Encoding.default_external = 'utf-8'
3
+ $home = File.expand_path(File.dirname(File.expand_path(__FILE__)) + "/..")
4
+ $: << File.join($home,"lib")
5
+ require 'rubygems'
6
+ require 'patriot'
7
+
8
+ Patriot::Tool::PatriotCommand.start(ARGV)
data/bin/patriot-init ADDED
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ require 'fileutils'
3
+
4
+ target=ARGV[0]
5
+ unless File.exist?(target)
6
+ puts "directory #{target} does not exist"
7
+ Dir.mkdir(target)
8
+ end
9
+ base_dir = File.join(File.dirname(File.expand_path(__FILE__)), "..")
10
+ skel_dir = File.join(base_dir, "skel")
11
+ Dir.glob(File.join(skel_dir,"*")).each do |d|
12
+ if File.exist?(File.join(target, File.basename(d)))
13
+ puts "#{d} already exists"
14
+ else
15
+ puts "copy #{d} to #{target}"
16
+ FileUtils.cp_r(d, target)
17
+ end
18
+ end
19
+
20
+
21
+ def mkdir_ifnot_exist(dir)
22
+ if File.exist?(dir)
23
+ puts "#{dir} already exist"
24
+ else
25
+ puts "mkdir #{dir}"
26
+ FileUtils.mkdir_p(dir)
27
+ end
28
+ end
29
+
30
+ mkdir_ifnot_exist(File.join(target, "bin"))
31
+ FileUtils.cp(File.join(base_dir,"bin","patriot"),File.join(target,"bin","patriot"))
32
+ mkdir_ifnot_exist(File.join(target, "plugins"))
33
+ mkdir_ifnot_exist(File.join(target, "run"))
34
+
35
+
data/lib/patriot.rb ADDED
@@ -0,0 +1,11 @@
1
+ # -*- coding: utf-8 -*-
2
+ require 'rubygems'
3
+ require 'active_support'
4
+
5
+ require 'patriot/util'
6
+ require 'patriot/command'
7
+ require 'patriot/job_store'
8
+ require 'patriot/worker'
9
+ require 'patriot/controller'
10
+ require 'patriot/tool'
11
+
@@ -0,0 +1,71 @@
1
+ # the root name space for this scheduler
2
+ module Patriot
3
+ # a name space for commands
4
+ module Command
5
+ # a parameter key for command class name (used in jobs)
6
+ COMMAND_CLASS_KEY = "COMMAND_CLASS"
7
+
8
+ # attribute name for required products
9
+ REQUISITES_ATTR = "requisites"
10
+ # attribute name for produced products
11
+ PRODUCTS_ATTR = "products"
12
+ # attribute name for job state
13
+ STATE_ATTR = "state"
14
+ # attribute name for job priority
15
+ PRIORITY_ATTR = "priority"
16
+ # attribute name for job execution node constraint
17
+ EXEC_NODE_ATTR = "exec_node"
18
+ # attribute name for job execution host constraint
19
+ EXEC_HOST_ATTR = "exec_host"
20
+ # attribute name for start time constraint
21
+ START_DATETIME_ATTR = "start_datetime"
22
+ # attribute name for the skip on fail marker
23
+ SKIP_ON_FAIL_ATTR = "skip_on_fail"
24
+
25
+ # a list of comman attributes
26
+ COMMON_ATTRIBUTES = [
27
+ REQUISITES_ATTR,
28
+ PRODUCTS_ATTR,
29
+ STATE_ATTR,
30
+ PRIORITY_ATTR,
31
+ EXEC_NODE_ATTR,
32
+ EXEC_HOST_ATTR,
33
+ START_DATETIME_ATTR,
34
+ SKIP_ON_FAIL_ATTR
35
+ ]
36
+
37
+ # exit code of a command
38
+ module ExitCode
39
+ # successfully finished
40
+ SUCCEEDED = 0
41
+ # failed
42
+ FAILED = 1
43
+ # failed but skipped (marked skip_on_fail)
44
+ FAILURE_SKIPPED = 2
45
+ # skip (e.g., updated elsewhere)
46
+ SKIPPED = -1
47
+
48
+ # @param exit_code [Patriot::Command::ExitCode]
49
+ # @return [String] string expression of the exit code
50
+ def name_of(exit_code)
51
+ exit_code = exit_code.to_i
52
+ return case exit_code
53
+ when 0 then "SUCCEEDED"
54
+ when 1 then "FAILED"
55
+ when 2 then "FAILURE_SKIPPED"
56
+ when 4 then "FAILED" # for backward compatibility
57
+ else raise "unknown exit_code #{exit_code}"
58
+ end
59
+ end
60
+ module_function :name_of
61
+ end
62
+
63
+ require 'patriot/command/parser'
64
+ require 'patriot/command/command_macro'
65
+ require 'patriot/command/base'
66
+ require 'patriot/command/command_group'
67
+ require 'patriot/command/composite'
68
+ require 'patriot/command/sh_command'
69
+ end
70
+ end
71
+
@@ -0,0 +1,199 @@
1
+ require 'active_support/core_ext/hash/deep_merge'
2
+ module Patriot
3
+ module Command
4
+ # The base class of every command.
5
+ # The command is an executable form of the job.
6
+ class Base
7
+ include Patriot::Command::Parser
8
+ include Patriot::Util::Param
9
+ include Patriot::Util::DateUtil
10
+ include Patriot::Util::Logger
11
+
12
+ class << self
13
+ include Patriot::Command::CommandMacro
14
+ end
15
+
16
+ attr_accessor :parser, :test_mode, :target_datetime
17
+
18
+ # comman attributes handled distinctively (only effective in top level commands)
19
+ volatile_attr :requisites, :products, :priority, :start_after, :exec_date, :exec_node, :exec_host, :skip_on_fail
20
+
21
+ # @param config [Patriot::Util::Config::Base] configuration for this command
22
+ def initialize(config)
23
+ @config = config
24
+ @logger = create_logger(config)
25
+ @param = {}
26
+ @requisites = []
27
+ @products = []
28
+ @macros = {}
29
+ @test_mode = false
30
+ end
31
+
32
+ # convert this to a job so that it can be stored to JobStore
33
+ # @return [Patriot::JobStore::Job] a job for this command.
34
+ def to_job
35
+ job = Patriot::JobStore::Job.new(self.job_id)
36
+ job.read_command(self)
37
+ return job
38
+ end
39
+
40
+ # get the value of an attribute
41
+ # @param attr_name [String] attribute name
42
+ # @return [Object] the value of the attribute specified with argument
43
+ def [](attr_name)
44
+ return instance_variable_get("@#{attr_name}".to_sym)
45
+ end
46
+
47
+ # build the identifier of the job for this command.
48
+ # This method should be overriden in sub-classes
49
+ # @return [String] the identifier of the job.
50
+ def job_id
51
+ raise NotImplementedError
52
+ end
53
+
54
+ # set default command name
55
+ # replace :: to handle in JSON format
56
+ # @return [String] a simplified command namd
57
+ def command_name
58
+ return self.class.to_s.split("::").last.downcase.gsub(/command/,"")
59
+ end
60
+
61
+ # add products required by this job.
62
+ # @param requisites [Array<String>] a list of products required by this job.
63
+ def require(requisites)
64
+ return if requisites.nil?
65
+ @requisites |= requisites.flatten
66
+ end
67
+
68
+ # add products produced by this job.
69
+ # @param products [Array<String>] a list of products produced by this job
70
+ def produce(products)
71
+ return if products.nil?
72
+ @products |= products.flatten
73
+ end
74
+
75
+ # mark this job to skip execution
76
+ def skip
77
+ param 'state' => Patriot::JobStore::JobState::SUCCEEDED
78
+ end
79
+
80
+ # mark this job to suspend execution
81
+ def suspend
82
+ param 'state' => Patriot::JobStore::JobState::SUSPEND
83
+ end
84
+
85
+ # mark this job to skip in case of failures
86
+ def skip_on_fail?
87
+ return @skip_on_fail == 'true' || @skip_on_fail == true
88
+ end
89
+
90
+ # @return [String] the target month in '%Y-%m'
91
+ def _month_
92
+ return @target_datetime.strftime("%Y-%m")
93
+ end
94
+
95
+ # @return [String] the target date in '%Y-%m-%d'
96
+ def _date_
97
+ return @target_datetime.strftime("%Y-%m-%d")
98
+ end
99
+
100
+ # @return [Integer] the tergat hour
101
+ def _hour_
102
+ return @target_datetime.hour
103
+ end
104
+
105
+ # start datetime of this command.
106
+ # This command should be executed after the return value of this method
107
+ # @return [DateTime]
108
+ def start_date_time
109
+ return nil if @exec_date.nil? && @start_after.nil?
110
+ # set tomorrow as default
111
+ date = (@exec_date || date_add(_date_, 1)).split("-").map(&:to_i)
112
+ # set midnight as default
113
+ time = (@start_after || "00:00:00").split(":").map(&:to_i)
114
+ return DateTime.new(date[0], date[1], date[2], time[0], time[1], time[2])
115
+ end
116
+
117
+ # update parameters with a given hash.
118
+ # If the hash includes keys which values have already been defined,
119
+ # the value for the key is replaced with the new value.
120
+ # @param _param [Hash] a hash from attribute name to its value
121
+ def param(_param)
122
+ @param = @param.deep_merge(_param)
123
+ end
124
+
125
+ # build this command as executables
126
+ # @param _param [Hash] default parameter
127
+ def build(_param={})
128
+ @param = _param.deep_merge(@param)
129
+ init_param
130
+ @start_datetime = start_date_time
131
+ cmds = configure()
132
+ cmds = [cmds] unless cmds.is_a?(Array)
133
+ cmds.each(&:validate_command_attrs)
134
+ return cmds.flatten
135
+ end
136
+
137
+ # initialize command attributes
138
+ def init_param
139
+ # set parameter value to instance variable
140
+ @param.each do |k,v|
141
+ raise "a reserved word #{k} is used as parameter name" if Patriot::Command::COMMAND_CLASS_KEY == k
142
+ raise "#{k} is already used in #{self.job_id}" unless instance_variable_get("@#{k}".to_sym).nil?
143
+ # don't evaluate here since all parameters are not set to instance variables
144
+ instance_variable_set("@#{k}".to_sym,v)
145
+ end
146
+
147
+ # evaluate command attributes using parameters (instance variables)
148
+ self.class.command_attrs.each { |a, d| configure_attr(a, d) }
149
+ end
150
+ protected :init_param
151
+
152
+ # configure a command attribute and set as an instance variable
153
+ # @param attr [String] an attribute name to be configured
154
+ # @param default_value default value of the attribute
155
+ def configure_attr(attr, default_value = nil)
156
+ v = instance_variable_get("@#{attr}".to_sym)
157
+ v = default_value if v.nil?
158
+ instance_variable_set("@#{attr}".to_sym, eval_attr(v))
159
+ end
160
+
161
+ # a hook method to implement comand-specific configuration
162
+ def configure
163
+ return self
164
+ end
165
+
166
+ # validate values of command attributes
167
+ # @see Patriot::Command::CommandMacro#validate_attr
168
+ def validate_command_attrs
169
+ self.class.validation_logics.each do |attr, logics|
170
+ val = self.instance_variable_get("@#{attr}".to_sym)
171
+ logics.each do |l|
172
+ unless l.call(self, attr, val)
173
+ raise "validation error : #{attr}=#{val} (#{self.class})"
174
+ end
175
+ end
176
+ end
177
+ end
178
+
179
+ # add a sub command
180
+ # @raise if sub command is not supported
181
+ def add_subcommand
182
+ raise "sub command is not supported"
183
+ end
184
+
185
+ # @return description of this command
186
+ def description
187
+ self.job_id
188
+ end
189
+
190
+ # execute this command
191
+ def execute()
192
+ raise NotImplementedError
193
+ end
194
+
195
+ end
196
+ end
197
+ end
198
+
199
+
@@ -0,0 +1,43 @@
1
+ module Patriot
2
+ module Command
3
+ # define a group of jobs
4
+ class CommandGroup < Base
5
+ declare_command_name :command_group
6
+ declare_command_name :job_group
7
+ attr_accessor :subcommands
8
+
9
+ # @see Patriot::Command::Base#initialize
10
+ def initialize(config)
11
+ super
12
+ @subcommands = []
13
+ end
14
+
15
+ # add a command to this group
16
+ # @param cmd [Patriot::Command::Base] a command to be added to this group
17
+ def add_subcommand(cmd)
18
+ @subcommands << cmd
19
+ end
20
+
21
+ # configure thie group.
22
+ # pass the required/produced products and parameters to the commands in this group
23
+ # @see Patriot::Command::Base#configure
24
+ # @return [Array<Patriot::Command::Base>] a list of commands in this group
25
+ def configure
26
+ return @subcommands.map{|cmd|
27
+ cmd.require @requisites
28
+ cmd.produce @products
29
+ cmd.build(@param)
30
+ }.flatten
31
+ end
32
+
33
+ # execute each command in this group
34
+ # @see Patriot::Command::Base#execute
35
+ def execute
36
+ @subcommands.each do |k,v|
37
+ v.execute
38
+ end
39
+ end
40
+
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,141 @@
1
+ module Patriot
2
+ module Command
3
+ # macros to be used for implmentation of command classes
4
+ module CommandMacro
5
+
6
+ # add module methods to DSL methods
7
+ # @param command_module [Module] module defines methods for the use in DSL
8
+ def add_dsl_function(command_module)
9
+ class_eval{ include command_module }
10
+ end
11
+
12
+ # declare DSL method name for defining the command
13
+ # @param mth_name [String] the DSL method name
14
+ # @param parent_cls [Class] parent command to which the new command is added
15
+ # @param cmd_cls [Class]
16
+ def declare_command_name(mth_name, parent_cls=Patriot::Command::CommandGroup, cmd_cls=self)
17
+ parent_cls.class_eval do
18
+ define_method(mth_name) do |&cmd_def|
19
+ cmd = new_command(cmd_cls, &cmd_def)
20
+ add_subcommand(cmd)
21
+ end
22
+ end
23
+ end
24
+
25
+ # declare command attributes to be able to use in DSL
26
+ # @param attrs [String|Hash] attribute names or a Hash from attribute name to its default value
27
+ # @yield block to preprocess attribute values
28
+ # @yieldparam [Patriot::Command::Base] command instance
29
+ # @yieldparam [String] attribute name
30
+ # @yieldparam [String] passed value
31
+ def command_attr(*attrs, &blk)
32
+ @command_attrs = {} if @command_attrs.nil?
33
+ default_values = {}
34
+ if attrs.size == 1 && attrs[0].is_a?(Hash)
35
+ default_values = attrs[0]
36
+ attrs = default_values.keys
37
+ end
38
+ attrs.each do |a|
39
+ raise "a reserved word #{a} is used as parameter name" if Patriot::Command::COMMAND_CLASS_KEY == a
40
+ raise "#{a} is already defined" if self.instance_methods.include?(a)
41
+ @command_attrs[a] = default_values[a]
42
+ define_method(a) do |*args|
43
+ raise "illegal size of arguments (#{a} with #{args.inspect})" unless args.size == 1
44
+ val = args[0]
45
+ if block_given?
46
+ yield(self, a, val)
47
+ else
48
+ self.param a.to_s => val
49
+ end
50
+ end
51
+ attr_writer a
52
+ end
53
+ return attrs
54
+ end
55
+
56
+ # declare command attributes for only internal use
57
+ # @param attrs [String] attribute name
58
+ def private_command_attr(*attrs)
59
+ command_attr(*attrs) do |cmd, attr_name, attr_val|
60
+ raise "only internal call is expected for #{attr_name}"
61
+ end
62
+ end
63
+
64
+ # declare command attributes to be able to use in DSL
65
+ # values of thes attributes are supposed to be used in {Patriot::Command::Base#do_configure}
66
+ # and would not be serialized
67
+ # @param attrs [String|Hash] attribute names or a Hash from attribute name to its default value
68
+ # @yield block to preprocess attribute values
69
+ # @yieldparam [Patriot::Command::Base] command instance
70
+ # @yieldparam [String] attribute name
71
+ # @yieldparam [String] passed value
72
+ def volatile_attr(*attrs, &blk)
73
+ @volatile_attrs = [] if @volatile_attrs.nil?
74
+ attrs = command_attr(*attrs, &blk)
75
+ attrs.each do |a|
76
+ @volatile_attrs << a
77
+ end
78
+ end
79
+
80
+ # @return [Hash] a Hash from attribute name to its value
81
+ def command_attrs
82
+ super_attrs = {}
83
+ if self.superclass.respond_to?(:command_attrs)
84
+ super_attrs = self.superclass.command_attrs
85
+ end
86
+ return super_attrs if @command_attrs.nil?
87
+ return super_attrs.merge(@command_attrs)
88
+ end
89
+
90
+ # @return [Array] a list of volatile attribute names
91
+ def volatile_attrs
92
+ super_attrs = []
93
+ if self.superclass.respond_to?(:volatile_attrs)
94
+ super_attrs = self.superclass.volatile_attrs
95
+ end
96
+ return super_attrs if @volatile_attrs.nil?
97
+ return @volatile_attrs | super_attrs
98
+ end
99
+
100
+ # @return [Array] a array of attribute names which should be inclued in Job
101
+ def serde_attrs
102
+ return command_attrs.keys - volatile_attrs
103
+ end
104
+
105
+ # validate attriubte value
106
+ # @param attrs attribute names to be validated
107
+ # @yield logic which validates the value
108
+ # @yieldreturn [Boolean] true if the value is valid, otherwise false
109
+ def validate_attr(*attrs, &blk)
110
+ raise "validation logic not given" unless block_given?
111
+ unless blk.arity == 3
112
+ raise "validation logic arguments should be (cmd, attr_name, attr_val)"
113
+ end
114
+ @validation_logics = {} if @validation_logics.nil?
115
+ attrs.each do |a|
116
+ raise "#{a} is not command attr" unless command_attrs.include?(a)
117
+ @validation_logics[a] = [] unless @validation_logics.has_key?(a)
118
+ @validation_logics[a] << blk
119
+ end
120
+ end
121
+
122
+ # check whether the attribute value is not nil
123
+ # @see {validate_attribute_value}
124
+ def validate_existence(*attrs)
125
+ validate_attr(*attrs) do |cmd, a, v|
126
+ !v.nil?
127
+ end
128
+ end
129
+
130
+ # @return [Hash] a hash from attribute name to validation logic (Process)
131
+ def validation_logics
132
+ super_logics = {}
133
+ if self.superclass.respond_to?(:volatile_attrs)
134
+ super_logics = self.superclass.validation_logics
135
+ end
136
+ return super_logics if @validation_logics.nil?
137
+ return super_logics.merge(@validation_logics)
138
+ end
139
+ end
140
+ end
141
+ end