coral_plan 0.1.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.
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