abid 0.3.0.pre.alpha.3 → 0.3.0.pre.alpha.4

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 (52) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +3 -3
  3. data/abid.gemspec +1 -2
  4. data/exe/abidsc +1 -1
  5. data/lib/abid.rb +3 -30
  6. data/lib/abid/application.rb +83 -13
  7. data/lib/abid/cli/assume.rb +7 -8
  8. data/lib/abid/cli/list.rb +2 -2
  9. data/lib/abid/cli/migrate.rb +1 -1
  10. data/lib/abid/cli/revoke.rb +9 -10
  11. data/lib/abid/config.rb +2 -0
  12. data/lib/abid/dsl/abid_job.rb +58 -0
  13. data/lib/abid/dsl/actions.rb +36 -0
  14. data/lib/abid/dsl/job.rb +58 -0
  15. data/lib/abid/dsl/job_manager.rb +53 -0
  16. data/lib/abid/dsl/mixin.rb +52 -0
  17. data/lib/abid/dsl/params_spec.rb +64 -0
  18. data/lib/abid/dsl/play.rb +35 -0
  19. data/lib/abid/dsl/play_core.rb +354 -0
  20. data/lib/abid/dsl/rake_job.rb +41 -0
  21. data/lib/abid/dsl/syntax.rb +34 -0
  22. data/lib/abid/dsl/task.rb +53 -0
  23. data/lib/abid/engine.rb +36 -6
  24. data/lib/abid/engine/executor.rb +30 -48
  25. data/lib/abid/engine/process.rb +102 -68
  26. data/lib/abid/engine/process_manager.rb +72 -28
  27. data/lib/abid/engine/scheduler.rb +24 -30
  28. data/lib/abid/engine/waiter.rb +20 -30
  29. data/lib/abid/engine/worker_manager.rb +26 -60
  30. data/lib/abid/environment.rb +12 -27
  31. data/lib/abid/error.rb +2 -0
  32. data/lib/abid/params_format.rb +29 -12
  33. data/lib/abid/rake_extensions.rb +11 -3
  34. data/lib/abid/state_manager.rb +40 -0
  35. data/lib/abid/state_manager/state.rb +52 -114
  36. data/lib/abid/state_manager/state_service.rb +88 -0
  37. data/lib/abid/status.rb +63 -0
  38. data/lib/abid/version.rb +1 -1
  39. metadata +19 -32
  40. data/lib/Abidfile.rb +0 -1
  41. data/lib/abid/dsl_definition.rb +0 -29
  42. data/lib/abid/job.rb +0 -67
  43. data/lib/abid/job_manager.rb +0 -22
  44. data/lib/abid/mixin_task.rb +0 -29
  45. data/lib/abid/params_parser.rb +0 -50
  46. data/lib/abid/play.rb +0 -66
  47. data/lib/abid/play_core.rb +0 -53
  48. data/lib/abid/rake_extensions/task.rb +0 -41
  49. data/lib/abid/state_manager/database.rb +0 -40
  50. data/lib/abid/state_manager/state_proxy.rb +0 -65
  51. data/lib/abid/task.rb +0 -123
  52. data/lib/abid/task_manager.rb +0 -61
