jeffrafter-marvin 0.1.20081115
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.
- data/README.textile +110 -0
- data/VERSION.yml +4 -0
- data/bin/marvin +67 -0
- data/config/settings.yml.sample +13 -0
- data/config/setup.rb +14 -0
- data/handlers/hello_world.rb +9 -0
- data/handlers/logging_handler.rb +87 -0
- data/handlers/tweet_tweet.rb +21 -0
- data/lib/marvin/abstract_client.rb +210 -0
- data/lib/marvin/abstract_parser.rb +19 -0
- data/lib/marvin/base.rb +121 -0
- data/lib/marvin/command_handler.rb +62 -0
- data/lib/marvin/core_ext.rb +11 -0
- data/lib/marvin/data_store.rb +73 -0
- data/lib/marvin/dispatchable.rb +94 -0
- data/lib/marvin/drb_handler.rb +7 -0
- data/lib/marvin/exception_tracker.rb +16 -0
- data/lib/marvin/exceptions.rb +8 -0
- data/lib/marvin/handler.rb +12 -0
- data/lib/marvin/irc/abstract_server.rb +4 -0
- data/lib/marvin/irc/base_server.rb +11 -0
- data/lib/marvin/irc/client.rb +105 -0
- data/lib/marvin/irc/event.rb +30 -0
- data/lib/marvin/irc/socket_client.rb +69 -0
- data/lib/marvin/irc.rb +9 -0
- data/lib/marvin/loader.rb +68 -0
- data/lib/marvin/logger.rb +23 -0
- data/lib/marvin/middle_man.rb +103 -0
- data/lib/marvin/parsers/regexp_parser.rb +96 -0
- data/lib/marvin/parsers/simple_parser/default_events.rb +37 -0
- data/lib/marvin/parsers/simple_parser/event_extensions.rb +14 -0
- data/lib/marvin/parsers/simple_parser/prefixes.rb +34 -0
- data/lib/marvin/parsers/simple_parser.rb +101 -0
- data/lib/marvin/parsers.rb +7 -0
- data/lib/marvin/settings.rb +77 -0
- data/lib/marvin/test_client.rb +60 -0
- data/lib/marvin/util.rb +30 -0
- data/lib/marvin.rb +44 -0
- data/script/daemon-runner +12 -0
- data/script/run +25 -0
- data/spec/marvin/abstract_client_test.rb +38 -0
- data/spec/spec_helper.rb +14 -0
- metadata +99 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
module Marvin
|
2
|
+
|
3
|
+
# A Simple Marvin handler based on processing
|
4
|
+
# commands, similar in design to MatzBot.
|
5
|
+
class CommandHandler < Base
|
6
|
+
|
7
|
+
class_inheritable_accessor :exposed_methods, :command_prefix
|
8
|
+
|
9
|
+
self.command_prefix = ""
|
10
|
+
self.exposed_methods = []
|
11
|
+
|
12
|
+
class << self
|
13
|
+
|
14
|
+
def exposes(*args)
|
15
|
+
self.exposed_methods ||= []
|
16
|
+
self.exposed_methods += args.map { |a| a.to_sym }.flatten
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
on_event :incoming_message do
|
22
|
+
logger.debug "Incoming message"
|
23
|
+
check_for_commands
|
24
|
+
end
|
25
|
+
|
26
|
+
def check_for_commands
|
27
|
+
data, command = nil, nil
|
28
|
+
if self.from_channel?
|
29
|
+
logger.debug "Processing command in channel"
|
30
|
+
split_message = options.message.split(" ", 3)
|
31
|
+
prefix = split_message.shift
|
32
|
+
# Return if in channel and it isn't address to the user.
|
33
|
+
return unless prefix == "#{self.client.nickname}:"
|
34
|
+
command, data = split_message # Set remaining.
|
35
|
+
else
|
36
|
+
command, data = options.message.split(" ", 2)
|
37
|
+
end
|
38
|
+
# Double check for sanity
|
39
|
+
return if command.blank?
|
40
|
+
command_name = extract_command_name(command)
|
41
|
+
unless command_name.nil?
|
42
|
+
logger.debug "Command Exists - processing"
|
43
|
+
# Dispatch the command.
|
44
|
+
self.send(command_name, data.to_a) if self.respond_to?(command_name)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def extract_command_name(command)
|
49
|
+
prefix_length = self.command_prefix.to_s.length
|
50
|
+
has_prefix = command[0...prefix_length] == self.command_prefix.to_s
|
51
|
+
logger.debug "Debugging, prefix is #{prefix_length} characters, has prefix? = #{has_prefix}"
|
52
|
+
if has_prefix
|
53
|
+
# Normalize the method name
|
54
|
+
method_name = command[prefix_length..-1].to_s.underscore.to_sym
|
55
|
+
logger.debug "Computed method name is #{method_name.inspect}"
|
56
|
+
return method_name if self.exposed_methods.to_a.include?(method_name)
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
@@ -0,0 +1,73 @@
|
|
1
|
+
require 'json'
|
2
|
+
|
3
|
+
module Marvin
|
4
|
+
# Implements a simple datastore interface, designed to make
|
5
|
+
# it easy to develop handlers which have persistent data.
|
6
|
+
class DataStore
|
7
|
+
|
8
|
+
cattr_accessor :logger, :registered_stores
|
9
|
+
self.logger = Marvin::Logger.logger
|
10
|
+
self.registered_stores = {}
|
11
|
+
|
12
|
+
# Returns the path to the data store relative to this file.
|
13
|
+
# Used when loading / dumping the data.
|
14
|
+
def self.datastore_location
|
15
|
+
path = Marvin::Settings[:datastore_location] ? Marvin::Settings.datastore_location : "tmp/datastore.json"
|
16
|
+
return Marvin::Settings.root / path
|
17
|
+
end
|
18
|
+
|
19
|
+
# Dump the current data store contents to file.
|
20
|
+
def self.dump!
|
21
|
+
File.open(self.datastore_location, "w+") do |f|
|
22
|
+
f.write self.registered_stores.to_json
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Load the current data store contents from file.
|
27
|
+
def self.load!
|
28
|
+
results = {}
|
29
|
+
if File.exists?(self.datastore_location)
|
30
|
+
begin
|
31
|
+
json = JSON.load(File.read(self.datastore_location))
|
32
|
+
results = json if json.is_a?(Hash)
|
33
|
+
rescue JSON::ParserError
|
34
|
+
end
|
35
|
+
end
|
36
|
+
self.registered_stores = results
|
37
|
+
end
|
38
|
+
|
39
|
+
|
40
|
+
# For each individual datastore.
|
41
|
+
|
42
|
+
attr_accessor :name
|
43
|
+
|
44
|
+
def initialize(name)
|
45
|
+
self.name = name.to_s
|
46
|
+
self.registered_stores ||= {}
|
47
|
+
self.registered_stores[self.name] ||= {}
|
48
|
+
end
|
49
|
+
|
50
|
+
def [](key)
|
51
|
+
((self.registered_stores||={})[self.name]||={})[key.to_s]
|
52
|
+
end
|
53
|
+
|
54
|
+
def []=(key,value)
|
55
|
+
self.registered_stores[self.name][key.to_s] = value
|
56
|
+
end
|
57
|
+
|
58
|
+
def method_missing(name, *args, &blk)
|
59
|
+
if name.to_s =~ /^(.*)=$/i
|
60
|
+
self[$1.to_s] = args.first
|
61
|
+
elsif self.registered_stores[self.name].has_key?(name.to_s)
|
62
|
+
return self.registered_stores[self.name][name.to_s]
|
63
|
+
else
|
64
|
+
super(name, *args, &blk)
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
def to_hash
|
69
|
+
self.registered_stores[self.name]
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Marvin
|
2
|
+
# = Marvin::Dispatchable
|
3
|
+
# A Generic mixin which lets you define an object
|
4
|
+
# Which accepts handlers which can have arbitrary
|
5
|
+
# events dispatched.
|
6
|
+
# == Usage
|
7
|
+
#
|
8
|
+
# class X
|
9
|
+
# include Marvin::Dispatchable
|
10
|
+
# self.handlers << SomeHandler.new
|
11
|
+
# end
|
12
|
+
# X.new.dispatch(:name, {:args => "Values"})
|
13
|
+
#
|
14
|
+
# Will first check if SomeHandler#handle_name exists,
|
15
|
+
# calling handle_name({:args => "Values"}) if it does,
|
16
|
+
# otherwise calling SomeHandler#handle(:name, {:args => "Values"})
|
17
|
+
module Dispatchable
|
18
|
+
|
19
|
+
def self.included(parent)
|
20
|
+
parent.class_eval do
|
21
|
+
include InstanceMethods
|
22
|
+
extend ClassMethods
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module InstanceMethods
|
27
|
+
|
28
|
+
# Returns the handlers registered on this class,
|
29
|
+
# used inside +dispatch+.
|
30
|
+
def handlers
|
31
|
+
self.class.handlers
|
32
|
+
end
|
33
|
+
|
34
|
+
# Dispatch an 'event' with a given name to the handlers
|
35
|
+
# registered on the current class. Used as a nicer way of defining
|
36
|
+
# behaviours that should occur under a given set of circumstances.
|
37
|
+
# == Params
|
38
|
+
# +name+: The name of the current event
|
39
|
+
# +opts+: an optional hash of options to pass
|
40
|
+
def dispatch(name, opts = {})
|
41
|
+
# The full handler name is the method we call given it exists.
|
42
|
+
full_handler_name = :"handle_#{name.to_s.underscore}"
|
43
|
+
# First, dispatch locally if the method is defined.
|
44
|
+
if self.respond_to?(full_handler_name)
|
45
|
+
self.send(full_handler_name, opts)
|
46
|
+
end
|
47
|
+
# Iterate through all of the registered handlers,
|
48
|
+
# If there is a method named handle_<event_name>
|
49
|
+
# defined we sent that otherwise we call the handle
|
50
|
+
# method on the handler. Note that the handle method
|
51
|
+
# is the only required aspect of a handler. An improved
|
52
|
+
# version of this would likely cache the respond_to?
|
53
|
+
# call.
|
54
|
+
self.handlers.each do |handler|
|
55
|
+
if handler.respond_to?(full_handler_name)
|
56
|
+
handler.sent(full_handler_name, opts)
|
57
|
+
else
|
58
|
+
handler.handle name, opts
|
59
|
+
end
|
60
|
+
end
|
61
|
+
# If we get the HaltHandlerProcessing exception, we
|
62
|
+
# catch it and continue on our way. In essence, we
|
63
|
+
# stop the dispatch of events to the next set of the
|
64
|
+
# handlers.
|
65
|
+
rescue HaltHandlerProcessing
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
|
70
|
+
module ClassMethods
|
71
|
+
|
72
|
+
# Return an array of all registered handlers, stored in the
|
73
|
+
# class variable @@handlers. Used inside the #handlers instance
|
74
|
+
# method as well as inside things such as register_handler.
|
75
|
+
def handlers
|
76
|
+
@@handlers ||= []
|
77
|
+
end
|
78
|
+
|
79
|
+
# Assigns a new array of handlers and assigns each.
|
80
|
+
def handlers=(new_value)
|
81
|
+
@@handlers = []
|
82
|
+
new_value.to_a.each { |h| register_handler h }
|
83
|
+
end
|
84
|
+
|
85
|
+
# Appends a handler to the list of handlers for this object.
|
86
|
+
# Handlers are called in the order they are registered.
|
87
|
+
def register_handler(handler)
|
88
|
+
self.handlers << handler unless handler.nil? || !handler.respond_to?(:handle)
|
89
|
+
end
|
90
|
+
|
91
|
+
end
|
92
|
+
|
93
|
+
end
|
94
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module Marvin
|
2
|
+
class ExceptionTracker
|
3
|
+
|
4
|
+
cattr_accessor :logger
|
5
|
+
self.logger = Marvin::Logger.logger
|
6
|
+
|
7
|
+
def self.log(e)
|
8
|
+
logger.fatal "Exception raised inside Marvin Instance."
|
9
|
+
logger.fatal "#{e} - #{e.message}"
|
10
|
+
e.backtrace.each do |line|
|
11
|
+
logger.fatal line
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Marvin
|
2
|
+
module Handler
|
3
|
+
|
4
|
+
# Received a given +message+ with a set of default
|
5
|
+
# +opts+ (defaulting back to an empty hash), which
|
6
|
+
# will be used to perform some sort of action.
|
7
|
+
def handle(message, opts = {})
|
8
|
+
Marvin::Logger.debug "NOP handle - got message #{message.inspect}"
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
require 'eventmachine'
|
2
|
+
|
3
|
+
module Marvin::IRC
|
4
|
+
|
5
|
+
# == Marvin::IRC::Client
|
6
|
+
# An EventMachine protocol implementation built to
|
7
|
+
# serve as a basic, single server IRC client.
|
8
|
+
#
|
9
|
+
# Operates on the principal of Events as well
|
10
|
+
# as handlers.
|
11
|
+
#
|
12
|
+
# === Events
|
13
|
+
# Events are things that can happen (e.g. an
|
14
|
+
# incoming message). All outgoing events are
|
15
|
+
# automatically handled from within the client
|
16
|
+
# class. Incoming events are currently based
|
17
|
+
# on regular expression based matches of
|
18
|
+
# incoming messages. the Client#register_event
|
19
|
+
# method takes either an instance of Marvin::IRC::Event
|
20
|
+
# or a set of arguments which will then be used
|
21
|
+
# in the constructor of a new Marvin::IRC::Event
|
22
|
+
# instance (see, for example, the source code for
|
23
|
+
# this class for examples).
|
24
|
+
#
|
25
|
+
# === Handlers
|
26
|
+
# Handlers on the other hand do as the name suggests
|
27
|
+
# - they listen for dispatched events and act accordingly.
|
28
|
+
# Handlers are simply objects which follow a certain
|
29
|
+
# set of guidelines. Typically, a handler will at
|
30
|
+
# minimum respond to #handle(event_name, details)
|
31
|
+
# where event_name is a symbol for the current
|
32
|
+
# event (e.g. :incoming_event) whilst details is a
|
33
|
+
# a hash of details about the current event (e.g.
|
34
|
+
# message target and the message itself).
|
35
|
+
#
|
36
|
+
# ==== Getting the current client instance
|
37
|
+
# If the object responds to client=, The client will
|
38
|
+
# call it with the current instance of itself
|
39
|
+
# enabling the handler to do things such as respond.
|
40
|
+
# Also, if a method handle_[message_name] exists,
|
41
|
+
# it will be called instead of handle.
|
42
|
+
#
|
43
|
+
# ==== Adding handlers
|
44
|
+
# To add an object as a handler, you simply call
|
45
|
+
# the class method, register_handler with the
|
46
|
+
# handler as the only argument.
|
47
|
+
class Client < Marvin::AbstractClient
|
48
|
+
attr_accessor :em_connection
|
49
|
+
|
50
|
+
class EMConnection < EventMachine::Protocols::LineAndTextProtocol
|
51
|
+
attr_accessor :client
|
52
|
+
|
53
|
+
def initialize
|
54
|
+
super
|
55
|
+
self.client = Marvin::IRC::Client.new
|
56
|
+
self.client.em_connection = self
|
57
|
+
end
|
58
|
+
|
59
|
+
def post_init
|
60
|
+
client.process_connect
|
61
|
+
end
|
62
|
+
|
63
|
+
def unbind
|
64
|
+
client.process_disconnect
|
65
|
+
end
|
66
|
+
|
67
|
+
def receive_line(line)
|
68
|
+
self.client.receive_line(line)
|
69
|
+
end
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
def send_line(*args)
|
74
|
+
em_connection.send_data *args
|
75
|
+
end
|
76
|
+
|
77
|
+
## Client specific details
|
78
|
+
|
79
|
+
# Starts the EventMachine loop and hence starts up the actual
|
80
|
+
# networking portion of the IRC Client.
|
81
|
+
def self.run
|
82
|
+
self.setup # So we have options etc
|
83
|
+
EventMachine::run do
|
84
|
+
logger.debug "Connecting to #{self.configuration.server}:#{self.configuration.port}"
|
85
|
+
EventMachine::connect self.configuration.server, self.configuration.port, Marvin::IRC::Client::EMConnection
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
def self.stop
|
90
|
+
logger.debug "Telling all connections to quit"
|
91
|
+
self.connections.dup.each { |connection| connection.quit }
|
92
|
+
logger.debug "Telling Event Machine to Stop"
|
93
|
+
EventMachine::stop_event_loop
|
94
|
+
logger.debug "Stopped."
|
95
|
+
end
|
96
|
+
|
97
|
+
# Registers a callback handle that will be periodically run.
|
98
|
+
def periodically(timing, event_callback)
|
99
|
+
callback = proc { self.dispatch event_callback.to_sym }
|
100
|
+
EventMachine::add_periodic_timer(timing, &callback)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Marvin::IRC
|
2
|
+
class Event
|
3
|
+
attr_accessor :keys, :name, :raw_arguments
|
4
|
+
|
5
|
+
def initialize(name, *args)
|
6
|
+
self.name = name.to_sym
|
7
|
+
self.keys = args.flatten.map { |k| k.to_sym }
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_hash
|
11
|
+
return {} unless self.raw_arguments
|
12
|
+
results = {}
|
13
|
+
values = self.raw_arguments.to_a
|
14
|
+
last_index = self.keys.size - 1
|
15
|
+
self.keys.each_with_index do |key, i|
|
16
|
+
results[key] = (i == last_index ? values.join(" ").strip : values.shift)
|
17
|
+
end
|
18
|
+
return results
|
19
|
+
end
|
20
|
+
|
21
|
+
def inspect
|
22
|
+
"#<Marvin::IRC::Event name=#{self.name} attributes=[#{keys * ","}] >"
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_incoming_event_name
|
26
|
+
:"incoming_#{self.name}"
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
module Marvin::IRC
|
4
|
+
class SocketClient < Marvin::AbstractClient
|
5
|
+
attr_accessor :socket
|
6
|
+
|
7
|
+
def run
|
8
|
+
@socket = TCPSocket.new(self.configuration.server, self.configuration.port)
|
9
|
+
self.process_connect
|
10
|
+
self.enter_loop
|
11
|
+
end
|
12
|
+
|
13
|
+
def send_line(*args)
|
14
|
+
args.each { |l| @socket.write l } if !@socket.closed?
|
15
|
+
end
|
16
|
+
|
17
|
+
def disconnect_processed?
|
18
|
+
@disconnect_processed
|
19
|
+
end
|
20
|
+
|
21
|
+
def enter_loop
|
22
|
+
until @socket.closed?
|
23
|
+
line = @socket.readline.strip
|
24
|
+
receive_line line
|
25
|
+
end
|
26
|
+
self.process_disconnect unless self.disconnect_processed?
|
27
|
+
@disconnect_processed = true
|
28
|
+
rescue SystemExit
|
29
|
+
self.process_disconnect unless self.disconnect_processed?
|
30
|
+
@disconnect_processed = true
|
31
|
+
rescue Exception => e
|
32
|
+
Marvin::ExceptionTracker.log(e)
|
33
|
+
end
|
34
|
+
|
35
|
+
def quit(*args)
|
36
|
+
super(*args)
|
37
|
+
end
|
38
|
+
|
39
|
+
## Client specific details
|
40
|
+
|
41
|
+
def self.run
|
42
|
+
self.setup # So we have options etc
|
43
|
+
logger.debug "Connecting to #{self.configuration.server}:#{self.configuration.port}"
|
44
|
+
self.new.run
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.stop
|
48
|
+
logger.debug "Telling all connections to quit"
|
49
|
+
self.connections.each do |connection|
|
50
|
+
connection.quit
|
51
|
+
logger.debug "Preparing to close socket"
|
52
|
+
connection.socket.close
|
53
|
+
end
|
54
|
+
logger.debug "Stopped."
|
55
|
+
end
|
56
|
+
|
57
|
+
# Registers a callback handle that will be periodically run.
|
58
|
+
def periodically(timing, event_callback)
|
59
|
+
callback = proc { self.dispatch event_callback.to_sym }
|
60
|
+
Thread.new do
|
61
|
+
while true
|
62
|
+
callback.call
|
63
|
+
sleep timing
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
end
|
69
|
+
end
|
data/lib/marvin/irc.rb
ADDED
@@ -0,0 +1,9 @@
|
|
1
|
+
module Marvin
|
2
|
+
module IRC
|
3
|
+
autoload :Client, 'marvin/irc/client'
|
4
|
+
autoload :Event, 'marvin/irc/event'
|
5
|
+
autoload :SocketClient, 'marvin/irc/socket_client'
|
6
|
+
autoload :AbstractServer, 'marvin/irc/abstract_server'
|
7
|
+
autoload :BaseServer, 'marvin/irc/base_server'
|
8
|
+
end
|
9
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
module Marvin
|
2
|
+
class Loader
|
3
|
+
|
4
|
+
cattr_accessor :setup_block
|
5
|
+
|
6
|
+
cattr_accessor :start_hooks, :stop_hooks
|
7
|
+
self.stop_hooks, self.start_hooks = [], []
|
8
|
+
|
9
|
+
def self.before_connecting(&blk)
|
10
|
+
self.setup_block = blk
|
11
|
+
end
|
12
|
+
|
13
|
+
def setup_defaults
|
14
|
+
Marvin::Logger.setup
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.before_run(&blk)
|
18
|
+
self.start_hooks << blk unless blk.blank?
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.after_stop(&blk)
|
22
|
+
self.stop_hooks << blk unless blk.blank?
|
23
|
+
end
|
24
|
+
|
25
|
+
def load_handlers
|
26
|
+
handlers = Dir[Marvin::Settings.root / "handlers/**/*.rb"].map { |h| h[0..-4] }
|
27
|
+
handlers.each do |handler|
|
28
|
+
require handler
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def load_settings
|
33
|
+
Marvin::Settings.setup
|
34
|
+
Marvin::Settings.default_client.configuration = Marvin::Settings.to_hash
|
35
|
+
Marvin::Settings.default_client.setup
|
36
|
+
end
|
37
|
+
|
38
|
+
def pre_connect_setup
|
39
|
+
Marvin::DataStore.load!
|
40
|
+
require(Marvin::Settings.root / "config/setup")
|
41
|
+
self.setup_block.call unless self.setup_block.blank?
|
42
|
+
end
|
43
|
+
|
44
|
+
def run!
|
45
|
+
self.setup_defaults
|
46
|
+
self.load_settings
|
47
|
+
self.load_handlers
|
48
|
+
self.pre_connect_setup
|
49
|
+
self.start_hooks.each { |h| h.call }
|
50
|
+
Marvin::Settings.default_client.run
|
51
|
+
end
|
52
|
+
|
53
|
+
def stop!
|
54
|
+
Marvin::Settings.default_client.stop
|
55
|
+
self.stop_hooks.each { |h| h.call }
|
56
|
+
Marvin::DataStore.dump!
|
57
|
+
end
|
58
|
+
|
59
|
+
def self.run!
|
60
|
+
self.new.run!
|
61
|
+
end
|
62
|
+
|
63
|
+
def self.stop!
|
64
|
+
self.new.stop!
|
65
|
+
end
|
66
|
+
|
67
|
+
end
|
68
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'logger'
|
2
|
+
|
3
|
+
module Marvin
|
4
|
+
class Logger
|
5
|
+
|
6
|
+
cattr_accessor :logger
|
7
|
+
|
8
|
+
class << self
|
9
|
+
|
10
|
+
def setup
|
11
|
+
log_path = Marvin::Settings.root / "log/#{Marvin::Settings.environment}.log"
|
12
|
+
self.logger ||= ::Logger.new(Marvin::Settings.daemon? ? log_path : STDOUT)
|
13
|
+
end
|
14
|
+
|
15
|
+
def method_missing(name, *args, &blk)
|
16
|
+
self.setup # Ensure the logger is setup
|
17
|
+
self.logger.send(name, *args, &blk)
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|