diff --git a/README b/README new file mode 100644 index 0000000..af5f5fb --- /dev/null +++ b/README @@ -0,0 +1,48 @@ +DaemonKit README +================ + +DaemonKit has generated a skeleton Ruby daemon for you to build on. Please read +through this file to ensure you get going quickly. + +Directories +=========== + +bin/ + bunnicula - Stub executable to control your daemon with + +config/ + Environment configuration files + +lib/ + Place for your libraries + +libexec/ + bunnicula.rb - Your daemon code + +log/ + Log files based on the environment name + +spec/ + rspec's home + +tasks/ + Place for rake tasks + +vendor/ + Place for unpacked gems and DaemonKit + +tmp/ + Scratch folder + + +Logging +======= + +One of the biggest issues with writing daemons are gettign insight into what your +daemons are doing. Logging with DaemonKit is simplified as DaemonKit creates log +files per environment in log. + +On all environments except production the log level is set to DEBUG, but you can +toggle the log level by sending the running daemon SIGUSR1 and SIGUSR2 signals. +SIGUSR1 will toggle between DEBUG and INFO levels, SIGUSR2 will blatantly set the +level to DEBUG. diff --git a/Rakefile b/Rakefile new file mode 100644 index 0000000..aab9cc4 --- /dev/null +++ b/Rakefile @@ -0,0 +1,6 @@ +require File.dirname(__FILE__) + '/config/boot' + +require 'rake' +require 'daemon_kit/tasks' + +Dir[File.join(File.dirname(__FILE__), 'tasks/*.rake')].each { |rake| load rake } diff --git a/bin/bunnicula b/bin/bunnicula new file mode 100644 index 0000000..5638ea1 --- /dev/null +++ b/bin/bunnicula @@ -0,0 +1,7 @@ +#!/usr/bin/env ruby +# +# Stub executable for bunnicula + +require File.dirname(__FILE__) + '/../config/environment' + +DaemonKit::Application.exec( DAEMON_ROOT + '/libexec/bunnicula-daemon.rb' ) diff --git a/config/amqp.yml b/config/amqp.yml new file mode 100644 index 0000000..f06cfe7 --- /dev/null +++ b/config/amqp.yml @@ -0,0 +1,28 @@ +# AMQP client configuration file + +# These values will be used to configure the ampq gem, any values +# omitted will let the gem use it's own defaults. + +# The configuration specifies the following keys: +# * user - Username for the broker +# * pass - Password for the broker +# * host - Hostname where the broker is running +# * vhost - Vhost to connect to +# * port - Port where the broker is running +# * ssl - Use ssl or not +# * timeout - Timeout + +defaults: &defaults + user: guest + pass: guest + host: localhost + vhost: / + +development: + <<: *defaults + +test: + <<: *defaults + +production: + <<: *defaults diff --git a/config/arguments.rb b/config/arguments.rb new file mode 100644 index 0000000..6b2cb45 --- /dev/null +++ b/config/arguments.rb @@ -0,0 +1,12 @@ +# Argument handling for your daemon is configured here. +# +# You have access to two variables when this file is +# parsed. The first is +opts+, which is the object yielded from +# +OptionParser.new+, the second is +@options+ which is a standard +# Ruby hash that is later accessible through +# DaemonKit.arguments.options and can be used in your daemon process. + +# Here is an example: +# opts.on('-f', '--foo FOO', 'Set foo') do |foo| +# @options[:foo] = foo +# end diff --git a/config/boot.rb b/config/boot.rb new file mode 100644 index 0000000..0eaecd3 --- /dev/null +++ b/config/boot.rb @@ -0,0 +1,59 @@ +# Don't change this file! +# Configure your daemon in config/environment.rb + +DAEMON_ROOT = "#{File.expand_path(File.dirname(__FILE__))}/.." unless defined?( DAEMON_ROOT ) + +module DaemonKit + class << self + def boot! + unless booted? + pick_boot.run + end + end + + def booted? + defined? DaemonKit::Initializer + end + + def pick_boot + (vendor_kit? ? VendorBoot : GemBoot).new + end + + def vendor_kit? + File.exists?( "#{DAEMON_ROOT}/vendor/daemon_kit" ) + end + end + + class Boot + def run + load_initializer + DaemonKit::Initializer.run + end + end + + class VendorBoot < Boot + def load_initializer + require "#{DAEMON_ROOT}/vendor/daemon_kit/lib/daemon_kit/initializer" + end + end + + class GemBoot < Boot + def load_initializer + begin + gem 'daemon-kit' + require 'daemon_kit/initializer' + rescue Gem::LoadError => e + msg = <.rb' and loads +# that file first, and inside a DaemonKit::Initializer block. The +# remaning files then simply required into the running process. +# +# These files are mostly useful for operations that should fail blatantly +# before daemonizing, like loading gems. +# +# Be careful not to open any form of IO in here and expecting it to be +# open inside the running daemon since all IO instances are closed when +# daemonizing (including STDIN, STDOUT & STDERR). diff --git a/lib/bunnicula.rb b/lib/bunnicula.rb new file mode 100644 index 0000000..ce52554 --- /dev/null +++ b/lib/bunnicula.rb @@ -0,0 +1,2 @@ +# Your starting point for daemon specific classes. This directory is +# already included in your load path, so no need to specify it. diff --git a/libexec/bunnicula-daemon.rb b/libexec/bunnicula-daemon.rb new file mode 100644 index 0000000..c950dc3 --- /dev/null +++ b/libexec/bunnicula-daemon.rb @@ -0,0 +1,37 @@ +# Generated amqp daemon + +# Do your post daemonization configuration here +# At minimum you need just the first line (without the block), or a lot +# of strange things might start happening... +DaemonKit::Application.running! do |config| + # Trap signals with blocks or procs + # config.trap( 'INT' ) do + # # do something clever + # end + # config.trap( 'TERM', Proc.new { puts 'Going down' } ) +end + +# IMPORTANT CONFIGURATION NOTE +# +# Please review and update 'config/amqp.yml' accordingly or this +# daemon won't work as advertised. + +# Run an event-loop for processing +DaemonKit::AMQP.run do + # Inside this block we're running inside the reactor setup by the + # amqp gem. Any code in the examples (from the gem) would work just + # fine here. + + # Uncomment this for connection keep-alive + # AMQP.conn.connection_status do |status| + # DaemonKit.logger.debug("AMQP connection status changed: #{status}") + # if status == :disconnected + # AMQP.conn.reconnect(true) + # end + # end + + amq = ::MQ.new + amq.queue('test').subscribe do |msg| + DaemonKit.logger.debug "Received message: #{msg.inspect}" + end +end diff --git a/script/console b/script/console new file mode 100644 index 0000000..63987a3 --- /dev/null +++ b/script/console @@ -0,0 +1,3 @@ +#!/usr/bin/env ruby +require File.dirname(__FILE__) + '/../config/boot' +require 'daemon_kit/commands/console' diff --git a/script/destroy b/script/destroy new file mode 100644 index 0000000..138f8a3 --- /dev/null +++ b/script/destroy @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/destroy' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:daemon, :test_unit] +RubiGen::Scripts::Destroy.new.run(ARGV) diff --git a/script/generate b/script/generate new file mode 100644 index 0000000..0866e2a --- /dev/null +++ b/script/generate @@ -0,0 +1,14 @@ +#!/usr/bin/env ruby +APP_ROOT = File.expand_path(File.join(File.dirname(__FILE__), '..')) + +begin + require 'rubigen' +rescue LoadError + require 'rubygems' + require 'rubigen' +end +require 'rubigen/scripts/generate' + +ARGV.shift if ['--help', '-h'].include?(ARGV[0]) +RubiGen::Base.use_component_sources! [:daemon] +RubiGen::Scripts::Generate.new.run(ARGV) diff --git a/spec/bunnicula_spec.rb b/spec/bunnicula_spec.rb new file mode 100644 index 0000000..a52055a --- /dev/null +++ b/spec/bunnicula_spec.rb @@ -0,0 +1,11 @@ +require File.dirname(__FILE__) + '/spec_helper.rb' + +# Time to add your specs! +# http://rspec.info/ +describe "Place your specs here" do + + it "find this spec in spec directory" do + violated "Be sure to write your specs" + end + +end diff --git a/spec/spec.opts b/spec/spec.opts new file mode 100644 index 0000000..cf6add7 --- /dev/null +++ b/spec/spec.opts @@ -0,0 +1 @@ +--colour \ No newline at end of file diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb new file mode 100644 index 0000000..93e16c3 --- /dev/null +++ b/spec/spec_helper.rb @@ -0,0 +1,23 @@ +DAEMON_ENV = 'test' unless defined?( DAEMON_ENV ) + +begin + require 'spec' +rescue LoadError + require 'rubygems' + gem 'rspec' + require 'spec' +end + +require File.dirname(__FILE__) + '/../config/environment' +DaemonKit::Application.running! + +Spec::Runner.configure do |config| + # == Mock Framework + # + # RSpec uses it's own mocking framework by default. If you prefer to + # use mocha, flexmock or RR, uncomment the appropriate line: + # + # config.mock_with :mocha + # config.mock_with :flexmock + # config.mock_with :rr +end diff --git a/tasks/rspec.rake b/tasks/rspec.rake new file mode 100644 index 0000000..ba292dc --- /dev/null +++ b/tasks/rspec.rake @@ -0,0 +1,19 @@ +begin + require 'spec' + require 'spec/rake/spectask' +rescue LoadError + puts <<-EOS +To use rspec for testing you must install rspec gem: + gem install rspec +EOS +end + +begin + desc "Run the specs under spec/" + Spec::Rake::SpecTask.new do |t| + t.spec_opts = ['--options', "spec/spec.opts"] + t.spec_files = FileList['spec/**/*_spec.rb'] + end +rescue NameError + # No loss, warning printed already +end