@@ -0,0 +1,58 @@
1
+ require 'forwardable'
2
+
3
+ module Abid
4
+ module DSL
5
+ # Common interface for RakeJob and AbidJob
6
+ class Job
7
+ extend Forwardable
8
+
9
+ def self.interface(name, args = [])
10
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
11
+ def #{name}(#{args.join(', ')})
12
+ raise NotImplementedError
13
+ end
14
+ RUBY
15
+ end
16
+ private_class_method :interface
17
+
18
+ interface :name
19
+ interface :arg_names
20
+ interface :worker
21
+ interface :prerequisites
22
+ interface :execute, %w(args)
23
+
24
+ interface :volatile?
25
+ interface :concerned?
26
+ interface :needed?
27
+
28
+ def initialize(task, params)
29
+ @task = task
30
+ @params = params
31
+ @options = task.application.options
32
+ end
33
+ attr_reader :task, :params, :options
34
+ def_delegators :task, :name, :arg_names
35
+
36
+ def trace_invoke
37
+ return unless @task.application.options.trace
38
+ @task.application.trace "** Invoke #{@task.name}"
39
+ end
40
+
41
+ def to_s
42
+ ParamsFormat.format_with_name(name, params)
43
+ end
44
+
45
+ def repair?
46
+ @options.repair
47
+ end
48
+
49
+ def dryrun?
50
+ @options.dryrun
51
+ end
52
+
53
+ def preview?
54
+ @options.preview
55
+ end
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,53 @@
1
+ require 'monitor'
2
+
3
+ module Abid
4
+ module DSL
5
+ # JobManager manages jobs.
6
+ class JobManager
7
+ def initialize(application)
8
+ @app = application
9
+ @tasks = Hash.new { |h, k| h[k] = {} }
10
+ @mon = Monitor.new
11
+ end
12
+
13
+ # Resolves params using params_spec and bind the task and resolved params.
14
+ # @param name [String,Symbol] task name
15
+ # @param params [Hash]
16
+ # @param scope [Rake::Scope]
17
+ # @return [Abid::DSL::TaskInstance]
18
+ def [](name, params = {}, scope = nil)
19
+ task = @app[name, scope]
20
+ resolved = resolve_params(task, params)
21
+ bind(task.name, resolved)
22
+ end
23
+
24
+ # Binds the task and params.
25
+ # @param name [String,Symbol] task name
26
+ # @param params [Hash]
27
+ # @return [Abid::DSL::TaskInstance]
28
+ def bind(name, params)
29
+ return @tasks[name][params] if @tasks[name][params]
30
+
31
+ @mon.synchronize do
32
+ @tasks[name][params.dup.freeze] ||= @app[name].bind(params)
33
+ end
34
+ end
35
+
36
+ def resolve_params(task, params)
37
+ ret = task.params_spec.each_with_object({}) do |(key, spec), h|
38
+ h[key] = fetch_param(task, key, spec, params, @app.global_params)
39
+ end
40
+ ParamsFormat.validate_params!(ret)
41
+ ret
42
+ end
43
+
44
+ def fetch_param(task, key, spec, *params_list)
45
+ found = params_list.find { |params| params.include?(key) }
46
+ return found[key] if found
47
+ return spec[:default] if spec.include?(:default)
48
+ raise "#{task.name}: param #{key} is not specified"
49
+ end
50
+ private :fetch_param
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,52 @@
1
+ require 'abid/dsl/play_core'
2
+
3
+ module Abid
4
+ module DSL
5
+ # `mixin` definition is evaluated in Mixin module context.
6
+ #
7
+ # mixin :foo do
8
+ # # this is evaluated in Mixin module context
9
+ # end
10
+ #
11
+ module Mixin
12
+ # Create new Mixin object.
13
+ # @param task [Rake::Task] owner task
14
+ def self.create(task)
15
+ mod = self
16
+ Module.new do
17
+ include mod
18
+ extend Mixin::ClassMethods
19
+ include task.application.global_mixin
20
+ extend helpers
21
+ self.task = task
22
+ end
23
+ end
24
+
25
+ # Create new global mixin.
26
+ # `global_mixin` does not include Application#global_mixin.
27
+ def self.create_global_mixin
28
+ Module.new do
29
+ include Mixin
30
+ extend Mixin::ClassMethods
31
+ end
32
+ end
33
+
34
+ include PlayCore
35
+
36
+ module ClassMethods
37
+ include PlayCore::ClassMethods
38
+
39
+ def included(obj)
40
+ return unless obj.is_a? PlayCore::ClassMethods
41
+ merge_helpers(obj)
42
+ end
43
+
44
+ def merge_helpers(obj)
45
+ my_helpers = helpers
46
+ obj.helpers.module_eval { include my_helpers }
47
+ obj.extend(obj.helpers) # re-extend by helpers
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,64 @@
1
+ module Abid
2
+ module DSL
3
+ # ParamsSpec manages params specifications declared in a play definition.
4
+ #
5
+ # Ancestors' params_spec are inherited.
6
+ class ParamsSpec
7
+ include Enumerable
8
+
9
+ attr_reader :specs
10
+ protected :specs
11
+
12
+ def initialize(play_class)
13
+ @play_class = play_class
14
+ @specs = {}
15
+ end
16
+
17
+ # @param key [Symbol] param name
18
+ # @return [Hash] param specification
19
+ def [](key)
20
+ @play_class.superplays.each do |sp|
21
+ h = sp.params_spec.specs
22
+ return h[key] if h.include?(key)
23
+ end
24
+ nil
25
+ end
26
+
27
+ # @param key [Symbol] param name
28
+ # @param val [Hash] param specification
29
+ def []=(key, val)
30
+ @specs[key] = val
31
+ end
32
+
33
+ # Mark given param as deleted.
34
+ # It does not affect ancestors' params_spec.
35
+ # @param key [Symbol] param name
36
+ def delete(key)
37
+ val = self[key]
38
+ @specs[key] = nil
39
+ val
40
+ end
41
+
42
+ # @yield [key, val] param name and spec
43
+ def each(&block)
44
+ to_h.each(&block)
45
+ end
46
+
47
+ def to_h
48
+ h = {}
49
+ @play_class.superplays.reverse.each do |sp|
50
+ h.update(sp.params_spec.specs)
51
+ end
52
+ h.reject { |_, v| v.nil? }
53
+ end
54
+
55
+ def inspect
56
+ to_h.inspect
57
+ end
58
+
59
+ def to_s
60
+ to_h.to_s
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,35 @@
1
+ require 'abid/dsl/play_core'
2
+
3
+ module Abid
4
+ module DSL
5
+ # `play` definition is evaluated in Play class context.
6
+ #
7
+ # play :foo do
8
+ # # this is evaluated in Play class context
9
+ # end
10
+ #
11
+ class Play
12
+ def self.create(task)
13
+ Class.new(self) do
14
+ include task.application.global_mixin
15
+ extend helpers
16
+ self.task = task
17
+ end
18
+ end
19
+
20
+ include PlayCore
21
+ extend PlayCore::ClassMethods
22
+
23
+ def initialize(params)
24
+ @params = params
25
+ @prerequisite_tasks = []
26
+ end
27
+
28
+ # default settings
29
+ worker :default
30
+ volatile false
31
+ set :concerned, true
32
+ set :needed, true
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,354 @@
1
+ module Abid
2
+ module DSL
3
+ # Common methods for Play and Mixin
4
+ module PlayCore
5
+ attr_reader :prerequisite_tasks
6
+ attr_reader :params
7
+
8
+ # Declared prerequisite tasks.
9
+ #
10
+ # play :foo do
11
+ # setup do
12
+ # needs :TASK_NAME, bar: 0
13
+ # end
14
+ # end
15
+ #
16
+ # @param task_name [Symbol, String] task name
17
+ # @param params [Hash] task params
18
+ def needs(task_name, **params)
19
+ t = task.application[task_name, @scope_in_actions]
20
+ (@prerequisite_tasks ||= []) << [t, self.params.merge(params)]
21
+ end
22
+
23
+ def run
24
+ # noop
25
+ end
26
+
27
+ def task
28
+ self.class.task
29
+ end
30
+
31
+ # Evaluates each actions in the task scope where the action is declared.
32
+ # @param tag [Symbol] action name
33
+ # @param args [Array] arguments
34
+ def call_action(tag, *args)
35
+ self.class.actions[tag].each do |scope, block|
36
+ @scope_in_actions = scope
37
+ instance_exec(*args, &block)
38
+ end
39
+ ensure
40
+ @scope_in_actions = nil
41
+ end
42
+
43
+ # @!visibility private
44
+ def eval_setting(value = nil, &block)
45
+ return instance_exec(&value) if value.is_a? Proc
46
+ return value unless value.nil?
47
+ return instance_exec(&block) if block_given?
48
+ true
49
+ end
50
+ private :eval_setting
51
+
52
+ def logger
53
+ task.application.logger
54
+ end
55
+
56
+ def preview?
57
+ task.application.options.dryrun || task.application.options.preview
58
+ end
59
+
60
+ # Play definition's body is extended by ClassMethods.
61
+ #
62
+ module ClassMethods
63
+ attr_accessor :task
64
+ private :task=
65
+
66
+ # Task params specification.
67
+ def params_spec
68
+ @params_spec ||= ParamsSpec.new(self)
69
+ end
70
+
71
+ # Actions include `setup` blocks and `after` blocks.
72
+ #
73
+ # play :foo do
74
+ # setup { 'this block is added to actions[:setup]' }
75
+ # after { ... }
76
+ # end
77
+ def actions
78
+ @actions ||= Actions.new(self)
79
+ end
80
+
81
+ # Define helper methods.
82
+ #
83
+ # play :foo do
84
+ # helpers do
85
+ # def country
86
+ # :jp
87
+ # end
88
+ # end
89
+ #
90
+ # today #=> :jp
91
+ # end
92
+ #
93
+ # `helpers` block is evaluated in the helpers module context, which
94
+ # extends the play class.
95
+ #
96
+ # If no block given, it returns the helper module.
97
+ #
98
+ # @return [Module] helpers module
99
+ def helpers(*extensions, &block)
100
+ @helpers ||= Module.new
101
+ @helpers.module_eval(&block) if block_given?
102
+ @helpers.module_eval { include(*extensions) } if extensions.any?
103
+ @helpers
104
+ end
105
+
106
+ # Declared setting.
107
+ #
108
+ # play :foo do
109
+ # set :first_name, 'Taro'
110
+ # set :family_name, 'Yamada'
111
+ # set :full_name, -> { first_name + ' ' + family_name }
112
+ #
113
+ # def run
114
+ # full_name #=> 'Taro Yamada'
115
+ # end
116
+ # end
117
+ #
118
+ # Settings are defiend as an intance methods of the play.
119
+ #
120
+ # If a param is declared with the same name of the setting, the param is
121
+ # undefined.
122
+ #
123
+ # mixin :bar do
124
+ # param :country
125
+ # end
126
+ #
127
+ # play :baz do
128
+ # include :bar
129
+ # set :country, :jp
130
+ #
131
+ # params_spec #=> {}
132
+ # end
133
+ #
134
+ # When block is given, it is lazily evaluated in the play context.
135
+ def set(name, value = nil, &block)
136
+ var = :"@#{name}"
137
+
138
+ params_spec.delete(name) # undef param
139
+ define_method(name) do
140
+ unless instance_variable_defined?(var)
141
+ val = eval_setting(value, &block)
142
+ instance_variable_set(var, val)
143
+ end
144
+ instance_variable_get(var)
145
+ end
146
+ end
147
+
148
+ # Declare task param.
149
+ #
150
+ # play :foo do
151
+ # param :city
152
+ # param :country, default: 'Japan'
153
+ #
154
+ # params_spec # => { city: {}, country: { default: 'Japan'} }
155
+ #
156
+ # def run
157
+ # puts "#{city}, #{country}"
158
+ # end
159
+ # end
160
+ #
161
+ # $ abid foo city=Tokyo
162
+ # Tokyo, Japan
163
+ #
164
+ # An instance method of the same name is defined.
165
+ #
166
+ # @param name [Symbol] param name
167
+ # @param spec [Hash] specification
168
+ # @option spec [Object] :default default value
169
+ def param(name, **spec)
170
+ define_method(name) do
171
+ raise NoParamError, "undefined param `#{name}' for #{task.name}" \
172
+ unless params.include?(name)
173
+ params[name]
174
+ end
175
+ params_spec[name] = spec
176
+ end
177
+
178
+ #
179
+ # Setting Helpers
180
+ #
181
+
182
+ # @!visibility private
183
+ def self.def_setting_helper(name)
184
+ class_eval <<-RUBY, __FILE__, __LINE__ + 1
185
+ def #{name}(val = nil, &block)
186
+ set :#{name}, val, &block
187
+ end
188
+ RUBY
189
+ end
190
+
191
+ # @!method worker(val = nil, &block)
192
+ # Set :worker name.
193
+ #
194
+ # play :foo do
195
+ # worker :my_worker
196
+ # action { ... }
197
+ # end
198
+ #
199
+ # This is short-hand style of `set :worker, :my_worker`
200
+ def_setting_helper :worker
201
+
202
+ # @!method volatile(val = nil, &block)
203
+ # Set :volatile flag.
204
+ #
205
+ # play :foo do
206
+ # volatile
207
+ # action { ... }
208
+ # end
209
+ #
210
+ # This is short-hand style of `set :volatile, true`
211
+ def_setting_helper :volatile
212
+
213
+ # Delete the param from params_spec.
214
+ #
215
+ # mixin :bar do
216
+ # param :country
217
+ # end
218
+ #
219
+ # play :baz do
220
+ # include :bar
221
+ #
222
+ # params_spec #=> { country: {} }
223
+ #
224
+ # undef_param :country
225
+ # params_spec #=> {}
226
+ # end
227
+ def undef_param(name)
228
+ params_spec.delete(name)
229
+ end
230
+
231
+ #
232
+ # Actions
233
+ #
234
+
235
+ # @!visibility :private
236
+ def self.define_action(name)
237
+ define_method(name) do |&block|
238
+ actions.add(name, task.scope, block)
239
+ end
240
+ end
241
+
242
+ # @!method setup(&block)
243
+ # Register _setup_ action.
244
+ #
245
+ # Setup action is called before #run.
246
+ # All prerequisites should be declared inside the setup blocks.
247
+ #
248
+ # play :foo do
249
+ # setup do
250
+ # needs :bar
251
+ # puts 'Setup!'
252
+ # end
253
+ #
254
+ # def run
255
+ # puts 'Running!'
256
+ # end
257
+ # end
258
+ #
259
+ # $ abid foo
260
+ # ... (:bar is executed)
261
+ # Setup!
262
+ # Running!
263
+ define_action :setup
264
+
265
+ # @!method action(&block)
266
+ # Register main action.
267
+ #
268
+ # play :foo do
269
+ # action { |args| ... }
270
+ # end
271
+ #
272
+ # `action` block is not executed in dryrun mode nor preview mode.
273
+ #
274
+ # Main actions of mixis are inherited to play, while `run` method is
275
+ # overwritten.
276
+ #
277
+ # @yieldparam args [Rake::TaskArguments]
278
+ define_action :action
279
+
280
+ # @!method safe_action(&block)
281
+ # Register safe action.
282
+ # `safe_action` is similar to `action`, but this block is executed
283
+ # in preview mode.
284
+ #
285
+ # You should guard dangerous operations in a safe_action block.
286
+ # This is useful to preview detail behavior of play.
287
+ #
288
+ # @yieldparam args [Rake::TaskArguments]
289
+ define_action :safe_action
290
+
291
+ # @!method after(&block)
292
+ # Register _after_ action.
293
+ #
294
+ # After action is called after #run.
295
+ #
296
+ # play :foo do
297
+ # def run
298
+ # ...
299
+ # end
300
+ #
301
+ # after do |error|
302
+ # next if error.nil?
303
+ # $syserr.puts "[ERROR] #{task.name} failed:"
304
+ # $syserr.puts "[ERROR] #{error}"
305
+ # end
306
+ # end
307
+ #
308
+ # `after` block is not executed in dryrun mode nor preview mode.
309
+ #
310
+ # @yieldparam error [StandardError, nil] if run method failed,
311
+ # otherwise nil.
312
+ define_action :after
313
+
314
+ # Include mixins.
315
+ #
316
+ # All methods, actions, settings and params_spec are inherited.
317
+ #
318
+ # mixin :foo do
319
+ # param :country
320
+ # end
321
+ #
322
+ # play :bar do
323
+ # include :bar
324
+ # params_spec #=> { country: {} }
325
+ # end
326
+ #
327
+ # When Module objects are given, it includes them as usual.
328
+ #
329
+ # @param mod [Array<Symbol, String, Module>] mixin name or module.
330
+ def include(*mod)
331
+ ms = mod.map { |m| resolve_mixin(m) }
332
+ super(*ms)
333
+ end
334
+ private :include
335
+
336
+ # @!visibility private
337
+ def resolve_mixin(mod)
338
+ return mod if mod.is_a? Module
339
+
340
+ mixin_task = task.application[mod.to_s, task.scope]
341
+ raise "#{mod} is not a mixin" unless mixin_task.is_a? MixinTask
342
+
343
+ mixin_task.internal
344
+ end
345
+ private :resolve_mixin
346
+
347
+ # Return a list of Mixin objects included.
348
+ def superplays
349
+ ancestors.select { |o| o.is_a? PlayCore::ClassMethods }
350
+ end
351
+ end
352
+ end
353
+ end
354
+ end