detroit 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (51) hide show
  1. data/.ruby +45 -0
  2. data/COPYING.rdoc +19 -0
  3. data/EXAMPLE.md +188 -0
  4. data/GPL3.txt +675 -0
  5. data/HISTORY.rdoc +14 -0
  6. data/README.rdoc +139 -0
  7. data/bin/detroit +9 -0
  8. data/lib/detroit.rb +67 -0
  9. data/lib/detroit.yml +45 -0
  10. data/lib/detroit/application.rb +427 -0
  11. data/lib/detroit/assembly.rb +80 -0
  12. data/lib/detroit/config.rb +197 -0
  13. data/lib/detroit/control.rb +124 -0
  14. data/lib/detroit/core_ext.rb +139 -0
  15. data/lib/detroit/custom.rb +65 -0
  16. data/lib/detroit/dsl.rb +55 -0
  17. data/lib/detroit/schedule.rb +187 -0
  18. data/lib/detroit/service.rb +188 -0
  19. data/lib/detroit/standard_assembly.rb +52 -0
  20. data/lib/detroit/tool.rb +216 -0
  21. data/lib/detroit/tool/core_ext.rb +3 -0
  22. data/lib/detroit/tool/core_ext/facets.rb +11 -0
  23. data/lib/detroit/tool/core_ext/filetest.rb +29 -0
  24. data/lib/detroit/tool/core_ext/shell_extensions.rb +7 -0
  25. data/lib/detroit/tool/core_ext/to_actual_filename.rb +19 -0
  26. data/lib/detroit/tool/core_ext/to_console.rb +97 -0
  27. data/lib/detroit/tool/core_ext/to_list.rb +29 -0
  28. data/lib/detroit/tool/core_ext/to_yamlfrag.rb +9 -0
  29. data/lib/detroit/tool/core_ext/unfold_paragraphs.rb +27 -0
  30. data/lib/detroit/tool/email_utils.rb +288 -0
  31. data/lib/detroit/tool/project_utils.rb +41 -0
  32. data/lib/detroit/tool/shell_utils.rb +235 -0
  33. data/qed/01_schedule/02_initialize.md +57 -0
  34. data/qed/99_plugins/rdoc/rdoc-plugin.rdoc +22 -0
  35. data/qed/99_plugins/rdoc/sample/Syckfile +6 -0
  36. data/qed/99_plugins/rdoc/sample/lib/sandbox/.xxx +1 -0
  37. data/qed/99_plugins/rdoc/sample/lib/sandbox/hello.rb +5 -0
  38. data/qed/99_plugins/rdoc/sample/lib/sandbox/xxx.rb +6 -0
  39. data/qed/99_plugins/rdoc/sample/lib/xxx/bye.rb +4 -0
  40. data/qed/99_plugins/rdoc/sample/meta/name +1 -0
  41. data/qed/99_plugins/rdoc/sample/meta/version +1 -0
  42. data/qed/samples/example_project/.ruby +0 -0
  43. data/qed/samples/example_project/Schedule +9 -0
  44. data/qed/samples/example_project/lib/foo/.xxx +1 -0
  45. data/qed/samples/example_project/lib/foo/hello.rb +7 -0
  46. data/qed/samples/example_project/lib/foo/xxx.rb +6 -0
  47. data/qed/samples/example_project/lib/foo/xxx/bye.rb +4 -0
  48. data/qed/samples/example_project/meta/name +1 -0
  49. data/qed/samples/example_project/meta/version +1 -0
  50. data/qed/samples/example_schedule.rb +57 -0
  51. metadata +139 -0
