amqp-daemon-kit 0.1.8.1

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 +131 -0
  5. data/Logging.txt +96 -0
  6. data/PostInstall.txt +6 -0
  7. data/README.rdoc +132 -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.rb +60 -0
  15. data/lib/daemon_kit/abstract_logger.rb +249 -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.rb +1 -0
  24. data/lib/daemon_kit/core_ext/configurable.rb +96 -0
  25. data/lib/daemon_kit/core_ext/string.rb +22 -0
  26. data/lib/daemon_kit/cron.rb +67 -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/dk_amqp.rb +39 -0
  30. data/lib/daemon_kit/em.rb +43 -0
  31. data/lib/daemon_kit/error_handlers/base.rb +32 -0
  32. data/lib/daemon_kit/error_handlers/hoptoad.rb +180 -0
  33. data/lib/daemon_kit/exceptions.rb +15 -0
  34. data/lib/daemon_kit/generators.rb +67 -0
  35. data/lib/daemon_kit/generators/base.rb +60 -0
  36. data/lib/daemon_kit/initializer.rb +449 -0
  37. data/lib/daemon_kit/jabber.rb +171 -0
  38. data/lib/daemon_kit/nanite.rb +7 -0
  39. data/lib/daemon_kit/nanite/agent.rb +77 -0
  40. data/lib/daemon_kit/pid_file.rb +61 -0
  41. data/lib/daemon_kit/ruote_participants.rb +125 -0
  42. data/lib/daemon_kit/ruote_pseudo_participant.rb +68 -0
  43. data/lib/daemon_kit/ruote_workitem.rb +187 -0
  44. data/lib/daemon_kit/safety.rb +84 -0
  45. data/lib/daemon_kit/tasks.rb +2 -0
  46. data/lib/daemon_kit/tasks/environment.rake +11 -0
  47. data/lib/daemon_kit/tasks/framework.rake +123 -0
  48. data/lib/daemon_kit/tasks/god.rake +62 -0
  49. data/lib/daemon_kit/tasks/log.rake +8 -0
  50. data/lib/daemon_kit/tasks/monit.rake +29 -0
  51. data/lib/daemon_kit/vendor/thor-0.13.6/CHANGELOG.rdoc +89 -0
  52. data/lib/daemon_kit/vendor/thor-0.13.6/LICENSE +20 -0
  53. data/lib/daemon_kit/vendor/thor-0.13.6/README.rdoc +297 -0
  54. data/lib/daemon_kit/vendor/thor-0.13.6/Thorfile +69 -0
  55. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor.rb +244 -0
  56. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions.rb +296 -0
  57. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/create_file.rb +103 -0
  58. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/directory.rb +91 -0
  59. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/empty_directory.rb +134 -0
  60. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/file_manipulation.rb +223 -0
  61. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/actions/inject_into_file.rb +104 -0
  62. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/base.rb +540 -0
  63. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/core_ext/file_binary_read.rb +9 -0
  64. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/core_ext/hash_with_indifferent_access.rb +75 -0
  65. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/core_ext/ordered_hash.rb +100 -0
  66. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/error.rb +30 -0
  67. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/group.rb +271 -0
  68. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/invocation.rb +180 -0
  69. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser.rb +4 -0
  70. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/argument.rb +67 -0
  71. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/arguments.rb +150 -0
  72. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/option.rb +128 -0
  73. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/parser/options.rb +169 -0
  74. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/rake_compat.rb +66 -0
  75. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/runner.rb +314 -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/shell/basic.rb +239 -0
  78. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/shell/color.rb +108 -0
  79. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/task.rb +102 -0
  80. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/util.rb +224 -0
  81. data/lib/daemon_kit/vendor/thor-0.13.6/lib/thor/version.rb +3 -0
  82. data/lib/daemon_kit/xmpp.rb +100 -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 +12 -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.rb.tt +67 -0
  111. data/lib/generators/daemon_kit/capistrano/templates/config/deploy/logrotate.erb +13 -0
  112. data/lib/generators/daemon_kit/capistrano/templates/config/deploy/production.rb.tt +6 -0
  113. data/lib/generators/daemon_kit/capistrano/templates/config/deploy/staging.rb.tt +6 -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 +48 -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 +21 -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 +20 -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 +302 -0
