textgoeshere-daemon-kit 0.1.8rc3

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 (185) hide show
  1. data/.gitignore +5 -0
  2. data/Configuration.txt +110 -0
  3. data/Deployment.txt +113 -0
  4. data/History.txt +124 -0
  5. data/Logging.txt +96 -0
  6. data/PostInstall.txt +6 -0
  7. data/README.rdoc +128 -0
  8. data/Rakefile +29 -0
  9. data/RuoteParticipants.txt +113 -0
  10. data/TODO.txt +27 -0
  11. data/bin/daemon-kit +18 -0
  12. data/config/website.yml +2 -0
  13. data/daemon-kit.gemspec +265 -0
  14. data/lib/daemon_kit/abstract_logger.rb +249 -0
  15. data/lib/daemon_kit/amqp.rb +39 -0
  16. data/lib/daemon_kit/application.rb +230 -0
  17. data/lib/daemon_kit/arguments.rb +165 -0
  18. data/lib/daemon_kit/commands/console.rb +38 -0
  19. data/lib/daemon_kit/commands/destroy.rb +10 -0
  20. data/lib/daemon_kit/commands/generate.rb +10 -0
  21. data/lib/daemon_kit/config.rb +113 -0
  22. data/lib/daemon_kit/console_daemon.rb +2 -0
  23. data/lib/daemon_kit/core_ext/configurable.rb +96 -0
  24. data/lib/daemon_kit/core_ext/string.rb +22 -0
  25. data/lib/daemon_kit/core_ext.rb +1 -0
  26. data/lib/daemon_kit/cron.rb +48 -0
  27. data/lib/daemon_kit/cucumber/world.rb +38 -0
  28. data/lib/daemon_kit/deployment/capistrano.rb +516 -0
  29. data/lib/daemon_kit/em.rb +43 -0
  30. data/lib/daemon_kit/error_handlers/base.rb +32 -0
  31. data/lib/daemon_kit/error_handlers/hoptoad.rb +180 -0
  32. data/lib/daemon_kit/exceptions.rb +15 -0
  33. data/lib/daemon_kit/generators/base.rb +60 -0
  34. data/lib/daemon_kit/generators.rb +67 -0
  35. data/lib/daemon_kit/initializer.rb +453 -0
  36. data/lib/daemon_kit/jabber.rb +171 -0
  37. data/lib/daemon_kit/nanite/agent.rb +77 -0
  38. data/lib/daemon_kit/nanite.rb +7 -0
  39. data/lib/daemon_kit/pid_file.rb +61 -0
  40. data/lib/daemon_kit/ruote_participants.rb +125 -0
  41. data/lib/daemon_kit/ruote_pseudo_participant.rb +68 -0
  42. data/lib/daemon_kit/ruote_workitem.rb +187 -0
  43. data/lib/daemon_kit/safety.rb +84 -0
  44. data/lib/daemon_kit/tasks/environment.rake +10 -0
  45. data/lib/daemon_kit/tasks/framework.rake +123 -0
  46. data/lib/daemon_kit/tasks/god.rake +62 -0
  47. data/lib/daemon_kit/tasks/log.rake +8 -0
  48. data/lib/daemon_kit/tasks/monit.rake +29 -0
  49. data/lib/daemon_kit/tasks.rb +2 -0
  50. data/lib/daemon_kit/vendor/thor-0.13.6/CHANGELOG.rdoc +89 -0
  51. data/lib/daemon_kit/vendor/thor-0.13.6/LICENSE +20 -0
  52. data/lib/daemon_kit/vendor/thor-0.13.6/README.rdoc +297 -0
  53. data/lib/daemon_kit/vendor/thor-0.13.6/Thorfile +69 -0
  54. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/create_file.rb +103 -0
  55. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/directory.rb +91 -0
  56. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/empty_directory.rb +134 -0
  57. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/file_manipulation.rb +223 -0
  58. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/inject_into_file.rb +104 -0
  59. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions.rb +296 -0
  60. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/base.rb +540 -0
  61. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/core_ext/file_binary_read.rb +9 -0
  62. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  63. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/core_ext/ordered_hash.rb +100 -0
  64. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/error.rb +30 -0
  65. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/group.rb +271 -0
  66. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/invocation.rb +180 -0
  67. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/argument.rb +67 -0
  68. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/arguments.rb +150 -0
  69. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/option.rb +128 -0
  70. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/options.rb +169 -0
  71. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser.rb +4 -0
  72. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/rake_compat.rb +66 -0
  73. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/runner.rb +314 -0
  74. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/shell/basic.rb +239 -0
  75. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/shell/color.rb +108 -0
  76. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/shell.rb +83 -0
  77. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/task.rb +102 -0
  78. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/util.rb +224 -0
  79. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/version.rb +3 -0
  80. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor.rb +244 -0
  81. data/lib/daemon_kit/xmpp.rb +100 -0
  82. data/lib/daemon_kit.rb +59 -0
  83. data/lib/generators/daemon_kit/amqp/USAGE +5 -0
  84. data/lib/generators/daemon_kit/amqp/amqp_generator.rb +24 -0
  85. data/lib/generators/daemon_kit/amqp/templates/config/amqp.yml +28 -0
  86. data/lib/generators/daemon_kit/amqp/templates/config/pre-daemonize/amqp.rb +7 -0
  87. data/lib/generators/daemon_kit/amqp/templates/libexec/%app_name%-daemon.rb +37 -0
  88. data/lib/generators/daemon_kit/app/USAGE +7 -0
  89. data/lib/generators/daemon_kit/app/app_generator.rb +140 -0
  90. data/lib/generators/daemon_kit/app/templates/Gemfile +8 -0
  91. data/lib/generators/daemon_kit/app/templates/README.tt +58 -0
  92. data/lib/generators/daemon_kit/app/templates/Rakefile +6 -0
  93. data/lib/generators/daemon_kit/app/templates/bin/daemon.tt +7 -0
  94. data/lib/generators/daemon_kit/app/templates/config/arguments.rb +12 -0
  95. data/lib/generators/daemon_kit/app/templates/config/boot.rb +70 -0
  96. data/lib/generators/daemon_kit/app/templates/config/environment.rb.tt +26 -0
  97. data/lib/generators/daemon_kit/app/templates/config/environments/development.rb +2 -0
  98. data/lib/generators/daemon_kit/app/templates/config/environments/production.rb +5 -0
  99. data/lib/generators/daemon_kit/app/templates/config/environments/test.rb +2 -0
  100. data/lib/generators/daemon_kit/app/templates/config/post-daemonize/readme +5 -0
  101. data/lib/generators/daemon_kit/app/templates/config/pre-daemonize/readme +12 -0
  102. data/lib/generators/daemon_kit/app/templates/lib/%app_name%.rb +2 -0
  103. data/lib/generators/daemon_kit/app/templates/libexec/%app_name%-daemon.rb +18 -0
  104. data/lib/generators/daemon_kit/app/templates/script/console +3 -0
  105. data/lib/generators/daemon_kit/app/templates/script/destroy +3 -0
  106. data/lib/generators/daemon_kit/app/templates/script/generate +3 -0
  107. data/lib/generators/daemon_kit/capistrano/capistrano_generator.rb +26 -0
  108. data/lib/generators/daemon_kit/capistrano/templates/Capfile +10 -0
  109. data/lib/generators/daemon_kit/capistrano/templates/USAGE +10 -0
  110. data/lib/generators/daemon_kit/capistrano/templates/config/deploy/logrotate.erb +13 -0
  111. data/lib/generators/daemon_kit/capistrano/templates/config/deploy/production.rb.tt +6 -0
  112. data/lib/generators/daemon_kit/capistrano/templates/config/deploy/staging.rb.tt +6 -0
  113. data/lib/generators/daemon_kit/capistrano/templates/config/deploy.rb.tt +67 -0
  114. data/lib/generators/daemon_kit/capistrano/templates/config/environments/staging.rb +0 -0
  115. data/lib/generators/daemon_kit/cron/USAGE +5 -0
  116. data/lib/generators/daemon_kit/cron/cron_generator.rb +24 -0
  117. data/lib/generators/daemon_kit/cron/templates/config/pre-daemonize/cron.rb +11 -0
  118. data/lib/generators/daemon_kit/cron/templates/libexec/%app_name%-daemon.rb +43 -0
  119. data/lib/generators/daemon_kit/cucumber/USAGE +11 -0
  120. data/lib/generators/daemon_kit/cucumber/cucumber_generator.rb +45 -0
  121. data/lib/generators/daemon_kit/cucumber/templates/config/environments/cucumber.rb +2 -0
  122. data/lib/generators/daemon_kit/cucumber/templates/features/step_definitions/.empty_directory +0 -0
  123. data/lib/generators/daemon_kit/cucumber/templates/features/support/env.rb +7 -0
  124. data/lib/generators/daemon_kit/cucumber/templates/script/cucumber +7 -0
  125. data/lib/generators/daemon_kit/cucumber/templates/tasks/cucumber.rake +13 -0
  126. data/lib/generators/daemon_kit/nanite_agent/USAGE +5 -0
  127. data/lib/generators/daemon_kit/nanite_agent/nanite_agent_generator.rb +29 -0
  128. data/lib/generators/daemon_kit/nanite_agent/templates/config/nanite.yml +35 -0
  129. data/lib/generators/daemon_kit/nanite_agent/templates/config/pre-daemonize/nanite_agent.rb +6 -0
  130. data/lib/generators/daemon_kit/nanite_agent/templates/lib/actors/sample.rb +11 -0
  131. data/lib/generators/daemon_kit/nanite_agent/templates/libexec/%app_name%-daemon.rb +31 -0
  132. data/lib/generators/daemon_kit/rspec/USAGE +5 -0
  133. data/lib/generators/daemon_kit/rspec/rspec_generator.rb +20 -0
  134. data/lib/generators/daemon_kit/rspec/templates/spec/%app_name%_spec.rb +11 -0
  135. data/lib/generators/daemon_kit/rspec/templates/spec/spec.opts +1 -0
  136. data/lib/generators/daemon_kit/rspec/templates/spec/spec_helper.rb +23 -0
  137. data/lib/generators/daemon_kit/rspec/templates/tasks/rspec.rake +19 -0
  138. data/lib/generators/daemon_kit/ruote/USAGE +5 -0
  139. data/lib/generators/daemon_kit/ruote/ruote_generator.rb +29 -0
  140. data/lib/generators/daemon_kit/ruote/templates/config/amqp.yml +30 -0
  141. data/lib/generators/daemon_kit/ruote/templates/config/pre-daemonize/ruote.rb +13 -0
  142. data/lib/generators/daemon_kit/ruote/templates/config/ruote.yml +23 -0
  143. data/lib/generators/daemon_kit/ruote/templates/lib/%app_name%.rb +4 -0
  144. data/lib/generators/daemon_kit/ruote/templates/lib/sample.rb +26 -0
  145. data/lib/generators/daemon_kit/ruote/templates/libexec/%app_name%-daemon.rb +33 -0
  146. data/lib/generators/daemon_kit/test_unit/USAGE +5 -0
  147. data/lib/generators/daemon_kit/test_unit/templates/tasks/test_unit.rake +7 -0
  148. data/lib/generators/daemon_kit/test_unit/templates/test/%app_name%_test.rb.tt +9 -0
  149. data/lib/generators/daemon_kit/test_unit/templates/test/test_helper.rb +6 -0
  150. data/lib/generators/daemon_kit/test_unit/test_unit_generator.rb +20 -0
  151. data/lib/generators/daemon_kit/xmpp/templates/config/pre-daemonize/xmpp.rb +6 -0
  152. data/lib/generators/daemon_kit/xmpp/templates/config/xmpp.yml +29 -0
  153. data/lib/generators/daemon_kit/xmpp/templates/libexec/%app_name%-daemon.rb +27 -0
  154. data/lib/generators/daemon_kit/xmpp/xmpp_generator.rb +24 -0
  155. data/script/console +10 -0
  156. data/script/destroy +14 -0
  157. data/script/generate +14 -0
  158. data/script/txt2html +71 -0
  159. data/spec/abstract_logger_spec.rb +126 -0
  160. data/spec/argument_spec.rb +70 -0
  161. data/spec/config_spec.rb +83 -0
  162. data/spec/configurable_spec.rb +56 -0
  163. data/spec/daemon_kit_spec.rb +7 -0
  164. data/spec/error_handlers_spec.rb +23 -0
  165. data/spec/fixtures/env.yml +15 -0
  166. data/spec/fixtures/noenv.yml +4 -0
  167. data/spec/initializer_spec.rb +26 -0
  168. data/spec/spec.opts +1 -0
  169. data/spec/spec_helper.rb +27 -0
  170. data/tasks/cucumber.rake +13 -0
  171. data/tasks/rspec.rake +21 -0
  172. data/tasks/tests.rake +6 -0
  173. data/templates/god/god.erb +69 -0
  174. data/templates/monit/monit.erb +14 -0
  175. data/test/test_amqp_generator.rb +48 -0
  176. data/test/test_cron_generator.rb +45 -0
  177. data/test/test_daemon-kit_generator.rb +84 -0
  178. data/test/test_daemon_kit_config.rb +28 -0
  179. data/test/test_deploy_capistrano_generator.rb +48 -0
  180. data/test/test_generator_helper.rb +29 -0
  181. data/test/test_helper.rb +7 -0
  182. data/test/test_nanite_agent_generator.rb +49 -0
  183. data/test/test_ruote_generator.rb +51 -0
  184. data/test/test_test_unit_generator.rb +46 -0
  185. metadata +325 -0