@@ -0,0 +1,14 @@
1
+ = RELEASE HISTORY
2
+
3
+ == 0.1.0 / 2011-06-29
4
+
5
+ Detroit is a lifecycle build system for Ruby. Detroit was originally
6
+ called Syckle, and was developed and used in house for Rubyworks projects
7
+ for several years. With the renaming of the project, the system has
8
+ been simplified, the code cleaned up and the version count reset, in
9
+ preperation of it's wider public release.
10
+
11
+ Changes:
12
+
13
+ * Happy Release Day!
14
+
@@ -0,0 +1,139 @@
1
+ = Detroit
2
+
3
+ Author:: Thomas Sawyer
4
+ Licence:: GPL v.3
5
+ Copyright:: (c) 2011 Rubyworks, Thomas Sawyer
6
+
7
+
8
+ == DESCRIPTION
9
+
10
+ Detroit is a software production management aid for Ruby developers.
11
+ Detroit utilizes a lifecycle methodology to help developers prepare and
12
+ release Ruby software in a clear, repeatable, linear fashion.
13
+
14
+
15
+ == RESOURCES
16
+
17
+ * {Homepage}[http://rubyworks.github.com/detroit]
18
+ * {Development}[http://github.com/rubyworks/detroit]
19
+ * {Mailing List}[http://googlegroups.com/group/rubyworks-mailinglist]
20
+
21
+
22
+ == HOW IT WORKS
23
+
24
+ Detroit defines development processions which consist of a set of named
25
+ production _lines_, or _tracks_, each with a series of named _stations_,
26
+ or _stops_. Developers attach work elements to stations by configuring
27
+ service instances in a project's Schedule or *.schedule files. Schedules
28
+ are written in either YAML or a Ruby DSL.
29
+
30
+ For example, a RubyForge service can be defined:
31
+
32
+ rubyforge:
33
+ service: Rubyforge
34
+ sitemap:
35
+ site: <%= name %>
36
+ active: true
37
+
38
+ As this example demonstrates, service configurations can draw on project
39
+ metadata via ERB embedded tags. Detroit gathers this information using
40
+ {.ruby}[http://dotruby.github.com/dotruby], but the data source can be
41
+ easily customized to meet the needs of different projects.
42
+
43
+ With service configuration and metadata in place, using Detroit is simply
44
+ a matter of passing a line name and stop to the +detroit+ command line
45
+ tool. For example,
46
+
47
+ $ detroit main:document
48
+
49
+ The track name and its stop are separated by a colon. This command
50
+ would run every stop on the +main+ track, in order, until it completes
51
+ the +document+ stop. Since +main+ is the default track, we can acheive
52
+ the same effect without specifying it.
53
+
54
+ $ detroit document
55
+
56
+ The use of tracks may seem constrictive to users of tools like Rake, but
57
+ there is a benefit to this approach. It helps ensure a project is
58
+ always up-to-date and in-sync --that no necessary steps are missed.
59
+ Detroit includes three tracks out of the box. The most significant of
60
+ which is +main+ which entails a route with ordred stops:
61
+
62
+ prepare # prepare services and ensure requirements
63
+ generate # code generation
64
+ compile # compile source code
65
+ test # run tests and/or specifications
66
+ analyze # run code analysis
67
+ document # generate documentation
68
+ package # create packages
69
+ verify # post package verification (eg. integration tests)
70
+ install # install package locally
71
+ publish # publish website/documentation
72
+ release # release packages
73
+ deploy # deply system to servers
74
+ promote # tell the world about you awesome work
75
+
76
+ All tracks also have a maintainence subtrack which consits of three stops:
77
+
78
+ reset # mark build files as out-of-date
79
+ clean # remove minor build files
80
+ purge # remove all build files
81
+
82
+ Where reset marks generated files out-of-date, clean removes temporary
83
+ products and purge removes all generated prodcuts.
84
+
85
+ In additon to +main+, Detroit includes +site+ and +attn+ tracks which are used
86
+ to generate and publish a project's website, and make project announcements
87
+ respectively. They are simply useful subsets of the +main+ track.
88
+
89
+ Please see http://rubyworks.github.com/detroit for more details on how to
90
+ use Detroit, including the creation of custom tracks, stops and service plugins.
91
+ Also try the <tt>--help</tt> option to see the detroit command's help
92
+ information.
93
+
94
+
95
+ == INSTALLATION
96
+
97
+ Detroit can, of course, be installed via RubyGems:
98
+
99
+ $ gem install detroit
100
+
101
+ We no longer recommend it, but Detroit can also be installed the
102
+ old-fashion way by downloading the .tar.gz package and using
103
+ Ruby Setup (See http://setup.rubyforge.org).
104
+
105
+ $ tar -xvzf detroit-1.0.0.tar.gz
106
+ $ cd detroit-1.0.0
107
+ $ sudo setup.rb
108
+
109
+ Ruby Setup is stand-alone version of the original setup.rb script.
110
+
111
+
112
+ == ISSUES
113
+
114
+ All in all, Detroit works well. There are some rough edges with regards
115
+ to the built-in service plugins, so from time to time you might run into
116
+ an odd error. Ususally it just means a service confirguraiton needs
117
+ adjustment.
118
+
119
+ Please note, Windows support has not been considered at all. While I do
120
+ not see any specific reason it should not work, there may well be issues
121
+ I have not considered since I do not use Windows. If you are Windows user
122
+ and give Detroit a try please let us know of any issues you encounter.
123
+
124
+
125
+ == HISTORY
126
+
127
+ Detroit is actaully the offspring of Reap v.10, and was called Syckle for
128
+ a number of years as it matured. It represents many years of design considerations
129
+ (and reconsiderations), that evolved Reap from its simple Rake extension origins,
130
+ which pre-dated Hoe, to the lifecycle system it is today.
131
+
132
+
133
+ == COPYRIGHT & LICENSE
134
+
135
+ (GPL v3 License)
136
+
137
+ Copyright (c) 2007 Thomas Sawyer
138
+
139
+ See COPYING.rdoc and GPL3.txt for details.
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ require 'detroit'
3
+ begin
4
+ Detroit.cli(*ARGV)
5
+ rescue => err
6
+ raise err #if $DEBUG
7
+ $stderr.puts err
8
+ end
9
+
@@ -0,0 +1,67 @@
1
+ module Detroit
2
+ # Access to this project's metadata.
3
+ def self.metadata
4
+ @metadata ||= (
5
+ require 'yaml'
6
+ YAML.load(File.new(File.dirname(__FILE__) + '/detroit.yml'))
7
+ )
8
+ end
9
+
10
+ # Access to project metadata via constants.
11
+ def self.const_missing(name)
12
+ metadata[name.to_s.downcase] || super(name)
13
+ end
14
+
15
+ # TODO: Only here b/c of bug in Ruby 1.8.x
16
+ #VERSION = "0.1.0"
17
+ end
18
+
19
+ # Erb is used to to script YAML-based schedule files.
20
+ require 'erb'
21
+
22
+ # OptionParser is used for command line parsing.
23
+ require 'optparse'
24
+
25
+ # Yea like we don't all ride a YAML.
26
+ require 'yaml'
27
+
28
+ # The ANSI gem is used to colorize terminal output.
29
+ require 'ansi/terminal'
30
+ require 'ansi/code'
31
+
32
+ # The parallel gem is used to (optionally) to multitask services.
33
+ begin
34
+ require 'parallel'
35
+ rescue LoadError
36
+ end
37
+
38
+ # POM is used to access project metadata.
39
+ require 'pom'
40
+
41
+ # Redtools provides the standard services.
42
+ require 'redtools'
43
+
44
+ # And all the rest is Detroit, baby.
45
+ if RUBY_VERSION > '1.9'
46
+ require_relative 'detroit/core_ext'
47
+ require_relative 'detroit/config'
48
+ require_relative 'detroit/service'
49
+ require_relative 'detroit/tool'
50
+ require_relative 'detroit/assembly'
51
+ require_relative 'detroit/standard_assembly'
52
+ require_relative 'detroit/control'
53
+ require_relative 'detroit/application'
54
+ require_relative 'detroit/schedule'
55
+ require_relative 'detroit/custom'
56
+ else
57
+ require 'detroit/core_ext'
58
+ require 'detroit/config'
59
+ require 'detroit/service'
60
+ require 'detroit/tool'
61
+ require 'detroit/assembly'
62
+ require 'detroit/standard_assembly'
63
+ require 'detroit/control'
64
+ require 'detroit/application'
65
+ require 'detroit/schedule'
66
+ require 'detroit/custom'
67
+ end
@@ -0,0 +1,45 @@
1
+ ---
2
+ name: detroit
3
+ version: 0.1.0
4
+ title: Detroit
5
+ summary: Software Production Mangement
6
+ description: Detroit is an advanced lifecycle build system. With Detroit, build tasks are user defined service instances tied to stops along a track. Whenever the detroit console command is run, a track is followed from beginning to designated destination.
7
+ loadpath:
8
+ - lib
9
+ manifest: Manifest
10
+ requires:
11
+ - name: facets
12
+ version: 0+
13
+ group: []
14
+
15
+ - name: pom
16
+ version: 0+
17
+ group: []
18
+
19
+ - name: qed
20
+ version: 0+
21
+ group:
22
+ - test
23
+ conflicts: []
24
+
25
+ replaces: []
26
+
27
+ engine_check: []
28
+
29
+ organization: Rubyworks
30
+ contact: Trans <transfire@gmail.com>
31
+ created: 2007-10-10
32
+ copyright: Copyright (c) 2007 Thomas Sawyer
33
+ licenses:
34
+ - GPL3
35
+ authors:
36
+ - Thomas Sawyer
37
+ maintainers: []
38
+
39
+ resources:
40
+ home: http://rubyworks.github.com/detroit
41
+ code: http://github.com/rubyworks/detroit
42
+ mail: http://groups.google.com/rubyworks-mailinglist
43
+ repositories:
44
+ public: http://github.com/proutils/detroit.git
45
+ spec_version: 1.0.0
@@ -0,0 +1,427 @@
1
+ module Detroit
2
+
3
+ #
4
+ DEFAULT_ASSEMBLY = :standard
5
+
6
+ # Application class is the main controller class for running
7
+ # a session of Detroit.
8
+ #--
9
+ # TODO: Rename Application to `Session`?
10
+ #++
11
+ class Application
12
+
13
+ # Options (generally from #cli).
14
+ attr :options
15
+
16
+ # Create a new Detroit Application instance.
17
+ def initialize(options)
18
+ @options = options
19
+ #load_standard_plugins
20
+ end
21
+
22
+ # # Load standard plugins.
23
+ # def load_standard_plugins
24
+ # #::Plugin.find("detroit/*.rb").each do |file|
25
+ # Detroit.standard_plugins.each do |file|
26
+ # begin
27
+ # require(file)
28
+ # rescue => err
29
+ # $stderr.puts err if $DEBUG
30
+ # end
31
+ # end
32
+ # end
33
+
34
+ # The selected assembly system.
35
+ def assembly
36
+ options[:assembly] || DEFAULT_ASSEMBLY
37
+ end
38
+
39
+ # Quiet mode?
40
+ def quiet?
41
+ options[:quiet]
42
+ end
43
+
44
+ # Multitask mode?
45
+ def multitask?
46
+ options[:multitask] && defined?(Parallel)
47
+ end
48
+
49
+ # Returns a list of services to skip as specificed on the commandline.
50
+ def skip
51
+ @skip ||= options[:skip].to_list.map{ |s| s.downcase }
52
+ end
53
+
54
+ #
55
+ def schedules
56
+ @schedules ||= options[:schedules]
57
+ end
58
+
59
+ # Detroit configuration.
60
+ def config
61
+ @config ||= Detroit::Config.new(schedules)
62
+ end
63
+
64
+ # Provides access to the Project instance via `Detroit.project` class method.
65
+ def project
66
+ @project ||= POM::Project.find
67
+ end
68
+
69
+ # User-defined service defaults.
70
+ #
71
+ # Returns Hash of service defaults.
72
+ def defaults
73
+ config.defaults
74
+ end
75
+
76
+ # Generates a configuration template for particular tool or all tools.
77
+ # This is only used for reference purposes.
78
+ def config_template(name=nil)
79
+ if name
80
+ list = [name, Detroit.tools[name]]
81
+ else
82
+ list = Detroit.tools
83
+ end
84
+ cfg = {}
85
+ list.each do |srv_name, srv_class|
86
+ attrs = srv_class.options #instance_methods.select{ |m| m.to_s =~ /\w+=$/ && !%w{taguri=}.include?(m.to_s) }
87
+ atcfg = attrs.inject({}){ |h, m| h[m.to_s.chomp('=')] = nil; h }
88
+ atcfg['service'] = srv_class.basename.downcase
89
+ atcfg['active'] = false
90
+ cfg[srv_name] = atcfg
91
+ end
92
+ cfg
93
+ end
94
+
95
+ # Active services are services defined in schedule files and do not
96
+ # have their active setting turned off.
97
+ #
98
+ # Returns Array of active services.
99
+ def active_services
100
+ @active_services ||= (
101
+ activelist = []
102
+
103
+ config.each do |key, opts|
104
+ next unless opts && opts['active'] != false
105
+
106
+ # omit any service in the skip list
107
+ next if skip.include?(key.to_s)
108
+
109
+ tool_name = (opts.delete('tool') || opts.delete('service') || key).to_s.downcase
110
+
111
+ unless Detroit.tools.key?(tool_name)
112
+ config.load_plugin(tool_name)
113
+ end
114
+
115
+ tool_class = Detroit.tools[tool_name]
116
+
117
+ abort "Unknown tool `#{tool_name}'." unless tool_class
118
+
119
+ if tool_class.available? #(project)
120
+ #opts = inject_environment(opts) # TODO: DEPRECATE
121
+ options = defaults[tool_name.downcase].to_h
122
+ options = options.merge(common_tool_options)
123
+ options = options.merge(opts)
124
+ #activelist << tool_class.new(key, options) #script,
125
+ ## remove any services specified by the --skip option on the comamndline
126
+ activelist << ServiceWrapper.new(key, tool_class, options) #script,
127
+ #else
128
+ # warn "Service #{tool_class} is not available."
129
+ end
130
+ end
131
+
132
+ # sorting here trickles down to processing later
133
+ activelist = activelist.sort_by{ |s| s.priority || 0 }
134
+
135
+ activelist
136
+ )
137
+ end
138
+
139
+ #alias_method :services, :active_services
140
+
141
+ # Service configuration, from project's schedule file(s).
142
+ #
143
+ # Returns Hash of service name and settings.
144
+ #def service_configs
145
+ # config.services
146
+ #end
147
+
148
+ # Run individual detroit scripts.
149
+ #def runscript(script, stop)
150
+ # @config.services.clear
151
+ # @config.load_schedule_file(script)
152
+ # #@service_configs = load_service_configs(script)
153
+ # run(stop)
154
+ #end
155
+
156
+ # Start the run.
157
+ def start(stop)
158
+ Dir.chdir(project.root) do # change into project directory
159
+ run(stop)
160
+ end
161
+ end
162
+
163
+ # Run up to the specified +track_and_stop+.
164
+ def run(track_and_stop)
165
+ raise "Malformed destination -- #{track_and_stop}" unless /^\w+\:{0,1}\w+$/ =~ track_and_stop
166
+
167
+ if track_and_stop
168
+ name, stop = track_and_stop.split(':')
169
+ name, stop = 'main', name unless stop
170
+ else
171
+ name = 'main'
172
+ stop = nil
173
+ end
174
+
175
+ name = name.to_sym
176
+ stop = stop.to_sym if stop
177
+
178
+ assm = Detroit.assemblies[assembly]
179
+
180
+ raise "Unkown assembly `#{assembly}'" unless assm
181
+
182
+ track = assm.get_track(name, stop)
183
+
184
+ #if stop
185
+ # system = track.route_with_stop(stop)
186
+ # raise "Unknown stop -- #{stop}" unless system
187
+
188
+ if not track.include?(stop)
189
+ #overview
190
+ $stderr.puts "Unknown stop for track `#{name}'."
191
+ exit 0
192
+ end
193
+
194
+ # prime the services (so as to fail early)
195
+ active_services.each do |srv|
196
+ srv.preconfigure if srv.respond_to?("preconfigure")
197
+ end
198
+
199
+ if multitask?
200
+ h = ["#{project.metadata.title} v#{project.metadata.version} [M]", "#{project.root}"]
201
+ else
202
+ h = ["#{project.metadata.title} v#{project.metadata.version}", "#{project.root}"]
203
+ end
204
+ status_header(*h)
205
+
206
+ start_time = Time.now
207
+
208
+ track.each do |run_stop|
209
+ next if skip.include?("#{run_stop}") # TODO: Should we really allow skipping stops?
210
+ service_hooks(name, ('pre_' + run_stop.to_s).to_sym)
211
+ service_calls(name, ('pre_' + run_stop.to_s).to_sym)
212
+ service_calls(name, run_stop)
213
+ service_calls(name, ('aft_' + run_stop.to_s).to_sym)
214
+ service_hooks(name, ('aft_' + run_stop.to_s).to_sym)
215
+ break if stop == run_stop
216
+ end
217
+
218
+ stop_time = Time.now
219
+ puts "\nFinished in #{stop_time - start_time} seconds." unless quiet?
220
+ end
221
+
222
+ # Execute service hook for given track and destination.
223
+ #--
224
+ # TODO: Deprecate service hooks?
225
+ #
226
+ # TODO: Currently only stop counts, maybe add track subdirs.
227
+ #++
228
+ def service_hooks(track, stop)
229
+ #hook = dir + ("#{track}/#{stop}.rb".gsub('_', '-'))
230
+ dir = hook_directory
231
+ return unless dir
232
+ name = stop.to_s.gsub('_', '-')
233
+ hook = dir + "#{name}.rb"
234
+ if hook.exist?
235
+ status_line("hook", name.capitalize)
236
+ hook_tool.instance_eval(hook.read)
237
+ end
238
+ end
239
+
240
+ # Returns a project's Detroit hooks directory.
241
+ def hook_directory
242
+ dir = project.root.glob("{.,}detroit/hooks").first
243
+ end
244
+
245
+ #
246
+ def hook_tool
247
+ @hook_tool ||= Tool.new(common_tool_options)
248
+ end
249
+
250
+ # TODO: Do we need verbose?
251
+ def common_tool_options
252
+ {
253
+ 'project' => project,
254
+ 'trial' => options[:trial],
255
+ 'trace' => options[:trace],
256
+ 'quiet' => options[:quiet],
257
+ 'force' => options[:force],
258
+ 'verbose' => options[:verbose]
259
+ }
260
+ end
261
+
262
+ # Make service calls.
263
+ def service_calls(track, stop)
264
+ prioritized_services = active_services.group_by{ |srv| srv.priority }.sort_by{ |k,v| k }
265
+ prioritized_services.each do |(priority, services)|
266
+ ## remove any services specified by the --skip option on the comamndline
267
+ #services = services.reject{ |srv| skip.include?(srv.key.to_s) }
268
+ ## only servies that are on the track
269
+ services = services.select{ |srv| srv.tracks.nil? or srv.tracks.include?(track.to_s) }
270
+
271
+ tasklist = services.map{ |srv| [srv, track, stop] }
272
+ if multitask?
273
+ results = Parallel.in_processes(tasklist.size) do |i|
274
+ run_a_service(*tasklist[i])
275
+ end
276
+ else
277
+ tasklist.each do |args|
278
+ run_a_service(*args)
279
+ end
280
+ end
281
+ end
282
+ end
283
+
284
+ # Run a service given the service, track name and stop name.
285
+ def run_a_service(srv, track, stop)
286
+ # run if the service supports the track and stop.
287
+ #if srv.respond_to?("#{track}_#{stop}")
288
+ if srv.stop?(stop)
289
+ if options[:verbose]
290
+ #status_line("#{srv.key.to_s} (#{srv.class}##{track}_#{stop})", stop.to_s.gsub('_', '-').capitalize)
291
+ status_line("#{srv.key.to_s} (#{srv.class}##{stop})", stop.to_s.gsub('_', '-').capitalize)
292
+ else
293
+ status_line("#{srv.key.to_s}", stop.to_s.gsub('_', '-').capitalize)
294
+ end
295
+ #srv.__send__("#{track}_#{stop}")
296
+ srv.invoke(stop)
297
+ end
298
+ end
299
+
300
+ # Returns a list of all terminal stops, i.e. stops at a tracks end.
301
+ # FIXME: stop_map is not defined.
302
+ def end_stops
303
+ (stop_map.keys - stop_map.values).compact
304
+ end
305
+
306
+ # Give an overview of stops this track supports.
307
+ # FIXME: end_stops blows up.
308
+ def overview
309
+ end_stops.each do |stop_name|
310
+ action_plan(stop_name).each do |act|
311
+ display_action(act)
312
+ end
313
+ puts
314
+ end
315
+ end
316
+
317
+ # --- Print Methods ------------------------------------------------------
318
+
319
+ # Print a status header, which consists of project name and version on the
320
+ # left and stop location on the right.
321
+ #
322
+ def status_header(left, right='')
323
+ left, right = left.to_s, right.to_s
324
+ #left.color = 'blue'
325
+ #right.color = 'magenta'
326
+ unless quiet?
327
+ puts
328
+ print_header(left, right)
329
+ #puts "=" * io.screen_width
330
+ end
331
+ end
332
+
333
+ # Print a status line, which consists of service name on the left
334
+ # and stop name on the right.
335
+ #
336
+ def status_line(left, right='')
337
+ left, right = left.to_s, right.to_s
338
+ #left.color = 'blue'
339
+ #right.color = 'magenta'
340
+ unless quiet?
341
+ puts
342
+ #puts "-" * io.screen_width
343
+ print_phase(left, right)
344
+ #puts "-" * io.screen_width
345
+ #puts
346
+ end
347
+ end
348
+
349
+ #
350
+ def display_action(action_item)
351
+ phase, service, action, parameters = *action_item
352
+ puts " %-10s %-10s %-10s" % [phase.to_s.capitalize, service.service_title, action]
353
+ #status_line(service.service_title, phase.to_s.capitalize)
354
+ end
355
+
356
+ #
357
+ def print_header(left, right)
358
+ if ANSI::SUPPORTED
359
+ printline('', '', :pad=>1, :sep=>' ', :style=>[:negative, :bold], :left=>[:bold], :right=>[:bold])
360
+ printline(left, right, :pad=>2, :sep=>' ', :style=>[:negative, :bold], :left=>[:bold], :right=>[:bold])
361
+ printline('', '', :pad=>1, :sep=>' ', :style=>[:negative, :bold], :left=>[:bold], :right=>[:bold])
362
+ else
363
+ printline(left, right, :pad=>2, :sep=>'=')
364
+ end
365
+ end
366
+
367
+ #
368
+ def print_phase(left, right)
369
+ if ANSI::SUPPORTED
370
+ printline(left, right, :pad=>2, :sep=>' ', :style=>[:on_white, :black, :bold], :left=>[:bold], :right=>[:bold])
371
+ else
372
+ printline(left, right, :pad=>2, :sep=>'-')
373
+ end
374
+ end
375
+
376
+ #
377
+ def printline(left, right='', options={})
378
+ return if quiet?
379
+
380
+ separator = options[:seperator] || options[:sep] || ' '
381
+ padding = options[:padding] || options[:pad] || 0
382
+
383
+ left, right = left.to_s, right.to_s
384
+
385
+ left_size = left.size
386
+ right_size = right.size
387
+
388
+ #left = colorize(left)
389
+ #right = colorize(right)
390
+
391
+ l = padding
392
+ r = -(right_size + padding)
393
+
394
+ style = options[:style] || []
395
+ lstyle = options[:left] || []
396
+ rstyle = options[:right] || []
397
+
398
+ left = lstyle.inject(left) { |s, c| ansize(s, c) }
399
+ right = rstyle.inject(right){ |s, c| ansize(s, c) }
400
+
401
+ line = separator * screen_width
402
+ line[l, left_size] = left if left_size != 0
403
+ line[r, right_size] = right if right_size != 0
404
+
405
+ line = style.inject(line){ |s, c| ansize(s, c) }
406
+
407
+ puts line + ansize('', :clear)
408
+ end
409
+
410
+ #
411
+ def ansize(text, code)
412
+ #return text unless text.color
413
+ if RUBY_PLATFORM =~ /win/
414
+ text.to_s
415
+ else
416
+ ANSI::Code.send(code.to_sym) + text
417
+ end
418
+ end
419
+
420
+ #
421
+ def screen_width
422
+ ANSI::Terminal.terminal_width
423
+ end
424
+
425
+ end
426
+
427
+ end #module Detroit