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,52 @@
1
+ module Detroit
2
+
3
+ # Standard assembly is the default. In the vast majority of
4
+ # cases this is all that will ever be used.
5
+ assembly :standard do
6
+
7
+ # Main track.
8
+ #
9
+ # TODO: Should :install comes before :verify b/c verfication might
10
+ # require a local installation?
11
+ track :main,
12
+ :prepare, # prepare services / ensure service requirements
13
+ :generate, # code generation
14
+ :compile, # compile source code
15
+ :test, # run tests and specifications
16
+ :analyze, # perform code analysis
17
+ :document, # generate documentation
18
+ :package, # create packages
19
+ :verify, # post package verification / integration tests
20
+ :install, # install the package locally (if need be)
21
+ :publish, # publish website/documentation
22
+ :release, # release packages
23
+ :deploy, # deploy system to servers
24
+ :promote # tell the world about your awesome work
25
+
26
+ # The site track is a subset of the main track used to
27
+ # isolate the distribution of documentation and uploading
28
+ # a project's website.
29
+ #
30
+ # prepare -> generate -> analyze -> document -> publish
31
+ #
32
+ track :site,
33
+ :prepare,
34
+ :generate,
35
+ :analyze,
36
+ :document,
37
+ :publish
38
+
39
+ # The attention track is a small subset of main track, used to
40
+ # isolate the sending of promotional materials, mainly release
41
+ # announcements.
42
+ #
43
+ # prepare -> generate -> promote
44
+ #
45
+ track :attn,
46
+ :prepare,
47
+ :generate,
48
+ :promote
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,216 @@
1
+ module Detroit
2
+
3
+ require 'detroit/tool/core_ext'
4
+ require 'detroit/tool/shell_utils'
5
+ require 'detroit/tool/project_utils'
6
+ require 'detroit/tool/email_utils'
7
+
8
+ # The Toold module provide an isolated namespace for
9
+ # Detoit's tools. This allows for general use of these
10
+ # tools by other applications, by including them into
11
+ # their own namespace.
12
+ module Tools
13
+ end
14
+
15
+ # Tool registry.
16
+ def self.tools
17
+ @tools ||= {}
18
+ end
19
+
20
+ #
21
+ def self.services
22
+ tools
23
+ end
24
+
25
+ # Add tool class to registry. If class name ends in `Tool`
26
+ # it will be considered a reusable base class and not be added.
27
+ def self.register_tool(tool_class)
28
+ name = tool_class.basename
29
+ return if name.empty?
30
+ return if name =~ /(Tool|Service)$/
31
+ tools[name.downcase] = tool_class
32
+ Tools.const_set(name, tool_class)
33
+ # TODO: Should we auto-create convenience method?
34
+ return tool_class
35
+ end
36
+
37
+ # This base class can be used for tools that do not need
38
+ # all of the utility methods provided by the regular Tool
39
+ # class.
40
+ class BasicTool
41
+ # Add an assembly to which the tool applies.
42
+ # By default the `standard` assembly is implied.
43
+ def self.assembly(assembly=nil)
44
+ @assembly ||= []
45
+ if assembly
46
+ @assembly << assembly.to_sym
47
+ @assembly.uniq!
48
+ end
49
+ @assembly
50
+ end
51
+
52
+ # Override the `tracks` method to limit the lines a service
53
+ # will work with by default. Generally this is not used,
54
+ # and a return value of +nil+ means all lines apply.
55
+ #--
56
+ # TODO: Rename to #lines ?
57
+ #++
58
+ def self.tracks
59
+ end
60
+
61
+ # Override this method if the tools availability is conditional.
62
+ def self.available?
63
+ true
64
+ end
65
+
66
+ # Returns list of writer method names.
67
+ def self.options(service_class=self)
68
+ service_class.instance_methods.
69
+ select{ |m| m.to_s =~ /\w+=$/ && !%w{taguri=}.include?(m.to_s) }.
70
+ map{ |m| m.to_s.chomp('=') }
71
+ end
72
+
73
+ # Returns a Class which is a new subclass of the current class.
74
+ def self.factory(&block)
75
+ Class.new(self, &block)
76
+ end
77
+
78
+ # When inherited, add class to tool registry.
79
+ def self.inherited(base)
80
+ Detroit.register_tool(base)
81
+ end
82
+
83
+ # TODO: Needed? Rename?
84
+ def service_title
85
+ self.class.name
86
+ end
87
+ end
88
+
89
+ # Tool is the base class for all Detroit tools.
90
+ #
91
+ # Tool class is essentially the same as a Service class except that it is
92
+ # a subclass of RedTools::Tool. Use this class to build Detroit services
93
+ # with all the conveniences of a RedTools::Tool.
94
+ class Tool < BasicTool
95
+ include ShellUtils
96
+ include ProjectUtils
97
+ include EmailUtils
98
+
99
+ public
100
+
101
+ #
102
+ attr :options
103
+
104
+ # If applicable tools should override #current to allow tool users
105
+ # to know if the tool needs to be used. For example the RDoc tool
106
+ # can look to see if any the files it would document are newer that
107
+ # the previous generated set of docs.
108
+ #
109
+ # The method can return a String instead of `true`, to convey a
110
+ # custom message explaining that the tool need not be run. For example,
111
+ # the RDoc tool returns "RDocs are current (path/to/rdocs)".
112
+ def current?
113
+ false
114
+ end
115
+
116
+ private
117
+
118
+ # Create a new tool object.
119
+ #
120
+ # This sets up utility extensions and assigns options to setter attributes
121
+ # if they exist and values are not nil. That last point is important.
122
+ # You must use 'false' to purposely negate an option, as +nil+ will instead
123
+ # allow any default setting to be used.
124
+ #
125
+ def initialize(options={})
126
+ initialize_extension_defaults
127
+
128
+ initialize_requires
129
+ initialize_defaults
130
+
131
+ initialize_options(options)
132
+
133
+ initialize_extensions
134
+ end
135
+
136
+ # TODO: It would be best if an error were raised if an option is not
137
+ # supported, however for now only a warning will be issued, b/c of
138
+ # subclassing makes things more complicated.
139
+ def initialize_options(options)
140
+ @options = options
141
+
142
+ options.each do |k, v|
143
+ #send("#{k}=", v) unless v.nil? #if respond_to?("#{k}=") && !v.nil?
144
+ if respond_to?("#{k}=")
145
+ send("#{k}=", v) unless v.nil? #if respond_to?("#{k}=") && !v.nil?
146
+ else
147
+ warn "#{self.class.name} does not respond to `#{k}`."
148
+ end
149
+ end
150
+ end
151
+
152
+ #
153
+ def initialize_extension_defaults
154
+ super if defined?(super)
155
+ end
156
+
157
+ # Require support libraries needed by this service.
158
+ #
159
+ # def initialize_requires
160
+ # require 'ostruct'
161
+ # end
162
+ #
163
+ def initialize_requires
164
+ end
165
+
166
+ # When subclassing, put default instance variable settngs here.
167
+ #
168
+ # Examples
169
+ #
170
+ # def initialize_defaults
171
+ # @gravy = true
172
+ # end
173
+ #
174
+ def initialize_defaults
175
+ end
176
+
177
+ # --- Odd Utilities -------------------------------------------------------
178
+
179
+ require 'facets/platform'
180
+
181
+ # Current platform.
182
+ def current_platform
183
+ Platform.local.to_s
184
+ end
185
+
186
+ # TODO: Is naming_policy really useful?
187
+ # TODO: How to set this in a more universal manner?
188
+ #
189
+ def naming_policy(*policies)
190
+ if policies.empty?
191
+ @naming_policy ||= ['down', 'ext']
192
+ else
193
+ @naming_policy = policies
194
+ end
195
+ end
196
+
197
+ #
198
+ #
199
+ def apply_naming_policy(name, ext)
200
+ naming_policy.each do |policy|
201
+ case policy.to_s
202
+ when /^low/, /^down/
203
+ name = name.downcase
204
+ when /^up/
205
+ name = name.upcase
206
+ when /^cap/
207
+ name = name.capitalize
208
+ when /^ext/
209
+ name = name + ".#{ext}"
210
+ end
211
+ end
212
+ name
213
+ end
214
+
215
+ end
216
+ end
@@ -0,0 +1,3 @@
1
+ Dir[File.dirname(__FILE__) + '/core_ext/**/*.rb'].each do |file|
2
+ require file
3
+ end
@@ -0,0 +1,11 @@
1
+ # I know some people will be deterred by the dependency on Facets b/c they
2
+ # see it as a "heavy" dependency. But really that is far from true, consider
3
+ # the facet that the following libs are all that it used.
4
+
5
+ require 'facets/array/not_empty'
6
+ require 'facets/module/basename'
7
+ require 'facets/module/alias_accessor'
8
+ require 'facets/kernel/yes' # pulls in #ask and #no? too.
9
+ require 'facets/kernel/silence' # FIXME ???
10
+ require 'facets/kernel/disable_warnings'
11
+
@@ -0,0 +1,29 @@
1
+ module FileTest
2
+
3
+ # Return a cached list of the PATH environment variable.
4
+ # This is a support method used by #bin?
5
+ def command_paths
6
+ @command_paths ||= ENV['PATH'].split(/[:;]/)
7
+ end
8
+
9
+ # Is a file a bin/ executable?
10
+ #
11
+ # TODO: Make more robust. Probably needs to be fixed for Windows.
12
+ def bin?(fname)
13
+ is_bin = command_paths.any? do |f|
14
+ FileTest.exist?(File.join(f, fname))
15
+ end
16
+ #is_bin ? File.basename(fname) : false
17
+ is_bin ? fname : false
18
+ end
19
+
20
+ ## Is a file a task?
21
+ #
22
+ #def task?(path)
23
+ # task = File.dirname($0) + "/#{path}"
24
+ # task.chomp!('!')
25
+ # task if FileTest.file?(task) && FileTest.executable?(task)
26
+ #end
27
+
28
+ end
29
+
@@ -0,0 +1,7 @@
1
+ require 'facets/pathname'
2
+ require 'facets/filetest'
3
+ require 'facets/fileutils'
4
+
5
+ # DEPRECATE: when new #glob is working.
6
+ require 'facets/dir/multiglob'
7
+
@@ -0,0 +1,19 @@
1
+ class String
2
+
3
+ # Find actual filename (casefolding) and returns it.
4
+ # Returns nil if no file is found.
5
+
6
+ def to_actual_filename
7
+ Dir.glob(self, File::FNM_CASEFOLD).first
8
+ end
9
+
10
+ # Find actual filename (casefolding) and replace string with it.
11
+ # If file not found, string remains the same and method returns nil.
12
+
13
+ def to_actual_filename!
14
+ filename = to_actual_filename
15
+ replace(filename) if filename
16
+ end
17
+
18
+ end
19
+
@@ -0,0 +1,97 @@
1
+ # TODO: Improve the naming scheme of these methods.
2
+
3
+ #
4
+ class Array #:nodoc:
5
+
6
+ # Convert an array into commandline parameters.
7
+ # The array is accepted in the format of Ruby
8
+ # method arguments --ie. [arg1, arg2, ..., hash]
9
+
10
+ def to_console
11
+ #flags = (Hash===last ? pop : {})
12
+ #flags = flags.to_console
13
+ #flags + ' ' + join(" ")
14
+ to_argv.join(' ')
15
+ end
16
+
17
+ # TODO: DEPRECATE
18
+ alias_method :to_params, :to_console
19
+
20
+ #
21
+ def to_argv
22
+ flags = (Hash===last ? pop : {})
23
+ flags = flags.to_argv
24
+ flags + self
25
+ end
26
+
27
+ # def to_console
28
+ # flags = (Hash===last ? pop : {})
29
+ # flags = flags.collect do |f,v|
30
+ # m = f.to_s.size == 1 ? '-' : '--'
31
+ # case v
32
+ # when Array
33
+ # v.collect{ |e| "#{m}#{f} '#{e}'" }.join(' ')
34
+ # when true
35
+ # "#{m}#{f}"
36
+ # when false, nil
37
+ # ''
38
+ # else
39
+ # "#{m}#{f} '#{v}'"
40
+ # end
41
+ # end
42
+ # return (flags + self).join(" ")
43
+ # end
44
+
45
+ end
46
+
47
+ class Hash
48
+
49
+ # Convert a Hash into command line arguments.
50
+ # The array is accepted in the format of Ruby
51
+ # method arguments --ie. [arg1, arg2, ..., hash]
52
+ def to_console
53
+ to_argv.join(' ')
54
+ end
55
+
56
+ # Convert a Hash into command line parameters.
57
+ # The array is accepted in the format of Ruby
58
+ # method arguments --ie. [arg1, arg2, ..., hash]
59
+ def to_argv
60
+ flags = []
61
+ each do |f,v|
62
+ m = f.to_s.size == 1 ? '-' : '--'
63
+ case v
64
+ when Array
65
+ v.each{ |e| flags << "#{m}#{f}='#{e}'" }
66
+ when true
67
+ flags << "#{m}#{f}"
68
+ when false, nil
69
+ # nothing
70
+ else
71
+ flags << "#{m}#{f}='#{v}'"
72
+ end
73
+ end
74
+ flags
75
+ end
76
+
77
+ # Turn a hash into arguments.
78
+ #
79
+ # h = { :list => [1,2], :base => "HI" }
80
+ # h.argumentize #=> [ [], { :list => [1,2], :base => "HI" } ]
81
+ # h.argumentize(:list) #=> [ [1,2], { :base => "HI" } ]
82
+ #
83
+ def argumentize(args_field=nil)
84
+ config = dup
85
+ if args_field
86
+ args = [config.delete(args_field)].flatten.compact
87
+ else
88
+ args = []
89
+ end
90
+ args << config
91
+ return args
92
+ end
93
+
94
+ alias_method :command_vector, :argumentize
95
+
96
+ end
97
+
@@ -0,0 +1,29 @@
1
+ class Array
2
+
3
+ def to_list
4
+ self
5
+ end
6
+
7
+ end
8
+
9
+ class NilClass
10
+
11
+ def to_list
12
+ []
13
+ end
14
+
15
+ end
16
+
17
+ class String
18
+
19
+ # Helper method for cleaning list options.
20
+ # This will split the option on ':' or ';'
21
+ # if it is a string, rather than an array.
22
+ # And it will make sure there are no nil elements.
23
+
24
+ def to_list
25
+ split(/[:;,\n]/)
26
+ end
27
+
28
+ end
29
+