@@ -0,0 +1,39 @@
1
+ require 'yaml'
2
+ require 'mq'
3
+
4
+ module DaemonKit
5
+ # Thin wrapper around the amqp gem, specifically designed to ease
6
+ # configuration of a AMQP consumer daemon and provide some added
7
+ # simplicity
8
+ class AMQP
9
+
10
+ @@instance = nil
11
+
12
+ class << self
13
+
14
+ def instance
15
+ @instance ||= new
16
+ end
17
+
18
+ private :new
19
+
20
+ def run(&block)
21
+ instance.run(&block)
22
+ end
23
+ end
24
+
25
+ def initialize( config = {} )
26
+ @config = DaemonKit::Config.load('amqp').to_h( true )
27
+ end
28
+
29
+ def run(&block)
30
+ # Ensure graceful shutdown of the connection to the broker
31
+ DaemonKit.trap('INT') { ::AMQP.stop { ::EM.stop } }
32
+ DaemonKit.trap('TERM') { ::AMQP.stop { ::EM.stop } }
33
+
34
+ # Start our event loop and AMQP client
35
+ DaemonKit.logger.debug("AMQP.start(#{@config.inspect})")
36
+ ::AMQP.start(@config, &block)
37
+ end
38
+ end
39
+ end
@@ -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,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,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,449 @@
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
+ require File.dirname(__FILE__) + '/core_ext'
7
+ $LOAD_PATH.unshift( File.expand_path('../', __FILE__).to_absolute_path ) unless
8
+ $LOAD_PATH.include?( File.expand_path('../', __FILE__).to_absolute_path )
9
+
10
+ require 'daemon_kit'
11
+
12
+ module DaemonKit
13
+
14
+ class << self
15
+
16
+ def configuration
17
+ @configuration
18
+ end
19
+
20
+ def configuration=( configuration )
21
+ @configuration = configuration
22
+ end
23
+
24
+ def arguments
25
+ @arguments
26
+ end
27
+
28
+ def arguments=( args )
29
+ @arguments = args
30
+ end
31
+
32
+ def trap( *args, &block )
33
+ self.configuration.trap( *args, &block )
34
+ end
35
+
36
+ def at_shutdown( &block )
37
+ self.configuration.at_shutdown( &block )
38
+ end
39
+
40
+ end
41
+
42
+
43
+ # This class does all the nightmare work of setting up a working
44
+ # environment for your daemon.
45
+ class Initializer
46
+
47
+ attr_reader :configuration
48
+
49
+ def self.run
50
+ configuration = DaemonKit.configuration || Configuration.new
51
+
52
+ yield configuration if block_given?
53
+ initializer = new configuration
54
+ initializer.before_daemonize
55
+ initializer
56
+ end
57
+
58
+ def self.continue!
59
+ initializer = new DaemonKit.configuration
60
+ initializer.after_daemonize
61
+ end
62
+
63
+ def self.shutdown( clean = false, do_exit = false )
64
+ return unless $daemon_kit_shutdown_hooks_ran.nil?
65
+ $daemon_kit_shutdown_hooks_ran = true
66
+
67
+ DaemonKit.logger.info "Running shutdown hooks"
68
+
69
+ DaemonKit.configuration.shutdown_hooks.each do |hook|
70
+ begin
71
+ hook.call
72
+ rescue => e
73
+ DaemonKit.logger.exception( e )
74
+ end
75
+ end
76
+
77
+ log_exceptions if DaemonKit.configuration.backtraces && !clean
78
+
79
+ DaemonKit.logger.warn "Shutting down #{DaemonKit.configuration.daemon_name}"
80
+
81
+ exit if do_exit
82
+ end
83
+
84
+ def initialize( configuration )
85
+ @configuration = configuration
86
+ end
87
+
88
+ def before_daemonize
89
+ DaemonKit.configuration = @configuration
90
+
91
+ set_load_path
92
+ load_gems
93
+ load_patches
94
+ load_environment
95
+ load_predaemonize_configs
96
+ end
97
+
98
+ def after_daemonize
99
+ set_umask
100
+
101
+ initialize_logger
102
+ initialize_signal_traps
103
+
104
+ include_core_lib
105
+ load_postdaemonize_configs
106
+ configure_backtraces
107
+
108
+ set_process_name
109
+
110
+ DaemonKit.logger.info( "DaemonKit (#{DaemonKit::VERSION}) booted, now running #{DaemonKit.configuration.daemon_name}" )
111
+
112
+ if DaemonKit.configuration.user || DaemonKit.configuration.group
113
+ euid = Process.euid
114
+ egid = Process.egid
115
+ uid = Process.uid
116
+ gid = Process.gid
117
+ DaemonKit.logger.info( "DaemonKit dropped privileges to: #{euid} (EUID), #{egid} (EGID), #{uid} (UID), #{gid} (GID)" )
118
+ end
119
+ end
120
+
121
+ def set_load_path
122
+ configuration.load_paths.each do |d|
123
+ $:.unshift( "#{DAEMON_ROOT}/#{d}" ) if File.directory?( "#{DAEMON_ROOT}/#{d}" )
124
+ end
125
+ end
126
+
127
+ def load_gems
128
+
129
+ end
130
+
131
+ def load_patches
132
+
133
+ end
134
+
135
+ def load_environment
136
+ # Needs to be global to prevent loading the files twice
137
+ return if $_daemon_environment_loaded
138
+ $_daemon_environment_loaded = true
139
+
140
+ config = configuration
141
+
142
+ eval(IO.read(configuration.environment_path), binding, configuration.environment_path)
143
+
144
+ eval(IO.read(configuration.daemon_initializer), binding, configuration.daemon_initializer) if File.exist?( configuration.daemon_initializer )
145
+ end
146
+
147
+ def load_predaemonize_configs
148
+ Dir[ File.join( DAEMON_ROOT, 'config', 'pre-daemonize', '*.rb' ) ].each do |f|
149
+ next if File.basename( f ) == File.basename( configuration.daemon_initializer )
150
+
151
+ require f
152
+ end
153
+ end
154
+
155
+ def load_postdaemonize_configs
156
+ Dir[ File.join( DAEMON_ROOT, 'config', 'post-daemonize', '*.rb' ) ].each do |f|
157
+ require f
158
+ end
159
+ end
160
+
161
+ def set_umask
162
+ File.umask configuration.umask
163
+ end
164
+
165
+ def initialize_logger
166
+ return if DaemonKit.logger
167
+
168
+ unless logger = configuration.logger
169
+ logger = AbstractLogger.new( configuration.log_path )
170
+ logger.level = configuration.log_level
171
+ logger.copy_to_stdout = configuration.log_stdout
172
+ end
173
+
174
+ DaemonKit.logger = logger
175
+
176
+ DaemonKit.logger.info "DaemonKit (#{DaemonKit::VERSION}) booting in #{DAEMON_ENV} mode"
177
+
178
+ configuration.trap("USR1") {
179
+ DaemonKit.logger.level = DaemonKit.logger.debug? ? :info : :debug
180
+ DaemonKit.logger.info "Log level changed to #{DaemonKit.logger.debug? ? 'DEBUG' : 'INFO' }"
181
+ }
182
+ configuration.trap("USR2") {
183
+ DaemonKit.logger.level = :debug
184
+ DaemonKit.logger.info "Log level changed to DEBUG"
185
+ }
186
+ configuration.trap("HUP") {
187
+ DaemonKit::Application.reopen_logs
188
+ }
189
+ end
190
+
191
+ def initialize_signal_traps
192
+ # Only exit the process if we're not in the 'test' environment
193
+ term_proc = Proc.new { DaemonKit::Initializer.shutdown( true, DAEMON_ENV != 'test' ) }
194
+ configuration.trap( 'INT', term_proc )
195
+ configuration.trap( 'TERM', term_proc )
196
+ at_exit { DaemonKit::Initializer.shutdown }
197
+ end
198
+
199
+ def include_core_lib
200
+ if File.exists?( core_lib = File.join( DAEMON_ROOT, 'lib', configuration.daemon_name + '.rb' ) )
201
+ require core_lib
202
+ end
203
+ end
204
+
205
+ def configure_backtraces
206
+ Thread.abort_on_exception = configuration.backtraces
207
+ end
208
+
209
+ def set_process_name
210
+ $0 = configuration.daemon_name
211
+ end
212
+
213
+ def self.log_exceptions
214
+ trace_file = File.join( DaemonKit.root, 'log', "backtrace-#{Time.now.strftime('%Y%m%d%H%M%S')}-#{Process.pid}.log" )
215
+ trace_log = Logger.new( trace_file )
216
+
217
+ # Find the last exception
218
+ e = nil
219
+ ObjectSpace.each_object {|o|
220
+ if ::Exception === o
221
+ e = o
222
+ end
223
+ }
224
+
225
+ 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 ***"
226
+ trace_log.error e
227
+
228
+ 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 ***"
229
+ ObjectSpace.each_object {|o|
230
+ if ::Exception === o
231
+ trace_log.error o
232
+ end
233
+ }
234
+
235
+ trace_log.close
236
+ end
237
+ end
238
+
239
+ # Holds our various configuration values
240
+ class Configuration
241
+
242
+ include Configurable
243
+
244
+ # Root to the daemon
245
+ attr_reader :root_path
246
+
247
+ # List of load paths
248
+ attr_accessor :load_paths
249
+
250
+ # Custom logger instance to use
251
+ attr_accessor :logger
252
+
253
+ # The log level to use, defaults to DEBUG
254
+ attr_reader :log_level
255
+
256
+ # Path to the log file, defaults to 'log/<environment>.log'
257
+ configurable :log_path
258
+
259
+ # Duplicate log data to stdout
260
+ attr_accessor :log_stdout
261
+
262
+ # Path to the pid file, defaults to 'log/<daemon_name>.pid'
263
+ attr_accessor :pid_file
264
+
265
+ # The application name
266
+ configurable :daemon_name, :locked => true
267
+
268
+ # Use the force kill patch? Give the number of seconds
269
+ configurable :force_kill_wait
270
+
271
+ # Should be log backtraces
272
+ configurable :backtraces, false
273
+
274
+ # Configurable umask
275
+ configurable :umask, 0022
276
+
277
+ # Configurable user
278
+ configurable :user, :locked => true
279
+
280
+ # Confgiruable group
281
+ configurable :group, :locked => true
282
+
283
+ # Collection of signal traps
284
+ attr_reader :signal_traps
285
+
286
+ # Our safety net (#Safety) instance
287
+ attr_accessor :safety_net
288
+
289
+ # :nodoc: Shutdown hooks
290
+ attr_reader :shutdown_hooks
291
+
292
+ def initialize
293
+ parse_arguments!
294
+
295
+ set_root_path!
296
+ set_daemon_defaults!
297
+
298
+ self.load_paths = default_load_paths
299
+ self.log_level ||= default_log_level
300
+ self.log_path ||= default_log_path
301
+
302
+ self.force_kill_wait = false
303
+
304
+ self.safety_net = DaemonKit::Safety.instance
305
+
306
+ @signal_traps = {}
307
+ @shutdown_hooks = []
308
+ end
309
+
310
+ def environment
311
+ ::DAEMON_ENV
312
+ end
313
+
314
+ # The path to the current environment's file (<tt>development.rb</tt>, etc.). By
315
+ # default the file is at <tt>config/environments/#{environment}.rb</tt>.
316
+ def environment_path
317
+ "#{root_path}/config/environments/#{environment}.rb"
318
+ end
319
+
320
+ def daemon_initializer
321
+ "#{root_path}/config/initializers/#{self.daemon_name}.rb"
322
+ end
323
+
324
+ # Add a trap for the specified signal, can be code block or a proc
325
+ def trap( signal, proc = nil, &block )
326
+ return if proc.nil? && !block_given?
327
+
328
+ # One step towards running on windows, not enough though
329
+ unless Signal.list.include?( signal )
330
+ DaemonKit.logger.warn( "Trapping #{signal} signals not supported on this platform" )
331
+ return
332
+ end
333
+
334
+ unless @signal_traps.has_key?( signal )
335
+ set_trap( signal )
336
+ end
337
+
338
+ @signal_traps[signal].unshift( proc || block )
339
+ end
340
+
341
+ # Add a block or proc to be called during shutdown
342
+ def at_shutdown( proc = nil, &block )
343
+ return if proc.nil? && !block_given?
344
+
345
+ @shutdown_hooks << ( proc || block )
346
+ end
347
+
348
+ def pid_file
349
+ @pid_file ||= "#{File.dirname(self.log_path)}/#{self.daemon_name}.pid"
350
+ end
351
+
352
+ # Set the log level
353
+ def log_level=( level )
354
+ @log_level = level
355
+ DaemonKit.logger.level = @log_level if DaemonKit.logger
356
+ end
357
+
358
+ protected
359
+
360
+ def run_traps( signal )
361
+ DaemonKit.logger.info "Running signal traps for #{signal}"
362
+ self.signal_traps[ signal ].each { |trap| trap.call }
363
+ end
364
+
365
+ private
366
+
367
+ def set_trap( signal )
368
+ DaemonKit.logger.info "Setting up trap for #{signal}"
369
+ @signal_traps[ signal ] = []
370
+ Signal.trap( signal, Proc.new { self.run_traps( signal ) } )
371
+ end
372
+
373
+ def parse_arguments!
374
+ return unless own_args?
375
+
376
+ configs = Arguments.configuration( ARGV ).first
377
+ @unused_arguments = {}
378
+
379
+ configs.each do |c|
380
+ k,v = c.split('=')
381
+
382
+ if v.nil?
383
+ error( "#{k} has no value" )
384
+ next
385
+ end
386
+
387
+ begin
388
+ if self.respond_to?( k )
389
+ self.send( "#{k}=", v ) # pid_file = /var/run/foo.pid
390
+ else
391
+ @unused_arguments[ k ] = v
392
+ end
393
+ rescue => e
394
+ error( "Couldn't set `#{k}' to `#{v}': #{e.message}" )
395
+ end
396
+ end
397
+ end
398
+
399
+ # DANGEROUS: Change the value of DAEMON_ENV
400
+ def environment=( env )
401
+ ::DAEMON_ENV.replace( env )
402
+ end
403
+
404
+ def set_root_path!
405
+ raise "DAEMON_ROOT is not set" unless defined?(::DAEMON_ROOT)
406
+ raise "DAEMON_ROOT is not a directory" unless File.directory?(::DAEMON_ROOT)
407
+
408
+ @root_path = ::DAEMON_ROOT.to_absolute_path
409
+
410
+ Object.const_set(:RELATIVE_DAEMON_ROOT, ::DAEMON_ROOT.dup) unless defined?(::RELATIVE_DAEMON_ROOT)
411
+ ::DAEMON_ROOT.replace @root_path
412
+ end
413
+
414
+ def set_daemon_defaults!
415
+ self.log_stdout = false
416
+ end
417
+
418
+ def default_load_paths
419
+ [ 'lib' ]
420
+ end
421
+
422
+ def default_log_path
423
+ File.join(root_path, 'log', "#{environment}.log")
424
+ end
425
+
426
+ def default_log_level
427
+ environment == 'production' ? :info : :debug
428
+ end
429
+
430
+ def error( msg )
431
+ msg = "[E] Configuration: #{msg}"
432
+
433
+ if DaemonKit.logger
434
+ DaemonKit.logger.error( msg )
435
+ else
436
+ STDERR.puts msg
437
+ end
438
+ end
439
+
440
+ # If we are executed with any of these commands, don't allow
441
+ # arguments to be parsed cause they will interfere with the
442
+ # script encapsulating DaemonKit, like capistrano
443
+ def own_args?
444
+ !%w( rake cap spec cucumber ).include?( File.basename( $0 ) )
445
+ end
446
+ end
447
+
448
+
449
+ end