Sutto-marvin 0.1.20081120 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +8 -0
- data/VERSION.yml +2 -2
- data/bin/marvin +88 -55
- data/config/settings.yml.sample +6 -1
- data/config/setup.rb +19 -4
- data/handlers/debug_handler.rb +2 -2
- data/handlers/hello_world.rb +1 -1
- data/lib/marvin.rb +14 -9
- data/lib/marvin/abstract_client.rb +10 -2
- data/lib/marvin/console.rb +54 -0
- data/lib/marvin/daemon.rb +71 -0
- data/lib/marvin/distributed.rb +14 -0
- data/lib/marvin/distributed/dispatch_handler.rb +82 -0
- data/lib/marvin/distributed/drb_client.rb +78 -0
- data/lib/marvin/distributed/ring_server.rb +41 -0
- data/lib/marvin/exception_tracker.rb +1 -1
- data/lib/marvin/irc.rb +4 -5
- data/lib/marvin/irc/client.rb +13 -2
- data/lib/marvin/irc/server.rb +57 -0
- data/lib/marvin/irc/server/abstract_connection.rb +79 -0
- data/lib/marvin/irc/server/base_connection.rb +66 -0
- data/lib/marvin/irc/server/channel.rb +111 -0
- data/lib/marvin/irc/server/named_store.rb +14 -0
- data/lib/marvin/irc/server/user.rb +5 -0
- data/lib/marvin/irc/server/user/handle_mixin.rb +138 -0
- data/lib/marvin/irc/server/user_connection.rb +127 -0
- data/lib/marvin/loader.rb +111 -36
- data/lib/marvin/logger.rb +2 -2
- data/lib/marvin/options.rb +11 -2
- data/lib/marvin/parsers/command.rb +43 -11
- data/lib/marvin/settings.rb +7 -6
- data/lib/marvin/status.rb +72 -0
- data/lib/marvin/test_client.rb +5 -1
- data/lib/marvin/util.rb +18 -1
- data/script/client +2 -18
- data/script/console +9 -0
- data/script/distributed_client +9 -0
- data/script/ring_server +12 -0
- data/script/server +12 -0
- data/script/status +11 -0
- data/test/parser_comparison.rb +27 -1
- data/test/parser_test.rb +254 -10
- data/test/test_helper.rb +1 -1
- metadata +62 -8
- data/lib/marvin/drb_handler.rb +0 -12
- data/lib/marvin/irc/abstract_server.rb +0 -4
- data/lib/marvin/irc/base_server.rb +0 -11
- data/script/daemon-runner +0 -12
@@ -0,0 +1,111 @@
|
|
1
|
+
module Marvin::IRC::Server
|
2
|
+
class Channel
|
3
|
+
include Marvin::Dispatchable
|
4
|
+
|
5
|
+
cattr_accessor :logger
|
6
|
+
self.logger = Marvin::Logger
|
7
|
+
|
8
|
+
attr_accessor :name, :members, :name, :topic, :operators, :mode
|
9
|
+
|
10
|
+
def inspect
|
11
|
+
"#<Marvin::IRC::Server::Channel name='#{name}' topic='#{@topic}' members=[#{self.members.map { |m| m.nick.inspect}.join(", ")}]>"
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(name)
|
15
|
+
@name = name
|
16
|
+
@members = []
|
17
|
+
@operators = []
|
18
|
+
@topic = ""
|
19
|
+
@mode = ""
|
20
|
+
logger.info "Created the channel #{name}"
|
21
|
+
dispatch :channel_created, :channel => self
|
22
|
+
end
|
23
|
+
|
24
|
+
def member?(user)
|
25
|
+
@members.include?(user)
|
26
|
+
end
|
27
|
+
|
28
|
+
def each_member(&blk)
|
29
|
+
members.each(&blk)
|
30
|
+
end
|
31
|
+
|
32
|
+
def add(user)
|
33
|
+
logger.info "Adding user #{user.nick} to #{@name}"
|
34
|
+
@operators << user if needs_op?
|
35
|
+
@members << user
|
36
|
+
end
|
37
|
+
|
38
|
+
def remove(user)
|
39
|
+
@members.delete(user)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Ind. operations on the room
|
43
|
+
|
44
|
+
def join(user)
|
45
|
+
return false if member?(user)
|
46
|
+
# Otherwise, we add a user
|
47
|
+
add user
|
48
|
+
@members.each { |m| m.notify :join, @name, :prefix => user.prefix }
|
49
|
+
dispatch :outgoing_join, :target => @name, :nick => user.nick
|
50
|
+
return true
|
51
|
+
end
|
52
|
+
|
53
|
+
def part(user, message = nil)
|
54
|
+
logger.debug "Getting part from #{user.inspect} w/ #{message}"
|
55
|
+
return false if !member?(user)
|
56
|
+
@members.each { |m| m.notify :part, @name, user.nick, message, :prefix => user.prefix }
|
57
|
+
remove user
|
58
|
+
# TODO: Remove channel from ChannelStore if it's empty.
|
59
|
+
dispatch :outgoing_part, :target => @name, :user => user, :message => message
|
60
|
+
check_emptyness!
|
61
|
+
return true
|
62
|
+
end
|
63
|
+
|
64
|
+
def quit(user, message = nil)
|
65
|
+
remove user
|
66
|
+
@members.each { |m| m.notify :quit, @name, message, :prefix => user.prefix }
|
67
|
+
# TODO: Remove channel from the store if it's empty
|
68
|
+
dispatch :outgoing_quit, :target => @name, :user => user, :message => message
|
69
|
+
check_emptyness!
|
70
|
+
end
|
71
|
+
|
72
|
+
def message(user, message)
|
73
|
+
@members.each { |m| m.notify :privmsg, @name, ":#{message}", :prefix => user.prefix unless user == m }
|
74
|
+
dispatch :outgoing_message, :target => self, :user => user, :message => message
|
75
|
+
end
|
76
|
+
|
77
|
+
def notice(user, message)
|
78
|
+
@members.each { |m| m.notify :notice, @name, ":#{message}", :prefix => user.prefix unless user == m }
|
79
|
+
dispatch :outgoing_notice, :target => self, :user => user, :message => message
|
80
|
+
end
|
81
|
+
|
82
|
+
def topic(user = nil, t = nil)
|
83
|
+
return @topic if t.blank?
|
84
|
+
logger.info "Getting topic for '#{@name}' - #{t.inspect}"
|
85
|
+
@topic = t
|
86
|
+
@members.each { |m| m.notify :topic, @name, ":#{t}", :prefix => user.prefix }
|
87
|
+
dispatch :outgoing_topic, :target => @name, :user => user, :topic => t
|
88
|
+
return @topic
|
89
|
+
end
|
90
|
+
|
91
|
+
def mode(user)
|
92
|
+
@operators.include?(user) ? "@" : ""
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def needs_op?
|
98
|
+
@operators.empty? && @members.empty?
|
99
|
+
end
|
100
|
+
|
101
|
+
def check_emptyness!
|
102
|
+
destroy if @members.empty?
|
103
|
+
end
|
104
|
+
|
105
|
+
def destroy
|
106
|
+
Marvin::IRC::Server::ChannelStore.delete(@name)
|
107
|
+
dispatch :channel_destroyed, :channel => self
|
108
|
+
end
|
109
|
+
|
110
|
+
end
|
111
|
+
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module Marvin::IRC::Server
|
2
|
+
class NamedStore
|
3
|
+
|
4
|
+
def self.new(key_plural, ref_value, &blk)
|
5
|
+
klass = Class.new(Hash) do
|
6
|
+
alias_method :"each_#{ref_value}", :each_value
|
7
|
+
alias_method key_plural.to_sym, :keys
|
8
|
+
end
|
9
|
+
klass.class_eval(&blk) unless blk.blank?
|
10
|
+
return klass.new
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
14
|
+
end
|
@@ -0,0 +1,138 @@
|
|
1
|
+
module Marvin::IRC::Server::User::HandleMixin
|
2
|
+
|
3
|
+
def handle_incoming_pass(opts = {})
|
4
|
+
@password = opts[:password]
|
5
|
+
end
|
6
|
+
|
7
|
+
def handle_incoming_user(opts = {})
|
8
|
+
@user = opts[:user]
|
9
|
+
@mode = opts[:mode]
|
10
|
+
@real_name = opts[:real_name]
|
11
|
+
welcome_if_complete!
|
12
|
+
end
|
13
|
+
|
14
|
+
def handle_incoming_nick(opts = {})
|
15
|
+
nick = opts[:new_nick]
|
16
|
+
if !nick.blank? && !Marvin::IRC::Server::UserStore.nick_taken?(nick.downcase)
|
17
|
+
if !(new_nick = @nick.nil?)
|
18
|
+
logger.debug "Notifying all users of nick change: #{@nick} => #{nick}"
|
19
|
+
# Get all users and let them now we've changed nick from @nick to nick
|
20
|
+
users = [self]
|
21
|
+
@channels.each do |c|
|
22
|
+
users += c.members.values
|
23
|
+
end
|
24
|
+
users.uniq.each { |u| u.notify :NICK, nick, :prefix => prefix }
|
25
|
+
dispatch :outgoing_nick, :nick => @nick, :new_nick => nick
|
26
|
+
end
|
27
|
+
# Update the store values
|
28
|
+
Marvin::IRC::Server::UserStore.delete(@nick.downcase) unless @nick.blank?
|
29
|
+
Marvin::IRC::Server::UserStore[nick.downcase] = self
|
30
|
+
# Change the nick and reset the number of attempts
|
31
|
+
@nick = nick
|
32
|
+
@nick_attempts = 0
|
33
|
+
# Finally, update the prefix.
|
34
|
+
update_prefix!
|
35
|
+
welcome_if_complete! if new_nick
|
36
|
+
elsif Marvin::IRC::Server::UserStore[nick.downcase] != self
|
37
|
+
# The nick is taken
|
38
|
+
# TODO: Remove hard coded nick attempts limit
|
39
|
+
if @nick_attempts > 5
|
40
|
+
# Handle abort here.
|
41
|
+
logger.debug "User has gone over nick attempt limits - Killing connection."
|
42
|
+
kill_connection!
|
43
|
+
dispatch :outgoing_nick_killed, :client => self
|
44
|
+
else
|
45
|
+
logger.debug "Noting users nick is taken, warning"
|
46
|
+
err :NICKNAMEINUSE, "*", nick, ":Nickname is already in use."
|
47
|
+
@nick_attempts += 1
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def handle_incoming_join(opts = {})
|
53
|
+
return if @prefix.blank?
|
54
|
+
opts[:target].split(",").each do |channel|
|
55
|
+
# If the channel name is invalud, let the user known and dispatch
|
56
|
+
# the correct event.
|
57
|
+
if channel !~ Marvin::IRC::Server::UserConnection::CHANNEL
|
58
|
+
logger.debug "Attempted to join invalid channel name '#{channel}'"
|
59
|
+
err :NOSUCHCHANNEL, channel, ":That channel doesn't exist"
|
60
|
+
dispatch :invalid_channel_name, :channel => channel, :client => self
|
61
|
+
return
|
62
|
+
end
|
63
|
+
chan = (Marvin::IRC::Server::ChannelStore[channel.downcase] ||= Marvin::IRC::Server::Channel.new(channel))
|
64
|
+
if chan.join(self)
|
65
|
+
rpl :TOPIC, channel, ":#{chan.topic.blank? ? "There is no topic" : nchan.topic}"
|
66
|
+
rpl :NAMREPLY, "=", channel, ":#{chan.members.map { |m| m.nick }.join(" ")}"
|
67
|
+
rpl :ENDOFNAMES, channel, ":End of /NAMES list."
|
68
|
+
@channels << chan
|
69
|
+
else
|
70
|
+
logger.debug "Couldn't join channel '#{channel}'"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def handle_incoming_ping(opts = {})
|
76
|
+
command :PONG, ":#{opts[:data]}"
|
77
|
+
end
|
78
|
+
|
79
|
+
def handle_incoming_pong(opts = {})
|
80
|
+
# Decrease the ping count.
|
81
|
+
@ping_count -= 1
|
82
|
+
@ping_count = 0 if @ping_count < 0
|
83
|
+
logger.debug "Got pong: #{opts[:data]}"
|
84
|
+
end
|
85
|
+
|
86
|
+
def handle_incoming_message(opts = {})
|
87
|
+
return if @prefix.blank?
|
88
|
+
unless (t = target_from(opts[:target])).blank?
|
89
|
+
t.message self, opts[:message]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
def handle_incoming_notice(opts = {})
|
94
|
+
return if @prefix.blank?
|
95
|
+
unless (t = target_from(opts[:target])).blank?
|
96
|
+
t.notice self, opts[:message]
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def handle_incoming_part(opts = {})
|
101
|
+
t = opts[:target].downcase
|
102
|
+
if !(chan = Marvin::IRC::Server::ChannelStore[t]).blank?
|
103
|
+
if chan.part(self, opts[:message])
|
104
|
+
@channels.delete(chan)
|
105
|
+
else
|
106
|
+
err :NOTONCHANNEL, opts[:target], ":Not a member of that channel"
|
107
|
+
end
|
108
|
+
else
|
109
|
+
err :NOSUCHNICK, opts[:target], ":No such nick/channel"
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def handle_incoming_quit(opts = {})
|
114
|
+
return unless @alive
|
115
|
+
@alive = false
|
116
|
+
@channels.each { |c| c.quit(self, opts[:message]) }
|
117
|
+
kill_connection!
|
118
|
+
end
|
119
|
+
|
120
|
+
def handle_incoming_topic(opts = {})
|
121
|
+
return if @prefix.blank? || opts[:target].blank?
|
122
|
+
c = Marvin::IRC::Server::ChannelStore[opts[:target].downcase]
|
123
|
+
return if c.blank?
|
124
|
+
if !@channels.include?(c)
|
125
|
+
err :NOTONCHANNEL, opts[:target], ":Not a member of that channel"
|
126
|
+
elsif opts[:message].nil?
|
127
|
+
t = c.topic
|
128
|
+
if t.blank?
|
129
|
+
rpl :NOTOPIC, c.name, ":No topic is set"
|
130
|
+
else
|
131
|
+
rpl :TOPIC, c.name, ":#{t}"
|
132
|
+
end
|
133
|
+
else
|
134
|
+
c.topic self, opts[:message].strip
|
135
|
+
end
|
136
|
+
end
|
137
|
+
|
138
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Marvin::IRC::Server
|
2
|
+
class UserConnection < AbstractConnection
|
3
|
+
USER_MODES = "aAbBcCdDeEfFGhHiIjkKlLmMnNopPQrRsStUvVwWxXyYzZ0123459*@"
|
4
|
+
CHANNEL_MODES = "bcdefFhiIklmnoPqstv"
|
5
|
+
CHANNEL = /^[\&\#]+/
|
6
|
+
include User::HandleMixin
|
7
|
+
|
8
|
+
attr_accessor :nick, :host, :user, :prefix, :password, :mode,
|
9
|
+
:real_name, :nick_attempts, :channels, :ping_count
|
10
|
+
|
11
|
+
def inspect
|
12
|
+
"#<Marvin::IRC::Server::UserConnection nick='#{@nick}' host='#{@host}' real_name='#{@real_name}' channels=[#{self.channels.map { |c| c.name.inspect}.join(", ")}]>"
|
13
|
+
end
|
14
|
+
|
15
|
+
def initialize(base, buffer = [])
|
16
|
+
super
|
17
|
+
@nick_attempts = 0
|
18
|
+
@channels = []
|
19
|
+
@ping_count = 0
|
20
|
+
end
|
21
|
+
|
22
|
+
# Notify is essentially command BUT it
|
23
|
+
# requires that the prefix is set.
|
24
|
+
def notify(command, *args)
|
25
|
+
opts = args.extract_options!
|
26
|
+
return if opts[:prefix].blank?
|
27
|
+
command command, *(args << opts)
|
28
|
+
end
|
29
|
+
|
30
|
+
# Notification messages
|
31
|
+
|
32
|
+
def message(user, message)
|
33
|
+
notify :PRIVMSG, @nick, ":#{message}", :prefix => user.prefix
|
34
|
+
dispatch :outgoing_message, :user => user, :message => message, :target => self
|
35
|
+
end
|
36
|
+
|
37
|
+
def notice(user, message)
|
38
|
+
notify :NOTICE, @nick, ":#{message}", :prefix => user.prefix
|
39
|
+
dispatch :outgoing_notice, :user => user, :message => message, :target => self
|
40
|
+
end
|
41
|
+
|
42
|
+
# Get the user / channel targeted by a particular request.
|
43
|
+
|
44
|
+
def target_from(target)
|
45
|
+
case target
|
46
|
+
when CHANNEL
|
47
|
+
chan = Marvin::IRC::Server::ChannelStore[target.downcase]
|
48
|
+
if chan.nil?
|
49
|
+
rpl :NOSUCHNICK, target, ":No such nick/channel"
|
50
|
+
elsif !chan.member?(self)
|
51
|
+
err :CANNOTSENDTOCHAN, target, ":Cannot send to channel"
|
52
|
+
else
|
53
|
+
return chan
|
54
|
+
end
|
55
|
+
else
|
56
|
+
user = Marvin::IRC::Server::UserStore[target.downcase]
|
57
|
+
if user.nil?
|
58
|
+
err :NOSUCHNICK, target, ":No suck nick/channel"
|
59
|
+
else
|
60
|
+
return user
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Implementations for connect / disconnect
|
66
|
+
|
67
|
+
def process_connect
|
68
|
+
super
|
69
|
+
end
|
70
|
+
|
71
|
+
def process_disconnect
|
72
|
+
super
|
73
|
+
end
|
74
|
+
|
75
|
+
def kill_connection!
|
76
|
+
@connection.kill_connection!
|
77
|
+
end
|
78
|
+
|
79
|
+
protected
|
80
|
+
|
81
|
+
def welcome_if_complete!
|
82
|
+
update_prefix!
|
83
|
+
# Next, send the MOTD and other misc. stuff
|
84
|
+
return if @welcomed || @prefix.blank?
|
85
|
+
rpl :WELCOME, @nick, ":Welcome to Marvin Server - #{@prefix}"
|
86
|
+
rpl :YOURHOST, @nick, ":Your host is #{server_host}, running version #{Marvin.version}"
|
87
|
+
rpl :CREATED, @nick, ":This server was created #{@connection.started_at}"
|
88
|
+
rpl :MYINFO, @nick, ":#{server_host} #{Marvin.version} #{USER_MODES} #{CHANNEL_MODES}"
|
89
|
+
rpl :MOTDSTART, @nick, ":- MOTD"
|
90
|
+
rpl :MOTD, @nick, ":- Welcome to Marvin Server, a Ruby + EventMachine ircd."
|
91
|
+
rpl :ENDOFMOTD, @nick, ":- End of /MOTD command."
|
92
|
+
@welcomed = true
|
93
|
+
end
|
94
|
+
|
95
|
+
def update_prefix!
|
96
|
+
@prefix = "#{@nick}!~#{@user}@#{peer_name}" if details_complete?
|
97
|
+
end
|
98
|
+
|
99
|
+
def details_complete?
|
100
|
+
!@nick.nil? && !@user.nil?
|
101
|
+
end
|
102
|
+
|
103
|
+
def server_host
|
104
|
+
@connection.host
|
105
|
+
end
|
106
|
+
|
107
|
+
def server_port
|
108
|
+
@connection.port
|
109
|
+
end
|
110
|
+
|
111
|
+
def rpl(number, *args)
|
112
|
+
numeric Marvin::IRC::Replies["RPL_#{number.to_s.upcase}"], *args
|
113
|
+
end
|
114
|
+
|
115
|
+
def err(number, *args)
|
116
|
+
numeric Marvin::IRC::Replies["ERR_#{number.to_s.upcase}"], *args
|
117
|
+
end
|
118
|
+
|
119
|
+
def numeric(number, *args)
|
120
|
+
args << {} unless args[-1].is_a?(Hash)
|
121
|
+
args[-1][:prefix] ||= server_host
|
122
|
+
args.unshift(@nick) unless args.first == @nick
|
123
|
+
command(number.to_s, *args)
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
end
|
data/lib/marvin/loader.rb
CHANGED
@@ -1,69 +1,144 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'singleton'
|
3
|
+
|
1
4
|
module Marvin
|
2
5
|
class Loader
|
6
|
+
include Singleton
|
3
7
|
|
4
|
-
|
8
|
+
# A Controller is any class e.g. a client / server
|
9
|
+
# which is provides the main functionality of the
|
10
|
+
# current client.
|
11
|
+
CONTROLLERS = {
|
12
|
+
:client => Marvin::Settings.default_client,
|
13
|
+
:server => Marvin::IRC::Server,
|
14
|
+
:ring_server => Marvin::Distributed::RingServer,
|
15
|
+
:distributed_client => Marvin::Distributed::DRbClient,
|
16
|
+
:console => nil
|
17
|
+
}
|
5
18
|
|
6
|
-
|
7
|
-
|
19
|
+
# For each of the known types, define a method
|
20
|
+
# as Marvin::Loader.type? so we can easily do
|
21
|
+
# things like conditional registers.
|
22
|
+
class << self
|
23
|
+
CONTROLLERS.keys.each do |type|
|
24
|
+
define_method(:"#{type}?") { Marvin::Loader.type == type }
|
25
|
+
end
|
26
|
+
end
|
8
27
|
|
28
|
+
cattr_accessor :hooks, :boot, :type
|
29
|
+
self.hooks = {}
|
30
|
+
self.type = :client
|
31
|
+
|
32
|
+
# Old style of registering a block to be run on startup
|
33
|
+
# for doing setup etc. now replaced by before_run
|
9
34
|
def self.before_connecting(&blk)
|
10
|
-
|
35
|
+
Marvin::Logger.warn "Marvin::Loader.before_connecting is deprecated, please use before_run instead."
|
36
|
+
before_run(&blk)
|
11
37
|
end
|
12
38
|
|
13
|
-
|
14
|
-
|
39
|
+
# Append a hook for a given type of hook in order
|
40
|
+
# to be called later on via invoke_hooks!
|
41
|
+
def self.append_hook(type, &blk)
|
42
|
+
self.hooks_for(type) << blk unless blk.blank?
|
15
43
|
end
|
16
44
|
|
17
|
-
|
18
|
-
|
45
|
+
# Return all of the existing hooks or an empty
|
46
|
+
# for a given hook type.
|
47
|
+
def self.hooks_for(type)
|
48
|
+
(self.hooks[type.to_sym] ||= [])
|
19
49
|
end
|
20
50
|
|
21
|
-
|
22
|
-
|
51
|
+
# Invoke (call) all of the hooks for a given
|
52
|
+
# type.
|
53
|
+
def self.invoke_hooks!(type)
|
54
|
+
hooks_for(type).each { |hook| hook.call }
|
23
55
|
end
|
24
56
|
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
57
|
+
# Append a call back to be run at a specific stage.
|
58
|
+
|
59
|
+
# Register a hook to be run before the controller
|
60
|
+
# has started running.
|
61
|
+
def self.before_run(&blk)
|
62
|
+
append_hook(:before_run, &blk)
|
30
63
|
end
|
31
64
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
65
|
+
# Register a hook to be run after the controller
|
66
|
+
# has stopped. Note that this will not guarantee
|
67
|
+
# all processing has completed.
|
68
|
+
def self.after_stop(&blk)
|
69
|
+
append_hook(:after_stop, &blk)
|
36
70
|
end
|
37
71
|
|
38
|
-
def
|
39
|
-
|
40
|
-
|
41
|
-
|
72
|
+
def self.run!(type = :client)
|
73
|
+
self.type = type.to_sym
|
74
|
+
self.instance.run!
|
75
|
+
end
|
76
|
+
|
77
|
+
def self.stop!(force = false)
|
78
|
+
self.instance.stop!(force)
|
42
79
|
end
|
43
80
|
|
44
81
|
def run!
|
45
|
-
|
46
|
-
self.
|
82
|
+
self.register_signals
|
83
|
+
Marvin::Options.parse! unless self.type == :console
|
84
|
+
Marvin::Daemon.daemonize! if Marvin::Settings.daemon?
|
85
|
+
Marvin::Logger.setup
|
47
86
|
self.load_settings
|
87
|
+
require(Marvin::Settings.root / "config/setup")
|
48
88
|
self.load_handlers
|
49
|
-
self.
|
50
|
-
|
51
|
-
Marvin::Settings.default_client.run
|
89
|
+
self.class.invoke_hooks! :before_run
|
90
|
+
attempt_controller_action! :run
|
52
91
|
end
|
53
92
|
|
54
|
-
def stop!
|
55
|
-
|
56
|
-
|
57
|
-
|
93
|
+
def stop!(force = false)
|
94
|
+
if force || !@attempted_stop
|
95
|
+
self.class.invoke_hooks! :before_stop
|
96
|
+
attempt_controller_action! :stop
|
97
|
+
self.class.invoke_hooks! :after_stop
|
98
|
+
@attempted_stop = true
|
99
|
+
end
|
100
|
+
Marvin::Daemon.cleanup! if Marvin::Settings.daemon?
|
58
101
|
end
|
59
102
|
|
60
|
-
|
61
|
-
|
103
|
+
protected
|
104
|
+
|
105
|
+
# Get the controller for the current type if
|
106
|
+
# it exists and attempt to class a given action.
|
107
|
+
def attempt_controller_action!(action)
|
108
|
+
klass = CONTROLLERS[self.type]
|
109
|
+
klass.send(action) unless klass.blank? || !klass.respond_to?(action, true)
|
62
110
|
end
|
63
111
|
|
64
|
-
|
65
|
-
|
112
|
+
# Load all of the handler's in the handlers subfolder
|
113
|
+
# of a marvin installation.
|
114
|
+
def load_handlers
|
115
|
+
Dir[Marvin::Settings.root / "handlers/**/*.rb"].each { |handler| require handler }
|
66
116
|
end
|
67
117
|
|
118
|
+
def load_settings
|
119
|
+
Marvin::Settings.setup
|
120
|
+
case Marvin::Loader.type
|
121
|
+
# Perform client / type specific setup.
|
122
|
+
when :client
|
123
|
+
Marvin::Settings.default_client.configuration = Marvin::Settings.to_hash
|
124
|
+
Marvin::Settings.default_client.setup
|
125
|
+
when :distributed_client
|
126
|
+
Marvin::Settings.default_client = Marvin::Distributed::DRbClient
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
def register_signals
|
131
|
+
["INT", "TERM"].each do |sig|
|
132
|
+
trap sig do
|
133
|
+
Marvin::Loader.stop!
|
134
|
+
exit
|
135
|
+
end
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
139
|
+
# Register to the Marvin::DataStore methods
|
140
|
+
before_run { Marvin::DataStore.load! if Marvin::Loader.type == :client }
|
141
|
+
after_stop { Marvin::DataStore.dump! if Marvin::Loader.type == :client }
|
142
|
+
|
68
143
|
end
|
69
144
|
end
|