detroit 0.1.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.
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,65 @@
1
+ module Detroit
2
+
3
+ # Custom tool is used to create "quicky" services.
4
+ #
5
+ # This is a useful alternative to writing a full-blown plugin
6
+ # when the need is simple.
7
+ #
8
+ class Custom < Tool
9
+
10
+ # Default track(s) in which this plugin operates.
11
+ DEFAULT_TRACK = "main"
12
+
13
+ # Which track(s) to run this custom plugin.
14
+ attr_accessor :track
15
+
16
+ # Special writer to allow single track or a list of tracks.
17
+ def track=(val)
18
+ @track = val.to_list #[val].flatten
19
+ end
20
+
21
+ # Plural alias for #track.
22
+ alias_accessor :tracks, :track
23
+
24
+ private
25
+
26
+ # Instantiate new custom plugin.
27
+ #
28
+ # FIXME: Custom#initialize seems to be running twice at startup. Why?
29
+ #
30
+ # This works by interpreting the service configuration as a hash of
31
+ # stop names to ruby code.
32
+ #
33
+ def initialize(options)
34
+ super(options)
35
+ options.each do |stop, script|
36
+ # skip specific names used for configuration
37
+ next if stop == 'service'
38
+ next if stop == 'tracks' or stop == 'track'
39
+ next if stop == 'active'
40
+ next if stop == 'priority'
41
+ # remaining options are names of track stops
42
+ #tracks.each do |t|
43
+ src = %{
44
+ def #{stop}
45
+ #{script}
46
+ end
47
+ }
48
+ (class << self; self; end).module_eval(src)
49
+ #end
50
+ end
51
+ end
52
+
53
+ # Set initial attribute defaults.
54
+ def initialize_defaults
55
+ @track = [DEFAULT_TRACK]
56
+ end
57
+
58
+ #
59
+ def method_missing(s, *a, &b)
60
+ super(s, *a, &b) if @context.respond_to?(s)
61
+ end
62
+
63
+ end
64
+
65
+ end
@@ -0,0 +1,55 @@
1
+ module Detroit
2
+
3
+ # NOT YET IN USE.
4
+ module DSL
5
+
6
+ # Define a track.
7
+ #
8
+ # Examples
9
+ #
10
+ # track :site do
11
+ # route :maintainence do
12
+ # stops :reset, :clean, :purge
13
+ # end
14
+ # end
15
+ #
16
+ def track(&block)
17
+ Track.new(&block)
18
+ end
19
+
20
+ # Define a service.
21
+ #
22
+ # Examples
23
+ #
24
+ # service :foo do
25
+ # reset do
26
+ # utime(0,0, project.path(:log) + 'foo.log')
27
+ # end
28
+ # end
29
+ #
30
+ def service(name, &block)
31
+ Service.registry[name.to_s] ||= ServiceDSL.new(&block).service_class
32
+ end
33
+
34
+ # ServiceDSL is used to define services via the Detroit DSL.
35
+ class ServiceDSL < BasicObject
36
+ attr :service_class
37
+
38
+ def initialize(name, &block)
39
+ @service_class = Class.new(Detroit::Service)
40
+ end
41
+
42
+ def available(&block)
43
+ (class << @service_class; self; end).class_eval do
44
+ define_method(:available?, &block)
45
+ end
46
+ end
47
+
48
+ def method_missing(name, *args, &block)
49
+ @service_class.define_method(name, &block)
50
+ end
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,187 @@
1
+ module Detroit
2
+
3
+ # Schedule encapsulates a `Schedule` file and it's service instance
4
+ # configurations.
5
+ class Schedule
6
+
7
+ # Load Scedule file.
8
+ def self.load(input)
9
+ new(input)
10
+ end
11
+
12
+ # Hash table of services.
13
+ attr :services
14
+
15
+ private
16
+
17
+ # Initialize new Schedule instance.
18
+ def initialize(file, options={})
19
+ @project = options[:project]
20
+
21
+ @services = {}
22
+
23
+ @file = (String === file ? File.new(file) : file)
24
+
25
+ case File.extname(@file.path)
26
+ when '.rb'
27
+ instance_eval(@file.read, @file.path)
28
+ when '.yml', '.yaml'
29
+ @services = YAML.load(erb(@file.read))
30
+ else
31
+ text = @file.read
32
+ if /^---/ =~ text
33
+ @services = YAML.load(erb(text))
34
+ else
35
+ instance_eval(text, @file.path)
36
+ end
37
+ end
38
+ end
39
+
40
+ # Define a service.
41
+ def service(name, settings={}, &block)
42
+ if block
43
+ block_context = BlockContext.new(&block)
44
+ settings = block_context.settings
45
+ end
46
+ @services[name.to_s] = settings.rekey(&:to_s)
47
+ end
48
+
49
+ # Access to project data.
50
+ #
51
+ # NOTE: Thinking that the project should be relative
52
+ # to the Routine file itself, unless a `project` is passed
53
+ # in manually through the initializer. In the mean time,
54
+ # the project is just relative to the current working directory.
55
+ #
56
+ # TODO: Make configurable and use .ruby by default ?
57
+ def project
58
+ @project ||= POM::Project.find #(file_directory)
59
+ end
60
+
61
+ # Capitalized service names called as methods
62
+ # can also define a service.
63
+ def method_missing(sym, *args, &block)
64
+ service_class = sym.to_s
65
+ case service_class
66
+ when /^[A-Z]/
67
+ if Hash === args.last
68
+ args.last[:service] = service_class
69
+ else
70
+ args << {:services=>service_class}
71
+ end
72
+ case args.first
73
+ when String, Symbol
74
+ name = args.first
75
+ else
76
+ name = service_class.to_s.downcase
77
+ end
78
+ service(name, *args, &block)
79
+ else
80
+ super(sym, *args, &block)
81
+ end
82
+ end
83
+
84
+ private
85
+
86
+ # Process Routine document via ERB.
87
+ def erb(text)
88
+ context = ERBContext.new(project)
89
+ ERB.new(text).result(context.__binding__)
90
+ end
91
+
92
+ # ERBContext provides the clean context to process a Routine
93
+ # as an ERB template.
94
+ class ERBContext
95
+ #
96
+ def initialize(project)
97
+ @project = project
98
+ end
99
+
100
+ # Access to a clean binding.
101
+ def __binding__
102
+ binding
103
+ end
104
+
105
+ # Provide access to project data.
106
+ def project
107
+ @project
108
+ end
109
+
110
+ #
111
+ def method_missing(name, *args)
112
+ if project.respond_to?(name)
113
+ project.__send__(name, *args)
114
+ elsif project.metadata.respond_to?(name)
115
+ project.metadata.__send__(name, *args)
116
+ else
117
+ super(name, *args)
118
+ end
119
+ end
120
+ end
121
+
122
+ #
123
+ class BlockContext
124
+ #
125
+ attr :settings
126
+
127
+ #
128
+ def initialize(&block)
129
+ @settings = {}
130
+ if block.arity == 0
131
+ instance_eval(&block)
132
+ else
133
+ block.call(self)
134
+ end
135
+ end
136
+
137
+ #
138
+ def set(name, value=nil, &block)
139
+ if block
140
+ block_context = BlockContext.new
141
+ block.call(block_context)
142
+ @settings[name.to_s] = block_context.settings
143
+ else
144
+ @settings[name.to_s] = value
145
+ end
146
+ end
147
+
148
+ #
149
+ def method_missing(symbol, value=nil, *args)
150
+ case name = symbol.to_s
151
+ when /=$/
152
+ @settings[name.chomp('=')] = value
153
+ else
154
+ super(symbol, value=nil, *args)
155
+ end
156
+ end
157
+ end
158
+
159
+ end
160
+
161
+ # NOTE: This is problematic, because a Scheudle file should really know from
162
+ # what file it was derived.
163
+
164
+ #
165
+ DOMAIN = "rubyworks.github.com/detroit,2011-05-27"
166
+
167
+ # TODO: If using Psych rather than Syck, then define a domain type.
168
+
169
+ #if defined?(Psych) #RUBY_VERSION >= '1.9'
170
+ # YAML::add_domain_type(DOMAIN, "schedule") do |type, hash|
171
+ # Schedule.load(hash)
172
+ # end
173
+ #else
174
+ YAML::add_builtin_type("schedule") do |type, value|
175
+ value
176
+ #case value
177
+ #when String
178
+ # Schedule.eval(value)
179
+ #when Hash
180
+ # Schedule.new(value)
181
+ #else
182
+ # raise "ERROR: Invalid Schedule"
183
+ #end
184
+ end
185
+ #end
186
+
187
+ end
@@ -0,0 +1,188 @@
1
+ module Detroit
2
+
3
+ #
4
+ #def self.services
5
+ # @registry ||= {}
6
+ #end
7
+
8
+ # Service class wraps a Tool instance.
9
+ #
10
+ # TODO: change name ?
11
+ #
12
+ # TODO: Need to work on how to limit a service's tracks per-assembly.
13
+ class ServiceWrapper
14
+ attr :key
15
+ attr :tracks
16
+ attr :priority
17
+ attr :active
18
+ attr :service
19
+ #attr :options
20
+
21
+ # Set the priority. Priority determines the order which
22
+ # services on the same stop are run.
23
+ def priority=(integer)
24
+ @priority = integer.to_i
25
+ end
26
+
27
+ # Set the tracks a service will be available on.
28
+ def tracks=(list)
29
+ @tracks = list.to_list
30
+ end
31
+
32
+ #
33
+ def active=(boolean)
34
+ @active = !!boolean
35
+ end
36
+
37
+ # Create new ServiceWrapper.
38
+ def initialize(key, service_class, options)
39
+ @key = key
40
+
41
+ ## set service defaults
42
+ @tracks = nil #service_class.tracks
43
+ @priority = 0
44
+ @active = true
45
+
46
+ self.active = options.delete('active') if !options['active'].nil?
47
+ self.tracks = options.delete('tracks') if options.key?('tracks')
48
+ self.priority = options.delete('priority') if options.key?('priority')
49
+
50
+ @service = service_class.new(options)
51
+ end
52
+
53
+ # Does the service support the given stop.
54
+ def stop?(name)
55
+ @service.respond_to?(name)
56
+ end
57
+
58
+ # Run the service stop procedure.
59
+ def invoke(name)
60
+ @service.__send__(name) # public_send
61
+ end
62
+
63
+ #
64
+ def inspect
65
+ "<#{self.class}:#{object_id} @key='#{key}'>"
66
+ end
67
+ end
68
+
69
+ end
70
+
71
+
72
+
73
+ =begin
74
+ # Mixin module is added to Service and Tool.
75
+ module Serviceable
76
+
77
+ #
78
+ def self.included(base)
79
+ base.extend ClassRegistry
80
+ base.extend DomainLanguage
81
+ end
82
+
83
+ # Register new instance of the Service class.
84
+ module ClassRegistry
85
+
86
+ # Class-level attribute of registered Service subclasses.
87
+ #
88
+ # Returns a Hash.
89
+ def registry
90
+ Detroit.services
91
+ end
92
+
93
+ # TODO: Probably should make a named registry instead.
94
+ def inherited(base)
95
+ return if base.name.to_s.empty?
96
+ if base.name !~ /Service$/
97
+ registry[base.basename.downcase] = base
98
+ end
99
+ end
100
+
101
+ # Returns a Class which is a new subclass of the current class.
102
+ def factory(&block)
103
+ Class.new(self, &block)
104
+ end
105
+
106
+ #
107
+ def options(service_class=self)
108
+ service_class.instance_methods.
109
+ select{ |m| m.to_s =~ /\w+=$/ && !%w{taguri=}.include?(m.to_s) }.
110
+ map{ |m| m.to_s.chomp('=') }
111
+ end
112
+
113
+ end
114
+
115
+ # Service Domain language. This module extends the Service class,
116
+ # to provide a convenience interface for defining stops.
117
+ module DomainLanguage
118
+ ## TODO: Err.. Is this being used?
119
+ #def init(&block)
120
+ # define_method(:init, &block)
121
+ #end
122
+
123
+ # Override the `tracks` method to limit the lines a service
124
+ # will work with by default. Generally this is not used,
125
+ # and a return value of +nil+ means all lines apply.
126
+ def tracks
127
+ end
128
+
129
+ # TODO: Perhaps deprecate this in favor of just defining an `availabe?`
130
+ # class method.
131
+ def available(&block)
132
+ @available = block if block
133
+ @available ||= nil
134
+ end
135
+
136
+ #
137
+ def available?(project)
138
+ return true unless available
139
+ @available.call(project)
140
+ end
141
+ end
142
+
143
+ #attr_reader :service_name
144
+
145
+ #
146
+ def service_title
147
+ self.class.name
148
+ end
149
+
150
+ # TODO: Is this being used?
151
+ #def service_actions
152
+ # self.class.service_actions
153
+ #end
154
+
155
+ #
156
+ #def inspect
157
+ # "<#{self.class}:#{object_id}>"
158
+ #end
159
+ end
160
+
161
+ # The Service class is the base class for defining basic or delgated services.
162
+ class Service
163
+ include Serviceable
164
+
165
+ #
166
+ attr :options
167
+
168
+ def initialize(options={})
169
+ @options = options
170
+ end
171
+ end
172
+
173
+
174
+ end #module Detroit
175
+
176
+ # Provides a clean namespace for creating services.
177
+ module Detroit::Plugins
178
+ Service = Detroit::Service
179
+ Tool = Detroit::Tool
180
+ end
181
+
182
+ =end
183
+
184
+ # TOPLEVEL DSL?
185
+ #def service(name, &block)
186
+ # #Detroit.services[name] = Service.factory(&block)
187
+ # Detroit::Service.registry[name.to_s] = Detroit::Service.factory(&block)
188
+ #end