coral_plan 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc ADDED
@@ -0,0 +1,48 @@
1
+ = coral_plan
2
+
3
+ This library provides the ability to create, load, execute, and save
4
+ execution plans.
5
+
6
+ The Coral Plan library contains a system that defines execution plans of
7
+ CLI commands. The goal is to make it easy to create, edit, load, execute,
8
+ and save these plans in a machine readable format (ie; JSON). The system
9
+ is composed of Events, Commands, Actions, and of course Plans. There are a
10
+ few simple rules:
11
+
12
+ 1. All plans begin with a Command but may have more Commands defined
13
+ that act in sequence unless there are errors with previous commands.
14
+
15
+ 2. Commands can trigger Events of various types, where Events are reading
16
+ and checking (ie; Regular expressions) Command output line by line.
17
+
18
+ 3. Events have different criteria for measuring success and will have
19
+ different fields. For example, a Regexp event contains a 'pattern'
20
+ field.
21
+
22
+ 4. Actions listen for Events and run Commands, with the ability to override
23
+ default Command parameters. Actions may also generate Events on either
24
+ successful execution or failure which can trigger other Actions.
25
+
26
+ Note: This library is still very early in development!
27
+
28
+ == Contributing to coral_plan
29
+
30
+ * Check out the latest master to make sure the feature hasn't been implemented
31
+ or the bug hasn't been fixed yet.
32
+ * Check out the issue tracker to make sure someone already hasn't requested
33
+ it and/or contributed it.
34
+ * Fork the project.
35
+ * Start a feature/bugfix branch.
36
+ * Commit and push until you are happy with your contribution.
37
+ * Make sure to add tests for it. This is important so I don't break it in a
38
+ future version unintentionally.
39
+ * Please try not to mess with the Rakefile, version, or history. If you want
40
+ to have your own version, or is otherwise necessary, that is fine, but
41
+ please isolate to its own commit so I can cherry-pick around it.
42
+
43
+ == Copyright
44
+
45
+ Licensed under GPLv3. See LICENSE.txt for further details.
46
+
47
+ Copyright (c) 2013 Adrian Webb <adrian.webb@coraltech.net>
48
+ Coral Technology Group LLC
data/Rakefile ADDED
@@ -0,0 +1,68 @@
1
+ # encoding: utf-8
2
+
3
+ require 'rubygems'
4
+ require 'bundler'
5
+ require 'rake'
6
+ require 'rake/testtask'
7
+ require 'rdoc/task'
8
+ require 'jeweler'
9
+
10
+ require './lib/coral_plan.rb'
11
+
12
+ #-------------------------------------------------------------------------------
13
+ # Dependencies
14
+
15
+ begin
16
+ Bundler.setup(:default, :development)
17
+ rescue Bundler::BundlerError => e
18
+ $stderr.puts e.message
19
+ $stderr.puts "Run `bundle install` to install missing gems"
20
+ exit e.status_code
21
+ end
22
+
23
+ #-------------------------------------------------------------------------------
24
+ # Gem specification
25
+
26
+ Jeweler::Tasks.new do |gem|
27
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
28
+ gem.name = "coral_plan"
29
+ gem.homepage = "http://github.com/coraltech/ruby-coral_plan"
30
+ gem.rubyforge_project = 'coral_plan'
31
+ gem.license = "GPLv3"
32
+ gem.email = "adrian.webb@coraltech.net"
33
+ gem.authors = ["Adrian Webb"]
34
+ gem.summary = %Q{Provides the ability to create, load, execute, and save execution plans}
35
+ gem.description = File.read('README.rdoc')
36
+ gem.required_ruby_version = '>= 1.8.1'
37
+ gem.has_rdoc = true
38
+ gem.rdoc_options << '--title' << 'Coral Execution Plan library' <<
39
+ '--main' << 'README.rdoc' <<
40
+ '--line-numbers'
41
+
42
+ # Dependencies defined in Gemfile
43
+ end
44
+
45
+ Jeweler::RubygemsDotOrgTasks.new
46
+
47
+ #-------------------------------------------------------------------------------
48
+ # Testing
49
+
50
+ Rake::TestTask.new(:test) do |test|
51
+ test.libs << 'lib' << 'test'
52
+ test.pattern = 'test/**/test_*.rb'
53
+ test.verbose = true
54
+ end
55
+
56
+ task :default => :test
57
+
58
+ #-------------------------------------------------------------------------------
59
+ # Documentation
60
+
61
+ Rake::RDocTask.new do |rdoc|
62
+ version = Coral::Plan::VERSION
63
+
64
+ rdoc.rdoc_dir = 'rdoc'
65
+ rdoc.title = "coral_plan #{version}"
66
+ rdoc.rdoc_files.include('README*')
67
+ rdoc.rdoc_files.include('lib/**/*.rb')
68
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.1
@@ -0,0 +1,167 @@
1
+
2
+ module Coral
3
+ module Plan
4
+ class Action < Core
5
+
6
+ #-----------------------------------------------------------------------------
7
+ # Constructor / Destructor
8
+
9
+ def initialize(options = {})
10
+ super(options)
11
+ @plan = ( options.has_key?(:plan) ? options[:plan] : nil )
12
+ @name = ( options.has_key?(:name) ? string(options[:name]) : '' )
13
+ end
14
+
15
+ #-----------------------------------------------------------------------------
16
+ # Property accessors / modifiers
17
+
18
+ attr_accessor :name
19
+
20
+ #---
21
+
22
+ def events
23
+ events = {}
24
+ @plan.action(@name, 'events', [], :array).each do |name|
25
+ events[name] = @plan.events[name]
26
+ end
27
+ return events
28
+ end
29
+
30
+ #---
31
+
32
+ def events=events
33
+ @plan.set_action(@name, "events", events)
34
+ end
35
+
36
+ #---
37
+
38
+ def commands
39
+ commands = []
40
+ @plan.action(@name, 'commands', [], :array).each do |name|
41
+ commands << @plan.commands[name]
42
+ end
43
+ return commands
44
+ end
45
+
46
+ #---
47
+
48
+ def commands=commands
49
+ @plan.set_action(@name, "commands", commands)
50
+ end
51
+
52
+ #---
53
+
54
+ def args
55
+ return search('args', :array)
56
+ end
57
+
58
+ #---
59
+
60
+ def args=args
61
+ @plan.set_action(@name, "args", args)
62
+ end
63
+
64
+ #---
65
+
66
+ def flags
67
+ return search('flags', :array)
68
+ end
69
+
70
+ #---
71
+
72
+ def flags=flags
73
+ @plan.set_action(@name, "flags", flags)
74
+ end
75
+
76
+ #---
77
+
78
+ def data
79
+ return search('data', :hash)
80
+ end
81
+
82
+ #---
83
+
84
+ def data=data
85
+ @plan.set_action(@name, "data", data)
86
+ end
87
+
88
+ #---
89
+
90
+ def next
91
+ commands = []
92
+ @plan.action(@name, 'next', [], :array).each do |name|
93
+ commands << @plan.commands[name]
94
+ end
95
+ return commands
96
+ end
97
+
98
+ #---
99
+
100
+ def next=commands
101
+ @plan.set_action(@name, "next", commands)
102
+ end
103
+
104
+ #---
105
+
106
+ def trigger_success
107
+ return @plan.action('trigger_success', :array)
108
+ end
109
+
110
+ #---
111
+
112
+ def trigger_success=event_names
113
+ @plan.set_action(@name, "trigger_success", array(event_names))
114
+ end
115
+
116
+ #---
117
+
118
+ def trigger_failure
119
+ return @plan.action('trigger_failure', :array)
120
+ end
121
+
122
+ #---
123
+
124
+ def trigger_failure=event_names
125
+ @plan.set_action(@name, "trigger_failure", array(event_names))
126
+ end
127
+
128
+ #-----------------------------------------------------------------------------
129
+ # Import / Export
130
+
131
+ def export
132
+ return symbol_map(@plan.action(@name))
133
+ end
134
+
135
+ #-----------------------------------------------------------------------------
136
+ # Utilities
137
+
138
+ def search(key, format = :array)
139
+ action_info = @plan.action(@name)
140
+ action_value = map(action_info[key])
141
+
142
+ merged_value = {}
143
+
144
+ commands.each do |command|
145
+ command_info = command.export
146
+ command_value = ( command_info[key] ? { command.name => filter(command_info[key], format) } : {} )
147
+
148
+ merged_value[name] = Coral::Util::Data.merge([ action_value, command_value ], true)
149
+ end
150
+ return merged_value
151
+ end
152
+
153
+ #---
154
+
155
+ def map(data)
156
+ if data && ! data.is_a?(Hash)
157
+ data_map = {}
158
+ commands.each do |command|
159
+ data_map[command.name] = array(data)
160
+ end
161
+ data = data_map
162
+ end
163
+ return data
164
+ end
165
+ end
166
+ end
167
+ end
@@ -0,0 +1,263 @@
1
+
2
+ module Coral
3
+ module Plan
4
+
5
+ #*******************************************************************************
6
+ # Errors
7
+
8
+ class ExecuteError < StandardError
9
+ end
10
+
11
+ #*******************************************************************************
12
+ # Base Plan definition
13
+
14
+ class Base < Memory
15
+
16
+ #-----------------------------------------------------------------------------
17
+ # Properties
18
+
19
+ @@instances = {}
20
+
21
+ #-----------------------------------------------------------------------------
22
+ # Constructor / Destructor
23
+
24
+ def self.create(name, options = {})
25
+ options[:name] = name unless options.has_key?(:name)
26
+ @@instances[name] = new(options)
27
+ return @@instances[name]
28
+ end
29
+
30
+ #---
31
+
32
+ def self.delete(name)
33
+ if @@instances.has_key?(name) && @@instances[name]
34
+ @@instances.delete(name)
35
+ end
36
+ end
37
+
38
+ #-----------------------------------------------------------------------------
39
+
40
+ def self.[](name)
41
+ if ! @@instances.has_key?(name) || ! @@instances[name]
42
+ @@instances[name] = new({ :name => name })
43
+ end
44
+ return @@instances[name]
45
+ end
46
+
47
+ #-----------------------------------------------------------------------------
48
+
49
+ def initialize(options = {})
50
+ super(options)
51
+
52
+ @home = ( options.has_key?(:home) && options[:home].is_a?(Coral::Repository) ? options[:home] : self )
53
+
54
+ @start_commands = []
55
+ @commands = {}
56
+ @events = {}
57
+ @actions = {}
58
+ end
59
+
60
+ #-----------------------------------------------------------------------------
61
+ # Property accessors / modifiers
62
+
63
+ attr_reader :home
64
+
65
+ #---
66
+
67
+ def home=home
68
+ if home && home.is_a?(Coral::Repository)
69
+ @home = home
70
+ set_repository(@home.directory, @submodule)
71
+ end
72
+ end
73
+
74
+ #---
75
+
76
+ def submodule=submodule
77
+ set_repository(@home.directory, submodule)
78
+ end
79
+
80
+ #-----------------------------------------------------------------------------
81
+
82
+ def start(reset = false)
83
+ if reset || @start_commands.empty?
84
+ @start_commands = []
85
+ get('start', [], :array).each do |name|
86
+ @start_commands << commands[name]
87
+ end
88
+ end
89
+ return @start_commands
90
+ end
91
+
92
+ #-----------------------------------------------------------------------------
93
+
94
+ def commands(reset = false)
95
+ if reset || @commands.empty?
96
+ @commands = {}
97
+ get('commands', {}, :hash).each do |name, command_info|
98
+ command_info = Coral::Util::Data.merge([ {
99
+ :plan => self,
100
+ :name => name,
101
+ }, symbol_map(command_info) ])
102
+
103
+ @commands[name] = Coral::Plan::Command.new(command_info)
104
+ end
105
+ end
106
+ return @commands
107
+ end
108
+
109
+ #---
110
+
111
+ def set_commands(commands = {})
112
+ return set('commands', events)
113
+ end
114
+
115
+ #---
116
+
117
+ def command(name, key = nil, default = {}, format = false)
118
+ return get_group('commands', name, key, default, format)
119
+ end
120
+
121
+ #---
122
+
123
+ def set_command(name, key = nil, value = {})
124
+ return set_group('commands', name, key, value)
125
+ end
126
+
127
+ #---
128
+
129
+ def delete_command(name, key = nil)
130
+ return delete_group('commands', name, key)
131
+ end
132
+
133
+ #-----------------------------------------------------------------------------
134
+
135
+ def events(reset = false)
136
+ if reset || @events.empty?
137
+ @events = {}
138
+ get('events', {}, :hash).each do |name, event_info|
139
+ event_info = Coral::Util::Data.merge([ {
140
+ :plan => self,
141
+ :name => name,
142
+ }, symbol_map(event_info) ])
143
+
144
+ @events[name] = Coral::Plan::Event.instance(event_info)
145
+ end
146
+ end
147
+ return @events
148
+ end
149
+
150
+ #---
151
+
152
+ def set_events(events = {})
153
+ return set('events', events)
154
+ end
155
+
156
+ #---
157
+
158
+ def event(name, key = nil, default = {}, format = false)
159
+ return get_group('events', name, key, default, format)
160
+ end
161
+
162
+ #---
163
+
164
+ def set_event(name, key = nil, value = {})
165
+ return set_group('events', name, key, value)
166
+ end
167
+
168
+ #---
169
+
170
+ def delete_event(name, key = nil)
171
+ return delete_group('events', name, key)
172
+ end
173
+
174
+ #-----------------------------------------------------------------------------
175
+
176
+ def actions(reset = false)
177
+ if reset || @actions.empty?
178
+ @actions = {}
179
+ get('actions', {}, :hash).each do |name, action_info|
180
+ action_info = Coral::Util::Data.merge([ {
181
+ :plan => self,
182
+ :name => name,
183
+ }, symbol_map(action_info) ])
184
+
185
+ @actions[name] = Coral::Plan::Action.new(action_info)
186
+ end
187
+ end
188
+ return @actions
189
+ end
190
+
191
+ #---
192
+
193
+ def set_actions(actions = {})
194
+ return set('actions', actions)
195
+ end
196
+
197
+ #---
198
+
199
+ def action(name, key = nil, default = {}, format = false)
200
+ return get_group('actions', name, key, default, format)
201
+ end
202
+
203
+ #---
204
+
205
+ def set_action(name, key = nil, value = {})
206
+ return set_group('actions', name, key, value)
207
+ end
208
+
209
+ #---
210
+
211
+ def delete_action(name, key = nil)
212
+ return delete_group('actions', name, key)
213
+ end
214
+
215
+ #-----------------------------------------------------------------------------
216
+ # Execution
217
+
218
+ def run(options)
219
+ success = true
220
+ start.each do |command|
221
+ success = recursive_exec(command, options)
222
+ break unless success
223
+ end
224
+ return success
225
+ end
226
+
227
+ #---
228
+
229
+ def recursive_exec(command, options = {}, parent_action = nil)
230
+ success = false
231
+
232
+ options[:info_prefix] = 'info' unless options.has_key?(:info_prefix)
233
+ options[:error_prefix] = 'error' unless options.has_key?(:error_prefix)
234
+
235
+ begin
236
+ triggered_events = command.exec(options, parent_action)
237
+ rescue
238
+ raise Coral::Plan::ExecuteError.new(command.name)
239
+ end
240
+
241
+ if triggered_events
242
+ success = true
243
+
244
+ unless triggered_events.empty?
245
+ triggered_events.each do |event_name, event|
246
+ actions.each do |action_name, action|
247
+ if action.events.has_key?(event_name)
248
+ action.commands.each do |subcommand|
249
+ success = recursive_exec(subcommand, options, action)
250
+ break unless success
251
+ end
252
+ end
253
+ break unless success
254
+ end
255
+ break unless success
256
+ end
257
+ end
258
+ end
259
+ return success
260
+ end
261
+ end
262
+ end
263
+ end