detroit 0.3.0 → 0.4.0
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.
- checksums.yaml +7 -0
- data/.index +59 -0
- data/EXAMPLE.md +66 -64
- data/{HISTORY.rdoc → HISTORY.md} +32 -5
- data/{COPYING.rdoc → LICENSE.txt} +0 -0
- data/README.md +142 -0
- data/bin/detroit +1 -1
- data/lib/detroit.rb +112 -40
- data/lib/detroit.yml +44 -29
- data/lib/detroit/assembly.rb +49 -193
- data/lib/detroit/basic_tool.rb +200 -0
- data/lib/detroit/basic_utils.rb +66 -0
- data/lib/detroit/core_ext.rb +2 -136
- data/lib/detroit/{tool/core_ext → core_ext}/facets.rb +3 -0
- data/lib/detroit/{tool/core_ext → core_ext}/filetest.rb +0 -0
- data/lib/detroit/{tool/core_ext → core_ext}/shell_extensions.rb +0 -0
- data/lib/detroit/{tool/core_ext → core_ext}/to_actual_filename.rb +0 -0
- data/lib/detroit/{tool/core_ext → core_ext}/to_console.rb +1 -0
- data/lib/detroit/{tool/core_ext → core_ext}/to_list.rb +0 -0
- data/lib/detroit/{tool/core_ext → core_ext}/to_yamlfrag.rb +0 -0
- data/lib/detroit/{tool/core_ext → core_ext}/unfold_paragraphs.rb +0 -0
- data/lib/detroit/{tool/email_utils.rb → email_utils.rb} +3 -1
- data/lib/detroit/exec.rb +55 -0
- data/lib/detroit/project.rb +134 -0
- data/lib/detroit/ruby_utils.rb +29 -0
- data/lib/detroit/{tool/shell_utils.rb → shell_utils.rb} +10 -5
- data/lib/detroit/toolchain.rb +6 -0
- data/lib/detroit/toolchain/cli.rb +320 -0
- data/lib/detroit/toolchain/config.rb +223 -0
- data/lib/detroit/toolchain/runner.rb +678 -0
- data/lib/detroit/toolchain/script.rb +248 -0
- data/lib/detroit/toolchain/worker.rb +84 -0
- data/man/detroit.1 +116 -0
- data/man/detroit.1.html +171 -0
- data/man/detroit.1.ronn +99 -0
- metadata +90 -51
- data/.ruby +0 -44
- data/README.rdoc +0 -132
- data/lib/detroit/application.rb +0 -463
- data/lib/detroit/assembly_system.rb +0 -80
- data/lib/detroit/config.rb +0 -203
- data/lib/detroit/control.rb +0 -129
- data/lib/detroit/custom.rb +0 -102
- data/lib/detroit/dsl.rb +0 -55
- data/lib/detroit/service.rb +0 -78
- data/lib/detroit/standard_assembly.rb +0 -51
- data/lib/detroit/tool.rb +0 -295
- data/lib/detroit/tool/core_ext.rb +0 -3
- data/lib/detroit/tool/project_utils.rb +0 -41
@@ -0,0 +1,200 @@
|
|
1
|
+
module Detroit
|
2
|
+
|
3
|
+
##
|
4
|
+
# This base class can be used for tools that do not need
|
5
|
+
# all of the utility methods provided by the regular Tool
|
6
|
+
# class.
|
7
|
+
#
|
8
|
+
class BasicTool
|
9
|
+
include BasicUtils
|
10
|
+
|
11
|
+
# Call this method to register a tool with an assembly.
|
12
|
+
# A tool can only belong to one assembly, but migration
|
13
|
+
# adapters can be defined to allow tools from one assembly
|
14
|
+
# to work with another (COMING SOON).
|
15
|
+
#
|
16
|
+
# @param [Assembly] a (optional)
|
17
|
+
# Assembly for which this tool is designed.
|
18
|
+
#
|
19
|
+
# @example
|
20
|
+
# class SomeTool < Tool
|
21
|
+
# assembly Standard
|
22
|
+
#
|
23
|
+
# @return [Assembly] Assembly module.
|
24
|
+
def self.assembly(a=nil)
|
25
|
+
#include(@assembly = a) if a
|
26
|
+
include(a) if a
|
27
|
+
@assembly
|
28
|
+
end
|
29
|
+
|
30
|
+
#
|
31
|
+
def self.assembly=(a)
|
32
|
+
@assembly = a
|
33
|
+
end
|
34
|
+
|
35
|
+
## Specify a supported station. This is used by the `chain?` method to
|
36
|
+
## determine if station is supporte by a tool. This is more convenient
|
37
|
+
## then overridding `chain?` method.
|
38
|
+
#def self.station(name, alt=nil)
|
39
|
+
# define_method("station_#{name}") do |options={}|
|
40
|
+
# meth = method(alt || name)
|
41
|
+
# case meth.arity
|
42
|
+
# when 0
|
43
|
+
# meth.call()
|
44
|
+
# else
|
45
|
+
# meth.call(options)
|
46
|
+
# end
|
47
|
+
# end
|
48
|
+
#end
|
49
|
+
|
50
|
+
# Returns a Class which is a new subclass of the current class.
|
51
|
+
#def self.factory(&block)
|
52
|
+
# Class.new(self, &block)
|
53
|
+
#end
|
54
|
+
|
55
|
+
# Override the usual new method in order to apply prerequisites.
|
56
|
+
#
|
57
|
+
# @return [BasicTool]
|
58
|
+
def self.new(options={})
|
59
|
+
tool = allocate
|
60
|
+
# TODO: I don't exactly like this, but how else to get project
|
61
|
+
# into the tool befire running `#prerequiste`?
|
62
|
+
tool.project = options['project']
|
63
|
+
ancestors.reverse_each do |anc|
|
64
|
+
next if (anc == BasicObject || anc == Object || anc == Kernel)
|
65
|
+
if anc.instance_methods.include?(:prerequisite)
|
66
|
+
pre = anc.instance_method(:prerequisite)
|
67
|
+
pre.bind(tool).call
|
68
|
+
end
|
69
|
+
end
|
70
|
+
tool.send(:initialize, options)
|
71
|
+
tool
|
72
|
+
end
|
73
|
+
|
74
|
+
# Returns list of writer method names. This is used for reference.
|
75
|
+
#
|
76
|
+
# @return [Array<String>]
|
77
|
+
def self.options(tool_class=self)
|
78
|
+
i = tool_class.ancestors.index(Tool)
|
79
|
+
m = []
|
80
|
+
tool_class.ancestors[0..i].each do |sc|
|
81
|
+
sc.public_instance_methods(false).each do |pm|
|
82
|
+
next if pm !~ /\w+=$/
|
83
|
+
next if %w{taguri=}.include?(m.to_s)
|
84
|
+
m << pm.to_s.chomp('=')
|
85
|
+
end
|
86
|
+
end
|
87
|
+
m
|
88
|
+
end
|
89
|
+
|
90
|
+
# Override this method if the tool's availability is conditional.
|
91
|
+
#
|
92
|
+
# @return [Boolean]
|
93
|
+
def self.available?
|
94
|
+
true
|
95
|
+
end
|
96
|
+
|
97
|
+
# This pre-initialization procedure is run before #initialize and
|
98
|
+
# for all ancestors, so `#super` should never be called within it.
|
99
|
+
# The method is intended to be used to require dependencies for a tool,
|
100
|
+
# so that tool's dependencies are only required when needed. But it can
|
101
|
+
# also be used to set pre-option attribute defaults.
|
102
|
+
#
|
103
|
+
# @example
|
104
|
+
# def prerequisite
|
105
|
+
# require 'ostruct'
|
106
|
+
# @gravy = true
|
107
|
+
# end
|
108
|
+
#
|
109
|
+
# @return [void]
|
110
|
+
def prerequisite
|
111
|
+
end
|
112
|
+
|
113
|
+
# Create a new tool object.
|
114
|
+
#
|
115
|
+
# This sets up utility extensions and assigns options to setter attributes
|
116
|
+
# if they exist and values are not nil. That last point is important.
|
117
|
+
# You must use 'false' to purposely negate an option, as +nil+ will instead
|
118
|
+
# allow any default setting to be used.
|
119
|
+
#
|
120
|
+
# @return [void]
|
121
|
+
def initialize(options={})
|
122
|
+
initialize_options(@options = options)
|
123
|
+
end
|
124
|
+
|
125
|
+
# Called by `#initialize` to call writers from given options.
|
126
|
+
#
|
127
|
+
# @return [void]
|
128
|
+
def initialize_options(options)
|
129
|
+
options.each do |k, v|
|
130
|
+
#send("#{k}=", v) unless v.nil? #if respond_to?("#{k}=") && !v.nil?
|
131
|
+
if respond_to?("#{k}=")
|
132
|
+
send("#{k}=", v) unless v.nil? #if respond_to?("#{k}=") && !v.nil?
|
133
|
+
else
|
134
|
+
warn "#{self.class.name} does not respond to `#{k}=`."
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Access to all options passed into `#initialize`.
|
140
|
+
#
|
141
|
+
# @return [Hash]
|
142
|
+
attr :options
|
143
|
+
|
144
|
+
#
|
145
|
+
# @todo Is this needed?
|
146
|
+
#
|
147
|
+
def title
|
148
|
+
self.class.name
|
149
|
+
end
|
150
|
+
|
151
|
+
# Does this tool attach to the specified station?
|
152
|
+
#
|
153
|
+
# By default this checks for the definition of a public method in the tool
|
154
|
+
# class with the same name as the station. Note, it does not use `respond_to?`
|
155
|
+
# to do this, which would find any such method in the class hierarchy. Instead
|
156
|
+
# it specifically checks for a definition in the tool class itself. This
|
157
|
+
# helps prevent potential accidental name clashes between support methods
|
158
|
+
# and station names.
|
159
|
+
#
|
160
|
+
def assemble?(station, options={})
|
161
|
+
self.class.public_methods(false).include?(station.to_sym)
|
162
|
+
end
|
163
|
+
|
164
|
+
#
|
165
|
+
def assemble(station, options={})
|
166
|
+
meth = method(station)
|
167
|
+
|
168
|
+
case meth.arity
|
169
|
+
when 0
|
170
|
+
meth.call()
|
171
|
+
else
|
172
|
+
meth.call(options)
|
173
|
+
end
|
174
|
+
end
|
175
|
+
|
176
|
+
# Project instance.
|
177
|
+
def project=(project)
|
178
|
+
@project = project
|
179
|
+
end
|
180
|
+
|
181
|
+
# Project instance.
|
182
|
+
def project
|
183
|
+
@project #||= Project.factory(root)
|
184
|
+
end
|
185
|
+
|
186
|
+
# Shortcut to project metadata.
|
187
|
+
def metadata
|
188
|
+
project.metadata
|
189
|
+
end
|
190
|
+
|
191
|
+
# Project root directory.
|
192
|
+
#
|
193
|
+
# @return [Pathname]
|
194
|
+
def root
|
195
|
+
@project.root
|
196
|
+
end
|
197
|
+
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
module Detroit
|
2
|
+
|
3
|
+
##
|
4
|
+
# Common utility methods included in all tools.
|
5
|
+
#
|
6
|
+
module BasicUtils
|
7
|
+
|
8
|
+
# Glob for finding root of a project.
|
9
|
+
def root_pattern
|
10
|
+
"{.index,.git,.hg,.svn,_darcs}"
|
11
|
+
end
|
12
|
+
|
13
|
+
# Project root directory.
|
14
|
+
def root
|
15
|
+
@root ||= (
|
16
|
+
path = nil
|
17
|
+
home = File.expand_path('~')
|
18
|
+
|
19
|
+
while dir != home && dir != '/'
|
20
|
+
if Dir[root_pattern].first
|
21
|
+
path = dir
|
22
|
+
break
|
23
|
+
end
|
24
|
+
dir = File.dirname(dir)
|
25
|
+
end
|
26
|
+
|
27
|
+
Pathname.new(path || Dir.pwd)
|
28
|
+
)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Configuration.
|
32
|
+
def config
|
33
|
+
@config ||= Config.new(root)
|
34
|
+
end
|
35
|
+
|
36
|
+
#
|
37
|
+
def naming_policy
|
38
|
+
@naming_policy ||= (
|
39
|
+
if config.naming_policy
|
40
|
+
Array(config.naming_policy)
|
41
|
+
else
|
42
|
+
['down', 'ext']
|
43
|
+
end
|
44
|
+
)
|
45
|
+
end
|
46
|
+
|
47
|
+
#
|
48
|
+
def apply_naming_policy(name, ext)
|
49
|
+
naming_policy.each do |policy|
|
50
|
+
case policy.to_s
|
51
|
+
when /^low/, /^down/
|
52
|
+
name = name.downcase
|
53
|
+
when /^up/
|
54
|
+
name = name.upcase
|
55
|
+
when /^cap/
|
56
|
+
name = name.capitalize
|
57
|
+
when /^ext/
|
58
|
+
name = name + ".#{ext}"
|
59
|
+
end
|
60
|
+
end
|
61
|
+
name
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
end
|
data/lib/detroit/core_ext.rb
CHANGED
@@ -1,137 +1,3 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
#Dir[File.join(__DIR__, 'core_ext', '*.rb')].each do |file|
|
4
|
-
# require file
|
5
|
-
#end
|
6
|
-
|
7
|
-
#require 'facets'
|
8
|
-
require 'facets/to_hash'
|
9
|
-
require 'facets/module/basename'
|
10
|
-
require 'facets/module/alias_accessor'
|
11
|
-
require 'facets/pathname'
|
12
|
-
#require 'facets/boolean'
|
13
|
-
|
14
|
-
class Array
|
15
|
-
|
16
|
-
def to_list
|
17
|
-
self
|
18
|
-
end
|
19
|
-
|
1
|
+
Dir[File.dirname(__FILE__) + '/core_ext/**/*.rb'].each do |file|
|
2
|
+
require file
|
20
3
|
end
|
21
|
-
|
22
|
-
class NilClass
|
23
|
-
|
24
|
-
def to_list
|
25
|
-
[]
|
26
|
-
end
|
27
|
-
|
28
|
-
end
|
29
|
-
|
30
|
-
class String
|
31
|
-
|
32
|
-
# Helper method for cleaning list options.
|
33
|
-
# This will split the option on ':' or ';'
|
34
|
-
# if it is a string, rather than an array.
|
35
|
-
# And it will make sure there are no nil elements.
|
36
|
-
|
37
|
-
def to_list
|
38
|
-
split(/[:;,\n]/).map{ |s| s.strip }
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
|
43
|
-
# TODO: Replace these with facets/shellwords !!!
|
44
|
-
|
45
|
-
#
|
46
|
-
class Array #:nodoc:
|
47
|
-
|
48
|
-
# Convert an array into commandline parameters.
|
49
|
-
# The array is accepted in the format of Ruby
|
50
|
-
# method arguments --ie. [arg1, arg2, ..., hash]
|
51
|
-
|
52
|
-
def to_console
|
53
|
-
#flags = (Hash===last ? pop : {})
|
54
|
-
#flags = flags.to_console
|
55
|
-
#flags + ' ' + join(" ")
|
56
|
-
to_argv.join(' ')
|
57
|
-
end
|
58
|
-
|
59
|
-
# TODO: DEPRECATE
|
60
|
-
alias_method :to_params, :to_console
|
61
|
-
|
62
|
-
#
|
63
|
-
def to_argv
|
64
|
-
flags = (Hash===last ? pop : {})
|
65
|
-
flags = flags.to_argv
|
66
|
-
flags + self
|
67
|
-
end
|
68
|
-
|
69
|
-
# def to_console
|
70
|
-
# flags = (Hash===last ? pop : {})
|
71
|
-
# flags = flags.collect do |f,v|
|
72
|
-
# m = f.to_s.size == 1 ? '-' : '--'
|
73
|
-
# case v
|
74
|
-
# when Array
|
75
|
-
# v.collect{ |e| "#{m}#{f} '#{e}'" }.join(' ')
|
76
|
-
# when true
|
77
|
-
# "#{m}#{f}"
|
78
|
-
# when false, nil
|
79
|
-
# ''
|
80
|
-
# else
|
81
|
-
# "#{m}#{f} '#{v}'"
|
82
|
-
# end
|
83
|
-
# end
|
84
|
-
# return (flags + self).join(" ")
|
85
|
-
# end
|
86
|
-
|
87
|
-
end
|
88
|
-
|
89
|
-
class Hash
|
90
|
-
|
91
|
-
# Convert a Hash into command line arguments.
|
92
|
-
# The array is accepted in the format of Ruby
|
93
|
-
# method arguments --ie. [arg1, arg2, ..., hash]
|
94
|
-
def to_console
|
95
|
-
to_argv.join(' ')
|
96
|
-
end
|
97
|
-
|
98
|
-
# Convert a Hash into command line parameters.
|
99
|
-
# The array is accepted in the format of Ruby
|
100
|
-
# method arguments --ie. [arg1, arg2, ..., hash]
|
101
|
-
def to_argv
|
102
|
-
flags = map do |f,v|
|
103
|
-
m = f.to_s.size == 1 ? '-' : '--'
|
104
|
-
case v
|
105
|
-
when Array
|
106
|
-
v.collect{ |e| "#{m}#{f}='#{e}'" }.join(' ')
|
107
|
-
when true
|
108
|
-
"#{m}#{f}"
|
109
|
-
when false, nil
|
110
|
-
''
|
111
|
-
else
|
112
|
-
"#{m}#{f}='#{v}'"
|
113
|
-
end
|
114
|
-
end
|
115
|
-
end
|
116
|
-
|
117
|
-
# Turn a hash into arguments.
|
118
|
-
#
|
119
|
-
# h = { :list => [1,2], :base => "HI" }
|
120
|
-
# h.argumentize #=> [ [], { :list => [1,2], :base => "HI" } ]
|
121
|
-
# h.argumentize(:list) #=> [ [1,2], { :base => "HI" } ]
|
122
|
-
#
|
123
|
-
def argumentize(args_field=nil)
|
124
|
-
config = dup
|
125
|
-
if args_field
|
126
|
-
args = [config.delete(args_field)].flatten.compact
|
127
|
-
else
|
128
|
-
args = []
|
129
|
-
end
|
130
|
-
args << config
|
131
|
-
return args
|
132
|
-
end
|
133
|
-
|
134
|
-
alias_method :command_vector, :argumentize
|
135
|
-
|
136
|
-
end
|
137
|
-
|
@@ -2,6 +2,9 @@
|
|
2
2
|
# see it as a "heavy" dependency. But really that is far from true, consider
|
3
3
|
# the facet that the following libs are all that it used.
|
4
4
|
|
5
|
+
require 'facets/pathname'
|
6
|
+
#require 'facets/boolean'
|
7
|
+
|
5
8
|
require 'facets/array/not_empty'
|
6
9
|
require 'facets/module/basename'
|
7
10
|
require 'facets/module/alias_accessor'
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/lib/detroit/exec.rb
ADDED
@@ -0,0 +1,55 @@
|
|
1
|
+
module Detroit
|
2
|
+
|
3
|
+
# Execute a sepcific detroit tool.
|
4
|
+
#
|
5
|
+
# Note this uses the executable gem to automatically
|
6
|
+
# "cli-ify" a tool class.
|
7
|
+
#
|
8
|
+
def self.tool_exec(*argv)
|
9
|
+
require 'executable'
|
10
|
+
|
11
|
+
tool, stop = argv.shift.split(':')
|
12
|
+
|
13
|
+
raise "No tool specified." unless tool
|
14
|
+
raise "No tool stop specified." unless stop
|
15
|
+
|
16
|
+
begin
|
17
|
+
require "detroit-#{tool}"
|
18
|
+
rescue LoadError
|
19
|
+
raise "Unknown tool. Perhaps try `gem install detroit-#{tool}`."
|
20
|
+
end
|
21
|
+
|
22
|
+
tool_class = Detroit.tools[tool]
|
23
|
+
|
24
|
+
exec_class = Class.new(tool_class) do
|
25
|
+
include Executable
|
26
|
+
|
27
|
+
# TODO: Fix executable, to at least super if defined super.
|
28
|
+
define_method(:initialize) do
|
29
|
+
tool_class.instance_method(:initialize).bind(self).call
|
30
|
+
end
|
31
|
+
|
32
|
+
# Show this message.
|
33
|
+
def help!
|
34
|
+
cli.show_help
|
35
|
+
exit
|
36
|
+
end
|
37
|
+
alias :h! :help!
|
38
|
+
|
39
|
+
define_method(:call) do |*args|
|
40
|
+
if assemble?(stop.to_sym)
|
41
|
+
assemble(stop.to_sym)
|
42
|
+
else
|
43
|
+
raise "#{tool} does not know how to #{stop}."
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
#exec_class.send(:define_method, :command_name) do
|
49
|
+
# tool
|
50
|
+
#end
|
51
|
+
|
52
|
+
exec_class.run(argv)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|