@@ -0,0 +1,43 @@
1
+ module DaemonKit
2
+
3
+ # EventMachine forms a critical part of the daemon-kit toolset, and
4
+ # especially of daemon process developers.
5
+ #
6
+ # This class abstracts away the difficulties of managing multiple
7
+ # libraries that all utilize the event reactor.
8
+ class EM
9
+
10
+ class << self
11
+
12
+ # Start a reactor, just like classical EM.run. If the block is
13
+ # provided, the method will block and call the provided block
14
+ # argument inside the running reactor. If the block argument is
15
+ # not provided the reactor will be started in a separate thread
16
+ # and the program will continue to run after the method. All the
17
+ # signal traps are configured to shutdown the reactor when the
18
+ # daemon exists.
19
+ def run(&block)
20
+ if ::EM.reactor_running?
21
+ DaemonKit.logger.warn "EventMachine reactor already running"
22
+ block.call if block_given?
23
+
24
+ else
25
+ if block_given?
26
+ ::EM.run { block.call }
27
+ else
28
+ Thread.main[:_dk_reactor] = Thread.new { EM.run {} }
29
+ DaemonKit.trap( 'INT' ) { DaemonKit::EM.stop }
30
+ DaemonKit.trap( 'TERM' ) { DaemonKit::EM.stop }
31
+ end
32
+ end
33
+ end
34
+
35
+ # Stop the reactor
36
+ def stop
37
+ ::EM.stop_event_loop if ::EM.reactor_running?
38
+ Thread.main[:_dk_reactor].join
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,32 @@
1
+ module DaemonKit
2
+ module ErrorHandlers
3
+ # Error handlers in DaemonKit are used by the #Safety class. Any
4
+ # error handler has to support the interface provided by this
5
+ # class. It's also required that safety handlers implement a
6
+ # singleton approach (handled by default by #Base).
7
+ class Base
8
+
9
+ class << self
10
+
11
+ @instance = nil
12
+
13
+ def instance
14
+ @instance ||= new
15
+ end
16
+ private :new
17
+
18
+ # When we're inherited, immediately register the handler with
19
+ # the safety net
20
+ def inherited( child ) #:nodoc:
21
+ Safety.register_error_handler( child )
22
+ end
23
+ end
24
+
25
+ # Error handlers should overwrite this method and implement
26
+ # their own reporting method.
27
+ def handle_exception( exception )
28
+ raise NoMethodError, "Error handler doesn't support #handle_exception"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,180 @@
1
+ require 'net/http'
2
+
3
+ module DaemonKit
4
+ module ErrorHandlers
5
+ # Error reporting via Hoptoad.
6
+ class Hoptoad < Base
7
+
8
+ # Front end to parsing the backtrace for each notice
9
+ # (Graciously borrowed from http://github.com/thoughtbot/hoptoad_notifier)
10
+ class Backtrace
11
+
12
+ # Handles backtrace parsing line by line
13
+ # (Graciously borrowed from http://github.com/thoughtbot/hoptoad_notifier)
14
+ class Line
15
+
16
+ INPUT_FORMAT = %r{^([^:]+):(\d+)(?::in `([^']+)')?$}.freeze
17
+
18
+ # The file portion of the line (such as app/models/user.rb)
19
+ attr_reader :file
20
+
21
+ # The line number portion of the line
22
+ attr_reader :number
23
+
24
+ # The method of the line (such as index)
25
+ attr_reader :method
26
+
27
+ # Parses a single line of a given backtrace
28
+ # @param [String] unparsed_line The raw line from +caller+ or some backtrace
29
+ # @return [Line] The parsed backtrace line
30
+ def self.parse(unparsed_line)
31
+ _, file, number, method = unparsed_line.match(INPUT_FORMAT).to_a
32
+ new(file, number, method)
33
+ end
34
+
35
+ def initialize(file, number, method)
36
+ self.file = file
37
+ self.number = number
38
+ self.method = method
39
+ end
40
+
41
+ # Reconstructs the line in a readable fashion
42
+ def to_s
43
+ "#{file}:#{number}:in `#{method}'"
44
+ end
45
+
46
+ def ==(other)
47
+ to_s == other.to_s
48
+ end
49
+
50
+ def inspect
51
+ "<Line:#{to_s}>"
52
+ end
53
+
54
+ def to_xml
55
+ data = [ method, file, number ].map { |s| URI.escape( s || 'unknown', %q{"'<>&} ) }
56
+ %q{<line method="%s" file="%s" number="%s" />} % data
57
+ end
58
+
59
+ private
60
+
61
+ attr_writer :file, :number, :method
62
+ end
63
+
64
+ # holder for an Array of Backtrace::Line instances
65
+ attr_reader :lines
66
+
67
+ def self.parse(ruby_backtrace, opts = {})
68
+ ruby_lines = split_multiline_backtrace(ruby_backtrace)
69
+
70
+ filters = opts[:filters] || []
71
+ filtered_lines = ruby_lines.to_a.map do |line|
72
+ filters.inject(line) do |line, proc|
73
+ proc.call(line)
74
+ end
75
+ end.compact
76
+
77
+ lines = filtered_lines.collect do |unparsed_line|
78
+ Line.parse(unparsed_line)
79
+ end
80
+
81
+ instance = new(lines)
82
+ end
83
+
84
+ def initialize(lines)
85
+ self.lines = lines
86
+ end
87
+
88
+ def inspect
89
+ "<Backtrace: " + lines.collect { |line| line.inspect }.join(", ") + ">"
90
+ end
91
+
92
+ def ==(other)
93
+ if other.respond_to?(:lines)
94
+ lines == other.lines
95
+ else
96
+ false
97
+ end
98
+ end
99
+
100
+ private
101
+
102
+ attr_writer :lines
103
+
104
+ def self.split_multiline_backtrace(backtrace)
105
+ if backtrace.to_a.size == 1
106
+ backtrace.to_a.first.split(/\n\s*/)
107
+ else
108
+ backtrace
109
+ end
110
+ end
111
+ end
112
+
113
+ # Your hoptoad API key
114
+ @api_key = nil
115
+ attr_accessor :api_key
116
+
117
+ def handle_exception( exception )
118
+ headers = {
119
+ 'Content-type' => 'text/xml',
120
+ 'Accept' => 'text/xml, application/xml'
121
+ }
122
+
123
+ http = Net::HTTP.new( url.host, url.port )
124
+ data = format_exception( exception )
125
+ DaemonKit.logger.debug("Sending to Hoptoad: #{data}")
126
+
127
+ response = begin
128
+ http.post( url.path, data, headers )
129
+ rescue TimeoutError => e
130
+ DaemonKit.logger.error("Timeout while contacting the Hoptoad server.")
131
+ nil
132
+ end
133
+ case response
134
+ when Net::HTTPSuccess then
135
+ DaemonKit.logger.info "Hoptoad Success: #{response.class}"
136
+ else
137
+ DaemonKit.logger.error "Hoptoad Failure: #{response.class}\n#{response.body if response.respond_to? :body}"
138
+ end
139
+ end
140
+
141
+ def url
142
+ URI.parse("http://hoptoadapp.com/notifier_api/v2/notices")
143
+ end
144
+
145
+ def format_exception( exception )
146
+ lines = Backtrace.parse( exception.backtrace )
147
+ exception_message= exception.message
148
+ exception_message.gsub!("\"","&quot;")
149
+ exception_message.gsub!("'","&apos;")
150
+ exception_message.gsub!("&","&amp;")
151
+ exception_message.gsub!("<","&lt;")
152
+ exception_message.gsub!(">","&gt;")
153
+
154
+ <<-EOF
155
+ <?xml version="1.0" encoding="UTF-8"?>
156
+ <notice version="2.0">
157
+ <api-key>#{self.api_key}</api-key>
158
+ <notifier>
159
+ <name>daemon-kit</name>
160
+ <version>#{DaemonKit::VERSION}</version>
161
+ <url>http://github.com/kennethkalmer/daemon-kit</url>
162
+ </notifier>
163
+ <error>
164
+ <class>#{exception.class.name}</class>
165
+ <message>#{exception_message}</message>
166
+ <backtrace>
167
+ #{Backtrace.parse( exception.backtrace ).lines.inject('') { |string,line| string << line.to_xml }}
168
+ </backtrace>
169
+ </error>
170
+ <server-environment>
171
+ <project-root>#{DaemonKit.root}</project-root>
172
+ <environment-name>#{DaemonKit.env}</environment-name>
173
+ </server-environment>
174
+ </notice>
175
+ EOF
176
+ end
177
+ end
178
+
179
+ end
180
+ end
@@ -0,0 +1,15 @@
1
+ module DaemonKit
2
+ # The core of daemon-kit exceptions
3
+ class Exception < ::StandardError
4
+ end
5
+
6
+ # Raised when no class is registered to process a ruote workitem
7
+ class MissingParticipant < Exception; end
8
+
9
+ # Raised when the daemon itself cannot be found.
10
+ class DaemonNotFound < Exception
11
+ def initialize( file )
12
+ super "No daemon found at the path '#{file}'"
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,60 @@
1
+ require 'thor/group'
2
+
3
+ module DaemonKit
4
+ module Generators
5
+ class Error < Thor::Error
6
+ end
7
+
8
+ class Base < Thor::Group
9
+ include Thor::Actions
10
+
11
+ add_runtime_options!
12
+
13
+ # Tries to get the description from a USAGE file one folder above the source
14
+ # root otherwise uses a default description.
15
+ def self.desc(description=nil)
16
+ return super if description
17
+ usage = File.expand_path(File.join(source_root, "..", "USAGE"))
18
+
19
+ @desc ||= if File.exist?(usage)
20
+ File.read(usage)
21
+ else
22
+ "Description:\n Create #{base_name.humanize.downcase} files for #{generator_name} generator."
23
+ end
24
+ end
25
+
26
+ protected
27
+
28
+ def app_name
29
+ @app_name = File.basename( destination_root )
30
+ end
31
+
32
+ # Small macro to add ruby as an option to the generator with proper
33
+ # default value plus an instance helper method called shebang.
34
+ #
35
+ def self.add_shebang_option!
36
+ class_option :ruby, :type => :string, :aliases => "-r", :default => Thor::Util.ruby_command,
37
+ :desc => "Path to the Ruby binary of your choice", :banner => "PATH"
38
+
39
+ no_tasks {
40
+ define_method :shebang do
41
+ @shebang ||= begin
42
+ command = if options[:ruby] == Thor::Util.ruby_command
43
+ "/usr/bin/env #{File.basename(Thor::Util.ruby_command)}"
44
+ else
45
+ options[:ruby]
46
+ end
47
+ "#!#{command}"
48
+ end
49
+ end
50
+ }
51
+ end
52
+
53
+ def self.namespace(name = nil)
54
+ return super if name
55
+ @namespace ||= super.sub(/_generator$/, '').sub(/:generators:/, ':').sub(/^daemon_kit:/, '')
56
+ end
57
+
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,67 @@
1
+ # TODO: Don't always depend on bundled thor
2
+ $:.unshift File.dirname(__FILE__) + '/vendor/thor-0.13.6/lib'
3
+
4
+ require 'daemon_kit/generators/base'
5
+
6
+ module DaemonKit
7
+ module Generators
8
+ autoload :AppGenerator, 'generators/daemon_kit/app/app_generator'
9
+ autoload :CucumberGenerator, 'generators/daemon_kit/cucumber/cucumber_generator'
10
+ autoload :AmqpGenerator, 'generators/daemon_kit/amqp/amqp_generator'
11
+ autoload :CronGenerator, 'generators/daemon_kit/cron/cron_generator'
12
+ autoload :CapistranoGenerator, 'generators/daemon_kit/capistrano/capistrano_generator'
13
+ autoload :NaniteAgentGenerator, 'generators/daemon_kit/nanite_agent/nanite_agent_generator'
14
+ autoload :SpecGenerator, 'generators/daemon_kit/rspec/rspec_generator'
15
+ autoload :TestUnitGenerator, 'generators/daemon_kit/test_unit/test_unit_generator'
16
+ autoload :RuoteGenerator, 'generators/daemon_kit/ruote/ruote_generator'
17
+ autoload :XmppGenerator, 'generators/daemon_kit/xmpp/xmpp_generator'
18
+
19
+ class << self
20
+
21
+ def configure!
22
+ end
23
+
24
+ def invoke( namespace, args = ARGV, config = {} )
25
+ klass_name = constants.detect do |sym|
26
+ klass = const_get( sym )
27
+ klass.respond_to?( :namespace ) && klass.namespace == namespace
28
+ end
29
+
30
+ if klass_name.nil?
31
+ raise Error, "Could not find generator #{namespace}."
32
+ end
33
+
34
+ klass = const_get( klass_name )
35
+
36
+ args << '--help' if args.empty? && klass.arguments.any? { |a| a.required? }
37
+ klass.start( args, config )
38
+ end
39
+
40
+ def help
41
+ namespaces = constants.inject([]) do |list, sym|
42
+ unless sym == :Base || sym == :AppGenerator
43
+ klass = const_get( sym )
44
+ list << klass.namespace if klass.respond_to?( :namespace )
45
+ end
46
+
47
+ list
48
+ end
49
+
50
+ puts "Usage:"
51
+ puts " script/generate GENERATOR [args] [options]"
52
+ puts
53
+ puts "General options:"
54
+ puts " -h, [--help] # Print generators options and usage"
55
+ puts " -p, [--pretend] # Run but do not make any changes"
56
+ puts " -f, [--force] # Overwrite files that already exist"
57
+ puts " -s, [--skip] # Skip files that already exist"
58
+ puts " -q, [--quiet] # Supress status output"
59
+ puts
60
+ puts "Available generators:"
61
+
62
+ namespaces.each { |ns| puts " " + ns }
63
+ puts
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,453 @@
1
+ require 'pathname'
2
+
3
+ DAEMON_ENV = (ENV['DAEMON_ENV'] || 'development').dup unless defined?(DAEMON_ENV)
4
+
5
+ # Absolute paths to the daemon_kit libraries added to $:
6
+ incdir = ( File.dirname(__FILE__) + '/..' )
7
+ absincdir = if RUBY_PLATFORM =~ /(:?mswin|mingw)/
8
+ File.expand_path( incdir )
9
+ else
10
+ File.expand_path( Pathname.new( incdir ).realpath.to_s )
11
+ end
12
+ $:.unshift absincdir unless $:.include?( absincdir )
13
+
14
+ require 'daemon_kit'
15
+
16
+ module DaemonKit
17
+
18
+ class << self
19
+
20
+ def configuration
21
+ @configuration
22
+ end
23
+
24
+ def configuration=( configuration )
25
+ @configuration = configuration
26
+ end
27
+
28
+ def arguments
29
+ @arguments
30
+ end
31
+
32
+ def arguments=( args )
33
+ @arguments = args
34
+ end
35
+
36
+ def trap( *args, &block )
37
+ self.configuration.trap( *args, &block )
38
+ end
39
+
40
+ def at_shutdown( &block )
41
+ self.configuration.at_shutdown( &block )
42
+ end
43
+
44
+ end
45
+
46
+
47
+ # This class does all the nightmare work of setting up a working
48
+ # environment for your daemon.
49
+ class Initializer
50
+
51
+ attr_reader :configuration
52
+
53
+ def self.run
54
+ configuration = DaemonKit.configuration || Configuration.new
55
+
56
+ yield configuration if block_given?
57
+ initializer = new configuration
58
+ initializer.before_daemonize
59
+ initializer
60
+ end
61
+
62
+ def self.continue!
63
+ initializer = new DaemonKit.configuration
64
+ initializer.after_daemonize
65
+ end
66
+
67
+ def self.shutdown( clean = false, do_exit = false )
68
+ return unless $daemon_kit_shutdown_hooks_ran.nil?
69
+ $daemon_kit_shutdown_hooks_ran = true
70
+
71
+ DaemonKit.logger.info "Running shutdown hooks"
72
+
73
+ DaemonKit.configuration.shutdown_hooks.each do |hook|
74
+ begin
75
+ hook.call
76
+ rescue => e
77
+ DaemonKit.logger.exception( e )
78
+ end
79
+ end
80
+
81
+ log_exceptions if DaemonKit.configuration.backtraces && !clean
82
+
83
+ DaemonKit.logger.warn "Shutting down #{DaemonKit.configuration.daemon_name}"
84
+
85
+ exit if do_exit
86
+ end
87
+
88
+ def initialize( configuration )
89
+ @configuration = configuration
90
+ end
91
+
92
+ def before_daemonize
93
+ DaemonKit.configuration = @configuration
94
+
95
+ set_load_path
96
+ load_gems
97
+ load_patches
98
+ load_environment
99
+ load_predaemonize_configs
100
+ end
101
+
102
+ def after_daemonize
103
+ set_umask
104
+
105
+ initialize_logger
106
+ initialize_signal_traps
107
+
108
+ include_core_lib
109
+ load_postdaemonize_configs
110
+ configure_backtraces
111
+
112
+ set_process_name
113
+
114
+ DaemonKit.logger.info( "DaemonKit (#{DaemonKit::VERSION}) booted, now running #{DaemonKit.configuration.daemon_name}" )
115
+
116
+ if DaemonKit.configuration.user || DaemonKit.configuration.group
117
+ euid = Process.euid
118
+ egid = Process.egid
119
+ uid = Process.uid
120
+ gid = Process.gid
121
+ DaemonKit.logger.info( "DaemonKit dropped privileges to: #{euid} (EUID), #{egid} (EGID), #{uid} (UID), #{gid} (GID)" )
122
+ end
123
+ end
124
+
125
+ def set_load_path
126
+ configuration.load_paths.each do |d|
127
+ $:.unshift( "#{DAEMON_ROOT}/#{d}" ) if File.directory?( "#{DAEMON_ROOT}/#{d}" )
128
+ end
129
+ end
130
+
131
+ def load_gems
132
+
133
+ end
134
+
135
+ def load_patches
136
+
137
+ end
138
+
139
+ def load_environment
140
+ # Needs to be global to prevent loading the files twice
141
+ return if $_daemon_environment_loaded
142
+ $_daemon_environment_loaded = true
143
+
144
+ config = configuration
145
+
146
+ eval(IO.read(configuration.environment_path), binding, configuration.environment_path)
147
+
148
+ eval(IO.read(configuration.daemon_initializer), binding, configuration.daemon_initializer) if File.exist?( configuration.daemon_initializer )
149
+ end
150
+
151
+ def load_predaemonize_configs
152
+ Dir[ File.join( DAEMON_ROOT, 'config', 'pre-daemonize', '*.rb' ) ].each do |f|
153
+ next if File.basename( f ) == File.basename( configuration.daemon_initializer )
154
+
155
+ require f
156
+ end
157
+ end
158
+
159
+ def load_postdaemonize_configs
160
+ Dir[ File.join( DAEMON_ROOT, 'config', 'post-daemonize', '*.rb' ) ].each do |f|
161
+ require f
162
+ end
163
+ end
164
+
165
+ def set_umask
166
+ File.umask configuration.umask
167
+ end
168
+
169
+ def initialize_logger
170
+ return if DaemonKit.logger
171
+
172
+ unless logger = configuration.logger
173
+ logger = AbstractLogger.new( configuration.log_path )
174
+ logger.level = configuration.log_level
175
+ logger.copy_to_stdout = configuration.log_stdout
176
+ end
177
+
178
+ DaemonKit.logger = logger
179
+
180
+ DaemonKit.logger.info "DaemonKit (#{DaemonKit::VERSION}) booting in #{DAEMON_ENV} mode"
181
+
182
+ configuration.trap("USR1") {
183
+ DaemonKit.logger.level = DaemonKit.logger.debug? ? :info : :debug
184
+ DaemonKit.logger.info "Log level changed to #{DaemonKit.logger.debug? ? 'DEBUG' : 'INFO' }"
185
+ }
186
+ configuration.trap("USR2") {
187
+ DaemonKit.logger.level = :debug
188
+ DaemonKit.logger.info "Log level changed to DEBUG"
189
+ }
190
+ configuration.trap("HUP") {
191
+ DaemonKit::Application.reopen_logs
192
+ }
193
+ end
194
+
195
+ def initialize_signal_traps
196
+ # Only exit the process if we're not in the 'test' environment
197
+ term_proc = Proc.new { DaemonKit::Initializer.shutdown( true, DAEMON_ENV != 'test' ) }
198
+ configuration.trap( 'INT', term_proc )
199
+ configuration.trap( 'TERM', term_proc )
200
+ at_exit { DaemonKit::Initializer.shutdown }
201
+ end
202
+
203
+ def include_core_lib
204
+ if File.exists?( core_lib = File.join( DAEMON_ROOT, 'lib', configuration.daemon_name + '.rb' ) )
205
+ require core_lib
206
+ end
207
+ end
208
+
209
+ def configure_backtraces
210
+ Thread.abort_on_exception = configuration.backtraces
211
+ end
212
+
213
+ def set_process_name
214
+ $0 = configuration.daemon_name
215
+ end
216
+
217
+ def self.log_exceptions
218
+ trace_file = File.join( DaemonKit.root, 'log', "backtrace-#{Time.now.strftime('%Y%m%d%H%M%S')}-#{Process.pid}.log" )
219
+ trace_log = Logger.new( trace_file )
220
+
221
+ # Find the last exception
222
+ e = nil
223
+ ObjectSpace.each_object {|o|
224
+ if ::Exception === o
225
+ e = o
226
+ end
227
+ }
228
+
229
+ trace_log.info "*** Below you'll find the most recent exception thrown, this will likely (but not certainly) be the exception that made #{DaemonKit.configuration.daemon_name} exit abnormally ***"
230
+ trace_log.error e
231
+
232
+ trace_log.info "*** Below you'll find all the exception objects in memory, some of them may have been thrown in your application, others may just be in memory because they are standard exceptions ***"
233
+ ObjectSpace.each_object {|o|
234
+ if ::Exception === o
235
+ trace_log.error o
236
+ end
237
+ }
238
+
239
+ trace_log.close
240
+ end
241
+ end
242
+
243
+ # Holds our various configuration values
244
+ class Configuration
245
+
246
+ include Configurable
247
+
248
+ # Root to the daemon
249
+ attr_reader :root_path
250
+
251
+ # List of load paths
252
+ attr_accessor :load_paths
253
+
254
+ # Custom logger instance to use
255
+ attr_accessor :logger
256
+
257
+ # The log level to use, defaults to DEBUG
258
+ attr_reader :log_level
259
+
260
+ # Path to the log file, defaults to 'log/<environment>.log'
261
+ configurable :log_path
262
+
263
+ # Duplicate log data to stdout
264
+ attr_accessor :log_stdout
265
+
266
+ # Path to the pid file, defaults to 'log/<daemon_name>.pid'
267
+ attr_accessor :pid_file
268
+
269
+ # The application name
270
+ configurable :daemon_name, :locked => true
271
+
272
+ # Use the force kill patch? Give the number of seconds
273
+ configurable :force_kill_wait
274
+
275
+ # Should be log backtraces
276
+ configurable :backtraces, false
277
+
278
+ # Configurable umask
279
+ configurable :umask, 0022
280
+
281
+ # Configurable user
282
+ configurable :user, :locked => true
283
+
284
+ # Confgiruable group
285
+ configurable :group, :locked => true
286
+
287
+ # Collection of signal traps
288
+ attr_reader :signal_traps
289
+
290
+ # Our safety net (#Safety) instance
291
+ attr_accessor :safety_net
292
+
293
+ # :nodoc: Shutdown hooks
294
+ attr_reader :shutdown_hooks
295
+
296
+ def initialize
297
+ parse_arguments!
298
+
299
+ set_root_path!
300
+ set_daemon_defaults!
301
+
302
+ self.load_paths = default_load_paths
303
+ self.log_level ||= default_log_level
304
+ self.log_path ||= default_log_path
305
+
306
+ self.force_kill_wait = false
307
+
308
+ self.safety_net = DaemonKit::Safety.instance
309
+
310
+ @signal_traps = {}
311
+ @shutdown_hooks = []
312
+ end
313
+
314
+ def environment
315
+ ::DAEMON_ENV
316
+ end
317
+
318
+ # The path to the current environment's file (<tt>development.rb</tt>, etc.). By
319
+ # default the file is at <tt>config/environments/#{environment}.rb</tt>.
320
+ def environment_path
321
+ "#{root_path}/config/environments/#{environment}.rb"
322
+ end
323
+
324
+ def daemon_initializer
325
+ "#{root_path}/config/initializers/#{self.daemon_name}.rb"
326
+ end
327
+
328
+ # Add a trap for the specified signal, can be code block or a proc
329
+ def trap( signal, proc = nil, &block )
330
+ return if proc.nil? && !block_given?
331
+
332
+ # One step towards running on windows, not enough though
333
+ unless Signal.list.include?( signal )
334
+ DaemonKit.logger.warn( "Trapping #{signal} signals not supported on this platform" )
335
+ return
336
+ end
337
+
338
+ unless @signal_traps.has_key?( signal )
339
+ set_trap( signal )
340
+ end
341
+
342
+ @signal_traps[signal].unshift( proc || block )
343
+ end
344
+
345
+ # Add a block or proc to be called during shutdown
346
+ def at_shutdown( proc = nil, &block )
347
+ return if proc.nil? && !block_given?
348
+
349
+ @shutdown_hooks << ( proc || block )
350
+ end
351
+
352
+ def pid_file
353
+ @pid_file ||= "#{File.dirname(self.log_path)}/#{self.daemon_name}.pid"
354
+ end
355
+
356
+ # Set the log level
357
+ def log_level=( level )
358
+ @log_level = level
359
+ DaemonKit.logger.level = @log_level if DaemonKit.logger
360
+ end
361
+
362
+ protected
363
+
364
+ def run_traps( signal )
365
+ DaemonKit.logger.info "Running signal traps for #{signal}"
366
+ self.signal_traps[ signal ].each { |trap| trap.call }
367
+ end
368
+
369
+ private
370
+
371
+ def set_trap( signal )
372
+ DaemonKit.logger.info "Setting up trap for #{signal}"
373
+ @signal_traps[ signal ] = []
374
+ Signal.trap( signal, Proc.new { self.run_traps( signal ) } )
375
+ end
376
+
377
+ def parse_arguments!
378
+ return unless own_args?
379
+
380
+ configs = Arguments.configuration( ARGV ).first
381
+ @unused_arguments = {}
382
+
383
+ configs.each do |c|
384
+ k,v = c.split('=')
385
+
386
+ if v.nil?
387
+ error( "#{k} has no value" )
388
+ next
389
+ end
390
+
391
+ begin
392
+ if self.respond_to?( k )
393
+ self.send( "#{k}=", v ) # pid_file = /var/run/foo.pid
394
+ else
395
+ @unused_arguments[ k ] = v
396
+ end
397
+ rescue => e
398
+ error( "Couldn't set `#{k}' to `#{v}': #{e.message}" )
399
+ end
400
+ end
401
+ end
402
+
403
+ # DANGEROUS: Change the value of DAEMON_ENV
404
+ def environment=( env )
405
+ ::DAEMON_ENV.replace( env )
406
+ end
407
+
408
+ def set_root_path!
409
+ raise "DAEMON_ROOT is not set" unless defined?(::DAEMON_ROOT)
410
+ raise "DAEMON_ROOT is not a directory" unless File.directory?(::DAEMON_ROOT)
411
+
412
+ @root_path = ::DAEMON_ROOT.to_absolute_path
413
+
414
+ Object.const_set(:RELATIVE_DAEMON_ROOT, ::DAEMON_ROOT.dup) unless defined?(::RELATIVE_DAEMON_ROOT)
415
+ ::DAEMON_ROOT.replace @root_path
416
+ end
417
+
418
+ def set_daemon_defaults!
419
+ self.log_stdout = false
420
+ end
421
+
422
+ def default_load_paths
423
+ [ 'lib' ]
424
+ end
425
+
426
+ def default_log_path
427
+ File.join(root_path, 'log', "#{environment}.log")
428
+ end
429
+
430
+ def default_log_level
431
+ environment == 'production' ? :info : :debug
432
+ end
433
+
434
+ def error( msg )
435
+ msg = "[E] Configuration: #{msg}"
436
+
437
+ if DaemonKit.logger
438
+ DaemonKit.logger.error( msg )
439
+ else
440
+ STDERR.puts msg
441
+ end
442
+ end
443
+
444
+ # If we are executed with any of these commands, don't allow
445
+ # arguments to be parsed cause they will interfere with the
446
+ # script encapsulating DaemonKit, like capistrano
447
+ def own_args?
448
+ !%w( rake cap spec cucumber ).include?( File.basename( $0 ) )
449
+ end
450
+ end
451
+
452
+
453
+ end