Sutto-marvin 0.1.0.20081016 → 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 ADDED
@@ -0,0 +1,155 @@
1
+ h1. Marvin
2
+
3
+ Marvin is a simple IRC Framework for Rails suitable for building things
4
+ such as simple IRC bots. Extracted from real use - we'd originally used
5
+ a heavily modified version of MatzBot - it's been built to service a
6
+ particular need.
7
+
8
+ h2. Background
9
+
10
+ Marvin is an event driven framework in two ways - for one, it uses
11
+ EventMachine for all networking purposes - as a result, it's both
12
+ relatively stable / reliable and also powerful.
13
+
14
+ Following on from this, the irc library is event driven. At the base
15
+ level, you choose a client (By Default, Marvin::IRC::Client.) and then you register
16
+ any number of handlers. Whenever an event happens e.g. an incoming message,
17
+ a connection unbinding or event just post_init, each handler is notified
18
+ and given a small set of details about the event.
19
+
20
+ Handlers are very simple - in fact, you could get away with registering
21
+ Object.new as a handler.
22
+
23
+ To function, handlers only require one method: handle - which takes
24
+ two options. an event name (e.g. :incoming_message) and a hash
25
+ of the aforementioned attributes / details. This data can then be processed.
26
+ Alternatively, if a handler has a "handle_[event_name]" method (e.g.
27
+ handle_incoming_message), it will instead be called. Also, if client=
28
+ is implemented this will be called when the client is setup containing
29
+ a reference to said client. This is used to that the handler can
30
+ respond to actions.
31
+
32
+ Like Rack for HTTP, Marvin provides a fair amount of example
33
+ handlers for simple stuff inside IRC.
34
+
35
+ h2. Getting Started
36
+
37
+ The easiest way to get started with Marvin is by installing the Marvin gem. To
38
+ do this, make sure Github is added to your gem sources (and you are using
39
+ rubygems >= 1.2.0):
40
+
41
+ $ gem sources -a http://gems.github.com
42
+ $ sudo gem install username-marvin
43
+
44
+ Once you have installed the gem, you should have access to the "marvin" command:
45
+
46
+ $ marvin --help
47
+
48
+ You can create a new marvin folder:
49
+
50
+ $ marvin create my_marvin_project
51
+
52
+ Then simply edit your settings in the +config/settings.yml+
53
+
54
+ default:
55
+ name: "My Marvin Bot"
56
+ server: irc.freenode.net
57
+ port: 6667
58
+ channel: "#marvin-testing"
59
+ use_logging: false
60
+ datastore_location: tmp/datastore.json
61
+ development:
62
+ user: MarvinBot
63
+ name: MarvinBot
64
+ nick: MarvinBot3000
65
+ production:
66
+ deployed: false
67
+
68
+ You can use the defaults or configure it. By default your marvin bot will not
69
+ do any logging. You will need to set that up. From this point you can begin
70
+ running the bot as a daemon or as a console application:
71
+
72
+ $ ./script/run
73
+
74
+ The bot should join the specified channel and will resond to some simple
75
+ commands by default:
76
+
77
+ |<YourName> MarvinBot3000: hello
78
+ |<MarvinBot3000> YourName: Hola!
79
+
80
+ h2. Marvin::Base - A handler starting point
81
+
82
+ The first, Marvin::Base provides a base set of methods (e.g. say,
83
+ reply etc etc.) which make writing a client easier. You can simply
84
+ inherit from Marvin::Base, write some logic and then use the class
85
+ method on_event to define responses to events. The passed in meta
86
+ data for each event is then usable via options.attribute_name - an
87
+ openstruct version of the details. e.g.
88
+
89
+ class NinjaStuff < Marvin::Base
90
+ on_event :incoming_message do
91
+ do_something
92
+ end
93
+ def do_something
94
+ reply options.message # Will echo back the message
95
+ end
96
+ end
97
+
98
+ Or the like. Also, the halt! method can be called in any subclass to
99
+ halt the handler callback chain.
100
+
101
+ h2. Marvin::CommandHandler - Ridiculously easy Bots
102
+
103
+ With Marvin::CommandHandler, you get to define seriously
104
+ simple classes which can act as a simple bot. It takes
105
+ great inspiration from "MatzBot":http://github.com/defunkt/matzbot/tree/master
106
+ which was actually one of the main inspirations for
107
+ creating marvin.
108
+
109
+ To write a CommandHandler, you simply create a subclass
110
+ (ala ActiveRecord::Base), define a few methods and then
111
+ just use the "exposes" class method. e.g.
112
+
113
+ class MySecondExample < Marvin::CommandHandler
114
+ exposes :hello
115
+ def hello(data)
116
+ reply "Hello!"
117
+ end
118
+ end
119
+
120
+ Where data is an array of parameters. exposed methods will be called
121
+ when they match the following pattern:
122
+
123
+ Botname: <exposed-method> <space-seperated-list-meaning-data>
124
+
125
+ h2. Marvin::MiddleMan - Introducing middleware
126
+
127
+ Marvin::MiddleMan lets you insert middleware between handlers
128
+ and you're client - letting you do things such as translating
129
+ all messages on the fly. It's build to be extensible and is
130
+ relatively simple to use. On any Marvin::Base subclass (baring
131
+ the MiddleMan itself), you can simple use the normal methods
132
+ of registering a handler with one exception - you now pass
133
+ one argument, the class reference to your middleman class.
134
+
135
+ h2. Marvin::DataStore - A dead simple persistent hash store
136
+
137
+ Want to save data between when you stop and start your IRC
138
+ Client? With Marvin, it's really, really simple - Marvin::DataStore
139
+ offers a simple syntax for persistent data stores.
140
+
141
+ New datastores can be created with Marvin::DataStore.new("global-store-key").
142
+ From there, you have a hash to do whatever the hell you want. Just
143
+ make sure the data you store is JSON-serializable.
144
+
145
+ When you start - stop a server (via Marvin::Loader.run! and Marvin::Loader.stop!)
146
+ you're data will be loaded from and written to disk accordingly.
147
+
148
+ If you're inside a Marvin::Base subclass it's even easier. You can get a cattr_access
149
+ style accessor for free - just use the "uses_datastore" method. e.g:
150
+
151
+ class X < Marvin::Base
152
+ uses_datastore "datastore-global-key", :cattr_name
153
+ end
154
+
155
+ Then, self.cattr_name will point to the data store instance.
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ patch: 20081115
3
+ major: 0
4
+ minor: 1
@@ -2,13 +2,8 @@ class HelloWorld < Marvin::CommandHandler
2
2
 
3
3
  exposes :hello
4
4
 
5
- uses_datastore "hello-count", :counts
6
-
7
5
  def hello(data)
8
- self.counts ||= {}
9
- self.counts[options.nick] ||= 0
10
- self.counts[options.nick] += 1
11
- reply "Oh hai there - This is hello ##{self.counts[options.nick]} from you!"
6
+ reply "Hola!" unless target == "#all"
12
7
  end
13
8
 
14
9
  end
@@ -16,4 +16,6 @@ class TweetTweet < Marvin::Base
16
16
  def show_tweet(tweet)
17
17
  end
18
18
 
19
+
20
+
19
21
  end
@@ -5,11 +5,12 @@ require "marvin/irc/event"
5
5
  module Marvin
6
6
  class AbstractClient
7
7
 
8
- cattr_accessor :events, :handlers, :configuration, :logger, :is_setup, :connections
8
+ include Marvin::Dispatchable
9
+
10
+ cattr_accessor :events, :configuration, :logger, :is_setup, :connections
9
11
  attr_accessor :channels, :nickname
10
12
 
11
13
  # Set the default values for the variables
12
- self.handlers = []
13
14
  self.events = []
14
15
  self.configuration = OpenStruct.new
15
16
  self.configuration.channels = []
@@ -27,12 +28,13 @@ module Marvin
27
28
  logger.debug "Setting the client for each handler"
28
29
  self.handlers.each { |h| h.client = self if h.respond_to?(:client=) }
29
30
  logger.debug "Dispatching the default :client_connected event"
30
- dispatch_event :client_connected
31
+ dispatch :client_connected
31
32
  end
32
33
 
33
34
  def process_disconnect
34
35
  self.connections.delete(self) if self.connections.include?(self)
35
- dispatch_event :client_disconnected
36
+ dispatch :client_disconnected
37
+ Marvin::Loader.stop! if self.connections.blank?
36
38
  end
37
39
 
38
40
  # Sets the current class-wide settings of this IRC Client
@@ -64,53 +66,10 @@ module Marvin
64
66
 
65
67
  ## Handling all of the the actual client stuff.
66
68
 
67
- # Appends a handler to the end of the handler callback
68
- # chain. Note that they will be called in the order they
69
- # are appended.
70
- def self.register_handler(handler)
71
- return if handler.blank?
72
- self.handlers << handler
73
- end
74
-
75
69
  def receive_line(line)
76
- dispatch_event :incoming_line, :line => line
70
+ dispatch :incoming_line, :line => line
77
71
  event = Marvin::Settings.default_parser.parse(line)
78
- dispatch_event(event.to_incoming_event_name, event.to_hash) unless event.nil?
79
- end
80
-
81
- # Handles the dispatch of an event and it's associated options
82
- # / properties (defaulting to an empty hash) to both the client
83
- # (used for things such as responding to PING) and each of the
84
- # registered handlers.
85
- def dispatch_event(name, opts = {})
86
- # The full handler name is simply what is used to handle
87
- # a single event (e.g. handle_incoming_message)
88
- full_handler_name = "handle_#{name}"
89
-
90
- # If the current handle_name method is defined on this
91
- # class, we dispatch to that first. We use this to provide
92
- # functionality such as responding to PING's and handling
93
- # required stuff on connections.
94
- self.send(full_handler_name, opts) if respond_to?(full_handler_name)
95
-
96
- begin
97
- # For each of the handlers, check first if they respond to
98
- # the full handler name (e.g. handle_incoming_message) - calling
99
- # that if it exists - otherwise falling back to the handle method.
100
- # if that doesn't exist, nothing is done.
101
- self.handlers.each do |handler|
102
- if handler.respond_to?(full_handler_name)
103
- handler.send(full_handler_name, opts)
104
- elsif handler.respond_to?(:handle)
105
- handler.handle name, opts
106
- end
107
- end
108
- # Raise an exception in order to stop the flow
109
- # of the control. Ths enables handlers to prevent
110
- # responses from happening multiple times.
111
- rescue HaltHandlerProcessing
112
- logger.debug "Handler Progress halted; Continuing on."
113
- end
72
+ dispatch(event.to_incoming_event_name, event.to_hash) unless event.nil?
114
73
  end
115
74
 
116
75
  # Default handlers
@@ -123,16 +82,18 @@ module Marvin
123
82
  def handle_client_connected(opts = {})
124
83
  logger.debug "About to handle post init"
125
84
  # IRC Connection is establish so we send all the required commands to the server.
126
- logger.debug "sending user command"
127
- command :user, self.configuration.user, "0", "*", Marvin::Util.last_param(self.configuration.name)
128
- default_nickname = self.configuration.nick || self.configuration.nicknames.shift
129
85
  logger.debug "Setting default nickname"
86
+ default_nickname = self.configuration.nick || self.configuration.nicknames.shift
130
87
  nick default_nickname
88
+ logger.debug "sending user command"
89
+ command :user, self.configuration.user, "0", "*", Marvin::Util.last_param(self.configuration.name)
131
90
  # If a password is specified, we will attempt to message
132
91
  # NickServ to identify ourselves.
133
92
  say ":IDENTIFY #{self.configuration.password}", "NickServ" unless self.configuration.password.blank?
134
93
  # Join the default channels
135
94
  self.configuration.channels.each { |c| self.join c }
95
+ rescue Exception => e
96
+ Marvin::ExceptionTracker.log(e)
136
97
  end
137
98
 
138
99
  # The default handler for when a users nickname is taken on
@@ -167,7 +128,7 @@ module Marvin
167
128
  def handle_incoming_numeric(opts = {})
168
129
  code = opts[:code].to_i
169
130
  args = Marvin::Util.arguments(opts[:data])
170
- dispatch_event :incoming_numeric_processed, {:code => code, :data => args}
131
+ dispatch :incoming_numeric_processed, {:code => code, :data => args}
171
132
  end
172
133
 
173
134
  ## General IRC Functions
@@ -190,14 +151,14 @@ module Marvin
190
151
  self.channels << channel
191
152
  command :JOIN, channel
192
153
  logger.info "Joined channel #{channel}"
193
- dispatch_event :outgoing_join, :target => channel
154
+ dispatch :outgoing_join, :target => channel
194
155
  end
195
156
 
196
157
  def part(channel, reason = nil)
197
158
  channel = Marvin::Util.channel_name(channel)
198
159
  if self.channels.include?(channel)
199
160
  command :part, channel, Marvin::Util.last_param(reason)
200
- dispatch_event :outgoing_part, :target => channel, :reason => reason
161
+ dispatch :outgoing_part, :target => channel, :reason => reason
201
162
  logger.info "Parted from room #{channel}#{reason ? " - #{reason}" : ""}"
202
163
  else
203
164
  logger.warn "Tried to disconnect from #{channel} - which you aren't a part of"
@@ -212,7 +173,7 @@ module Marvin
212
173
  end
213
174
  logger.debug "Parted from all channels, quitting"
214
175
  command :quit
215
- dispatch_event :quit
176
+ dispatch :quit
216
177
  # Remove the connections from the pool
217
178
  self.connections.delete(self)
218
179
  logger.info "Quit from server"
@@ -221,19 +182,19 @@ module Marvin
221
182
  def msg(target, message)
222
183
  command :privmsg, target, Marvin::Util.last_param(message)
223
184
  logger.info "Message sent to #{target} - #{message}"
224
- dispatch_event :outgoing_message, :target => target, :message => message
185
+ dispatch :outgoing_message, :target => target, :message => message
225
186
  end
226
187
 
227
188
  def action(target, message)
228
189
  action_text = Marvin::Util.last_param "\01ACTION #{message.strip}\01"
229
190
  command :privmsg, target, action_text
230
- dispatch_event :outgoing_action, :target => target, :message => message
191
+ dispatch :outgoing_action, :target => target, :message => message
231
192
  logger.info "Action sent to #{target} - #{message}"
232
193
  end
233
194
 
234
195
  def pong(data)
235
196
  command :pong, data
236
- dispatch_event :outgoing_pong
197
+ dispatch :outgoing_pong
237
198
  logger.info "PONG sent to #{data}"
238
199
  end
239
200
 
@@ -241,7 +202,7 @@ module Marvin
241
202
  logger.info "Changing nickname to #{new_nick}"
242
203
  command :nick, new_nick
243
204
  self.nickname = new_nick
244
- dispatch_event :outgoing_nick, :new_nick => new_nick
205
+ dispatch :outgoing_nick, :new_nick => new_nick
245
206
  logger.info "Nickname changed to #{new_nick}"
246
207
  end
247
208
 
data/lib/marvin/base.rb CHANGED
@@ -97,11 +97,11 @@ module Marvin
97
97
 
98
98
  # Determines whether the previous message was inside a channel.
99
99
  def from_channel?
100
- self.target && self.target[0..0] == "#"
100
+ self.target && self.target[0] == ?#
101
101
  end
102
102
 
103
103
  def addressed?
104
- self.from_user? || options.message.split(" ").first == "#{self.client.nickname}:"
104
+ self.from_user? || options.message.split(" ").first.downcase == "#{self.client.nickname.downcase}:"
105
105
  end
106
106
 
107
107
  def setup_defaults(options)
@@ -2,4 +2,10 @@ class File
2
2
  def self.present_dir
3
3
  File.dirname(__FILE__)
4
4
  end
5
+ end
6
+
7
+ class String
8
+ def /(*args)
9
+ File.join(self, *args)
10
+ end
5
11
  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,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,11 @@
1
+ require 'webrick'
2
+
3
+ module Marvin::IRC
4
+ class BaseServer < WEBrick::GenericServer
5
+
6
+ def run(sock)
7
+ File.open("x", "w+") { |f| f.puts sock.inspect }
8
+ end
9
+
10
+ end
11
+ end
@@ -57,12 +57,10 @@ module Marvin::IRC
57
57
  end
58
58
 
59
59
  def post_init
60
- super
61
60
  client.process_connect
62
61
  end
63
62
 
64
63
  def unbind
65
- super
66
64
  client.process_disconnect
67
65
  end
68
66
 
@@ -98,7 +96,7 @@ module Marvin::IRC
98
96
 
99
97
  # Registers a callback handle that will be periodically run.
100
98
  def periodically(timing, event_callback)
101
- callback = proc { self.dispatch_event event_callback.to_sym }
99
+ callback = proc { self.dispatch event_callback.to_sym }
102
100
  EventMachine::add_periodic_timer(timing, &callback)
103
101
  end
104
102
 
@@ -56,7 +56,13 @@ module Marvin::IRC
56
56
 
57
57
  # Registers a callback handle that will be periodically run.
58
58
  def periodically(timing, event_callback)
59
- callback = proc { self.dispatch_event event_callback.to_sym }
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
60
66
  end
61
67
 
62
68
  end
data/lib/marvin/irc.rb CHANGED
@@ -1,8 +1,9 @@
1
1
  module Marvin
2
2
  module IRC
3
- autoload :Client, 'marvin/irc/client'
4
- autoload :Event, 'marvin/irc/event'
5
- autoload :SocketClient, 'marvin/irc/socket_client'
3
+ autoload :Client, 'marvin/irc/client'
4
+ autoload :Event, 'marvin/irc/event'
5
+ autoload :SocketClient, 'marvin/irc/socket_client'
6
6
  autoload :AbstractServer, 'marvin/irc/abstract_server'
7
+ autoload :BaseServer, 'marvin/irc/base_server'
7
8
  end
8
9
  end
@@ -85,9 +85,7 @@ module Marvin
85
85
  private
86
86
 
87
87
  def setup_subhandler_clients
88
- self.subhandlers.each do |sh|
89
- sh.client = self.client if sh.respond_to?(:client=)
90
- end
88
+ self.subhandlers.each { |sh| sh.client = self.client if sh.respond_to?(:client=) }
91
89
  end
92
90
 
93
91
  # This should probably be extracted into some sort of Util's library as
@@ -86,6 +86,8 @@ module Marvin
86
86
 
87
87
  register_event :ping, /^\:(.+)\!\~?(.+)\@(.+) PING (.*)$/,
88
88
  :nick, :ident, :host, :data
89
+
90
+ register_event :ping, /PING (.*)$/, :data
89
91
 
90
92
  register_event :numeric, /^\:(\S+) ([0-9]+) (.*)$/,
91
93
  :server, :code, :data
@@ -39,9 +39,9 @@ module Marvin
39
39
  self.receive_line irc_command
40
40
  end
41
41
 
42
- def dispatch_event(name, opts = {})
42
+ def dispatch(name, opts = {})
43
43
  self.dispatched_events << [name, opts]
44
- super(name, opts = {})
44
+ super(name, opts)
45
45
  end
46
46
 
47
47
  def self.run
data/lib/marvin/util.rb CHANGED
@@ -1,6 +1,5 @@
1
1
  module Marvin
2
- class Util
3
- class << self
2
+ module Util
4
3
 
5
4
  # Return the channel-name version of a string by
6
5
  # appending "#" to the front if it doesn't already
@@ -25,6 +24,7 @@ module Marvin
25
24
  end
26
25
  alias lp last_param
27
26
 
28
- end
27
+ extend self
28
+
29
29
  end
30
30
  end
data/lib/marvin.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  $:.unshift File.dirname(__FILE__) # Append the current working dir to the front of the line.
2
2
 
3
3
  require 'rubygems'
4
- require 'extlib'
4
+ require 'active_support'
5
5
  require 'marvin/core_ext'
6
6
 
7
7
  # Make all exceptions available
@@ -9,6 +9,7 @@ require 'marvin/exceptions'
9
9
 
10
10
  module Marvin
11
11
  autoload :Util, 'marvin/util'
12
+ autoload :Dispatchable, 'marvin/dispatchable'
12
13
  autoload :AbstractClient, 'marvin/abstract_client'
13
14
  autoload :Base, 'marvin/base'
14
15
  autoload :ClientMixin, 'marvin/client_mixin'
data/script/run CHANGED
@@ -11,9 +11,15 @@ IS_DAEMON = ARGV.include?("--is-daemon")
11
11
  # And Require Marvin.
12
12
  require 'marvin'
13
13
 
14
- trap "SIGINT" do
15
- Marvin::Loader.stop!
16
- exit
14
+ ["INT", "TERM"].each do |sig|
15
+
16
+ # Trap a given signal and run all
17
+ # of our callbacks etc,
18
+ trap sig do
19
+ Marvin::Loader.stop!
20
+ exit
21
+ end
22
+
17
23
  end
18
24
 
19
25
  Marvin::Loader.run!
@@ -0,0 +1,38 @@
1
+ require File.dirname(__FILE__) + "/../spec_helper"
2
+
3
+ # Tests the behaviour of the AbstractClient functionality
4
+ # via a thin wrapper of the class via Marvin::TestClient.
5
+
6
+ describe "the base Marvin::TestClient functionality" do
7
+
8
+ it "should dispatch :client_connected as the first event on process_connect" do
9
+ client(true).dispatched_events.should == []
10
+ client.process_connect
11
+ client.dispatched_events.first.should == [:client_connected, {}]
12
+ end
13
+
14
+ it "should dispatch :client_connected as the first event on process_connect" do
15
+ client(true).dispatched_events.should == []
16
+ client.process_connect
17
+ Marvin::Logger.info client.outgoing_commands.inspect
18
+ client.dispatched_events[-2].first.should == :outgoing_nick
19
+ client.dispatched_events[-1].first.should == :outgoing_join
20
+ client.outgoing_commands.length.should == 3
21
+ client.outgoing_commands[0].should =~ /^USER \w+ 0 \* :\w+ \r\n$/
22
+ client.outgoing_commands[1].should =~ /^NICK \w+ \r\n$/
23
+ client.outgoing_commands[2].should =~ /^JOIN \#[A-Za-z0-9\-\_]+ \r\n$/
24
+ end
25
+
26
+ it "should dispatch :client_disconnect on process_disconnect" do
27
+ client(true).dispatched_events.should == []
28
+ client.process_disconnect
29
+ client.dispatched_events.last.should == [:client_disconnected, {}]
30
+ end
31
+
32
+ it "should add an :incoming_line event for each incoming line" do
33
+ client(true).dispatched_events.should == []
34
+ client.receive_line "SOME RANDOM LINE THAT HAS ZERO ACTUAL USE"
35
+ client.dispatched_events.first.should == [:incoming_line, {:line => "SOME RANDOM LINE THAT HAS ZERO ACTUAL USE"}]
36
+ end
37
+
38
+ end
@@ -0,0 +1,14 @@
1
+ require "rubygems"
2
+ require "bacon"
3
+
4
+ require File.dirname(__FILE__) + "/../lib/marvin"
5
+ # Now, Set everything up.
6
+ Marvin::Logger.logger = Logger.new(File.dirname(__FILE__) + "/../log/test.log")
7
+ Marvin::Settings.default_client = Marvin::TestClient
8
+ Marvin::Loader.run!
9
+
10
+ def client(force_new = false)
11
+ $test_client = Marvin::TestClient.new if force_new || $test_client.nil?
12
+ return $test_client
13
+ end
14
+
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: Sutto-marvin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.20081016
4
+ version: 0.1.20081115
5
5
  platform: ruby
6
6
  authors:
7
7
  - Darcy Laycock
@@ -9,8 +9,8 @@ autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
11
 
12
- date: 2008-08-05 00:00:00 -07:00
13
- default_executable: marvin
12
+ date: 2008-11-15 00:00:00 -08:00
13
+ default_executable:
14
14
  dependencies: []
15
15
 
16
16
  description: Marvin is a Ruby IRC library / framework for ultimate awesomeness and with an evented design.
@@ -22,6 +22,9 @@ extensions: []
22
22
  extra_rdoc_files: []
23
23
 
24
24
  files:
25
+ - README.textile
26
+ - VERSION.yml
27
+ - bin/marvin
25
28
  - lib/marvin
26
29
  - lib/marvin/abstract_client.rb
27
30
  - lib/marvin/abstract_parser.rb
@@ -29,11 +32,14 @@ files:
29
32
  - lib/marvin/command_handler.rb
30
33
  - lib/marvin/core_ext.rb
31
34
  - lib/marvin/data_store.rb
35
+ - lib/marvin/dispatchable.rb
32
36
  - lib/marvin/drb_handler.rb
33
37
  - lib/marvin/exception_tracker.rb
34
38
  - lib/marvin/exceptions.rb
39
+ - lib/marvin/handler.rb
35
40
  - lib/marvin/irc
36
41
  - lib/marvin/irc/abstract_server.rb
42
+ - lib/marvin/irc/base_server.rb
37
43
  - lib/marvin/irc/client.rb
38
44
  - lib/marvin/irc/event.rb
39
45
  - lib/marvin/irc/socket_client.rb
@@ -53,16 +59,18 @@ files:
53
59
  - lib/marvin/test_client.rb
54
60
  - lib/marvin/util.rb
55
61
  - lib/marvin.rb
56
- - bin/marvin
57
- - config/settings.yml.sample
58
- - config/setup.rb
62
+ - spec/marvin
63
+ - spec/marvin/abstract_client_test.rb
64
+ - spec/spec_helper.rb
65
+ - script/daemon-runner
66
+ - script/run
59
67
  - handlers/hello_world.rb
60
68
  - handlers/logging_handler.rb
61
69
  - handlers/tweet_tweet.rb
62
- - script/daemon-runner
63
- - script/run
64
- has_rdoc: true
65
- homepage: http://github.com/sutto/marvin
70
+ - config/setup.rb
71
+ - config/settings.yml.sample
72
+ has_rdoc: false
73
+ homepage: http://blog.ninjahideout.com/
66
74
  post_install_message:
67
75
  rdoc_options: []
68
76
 
@@ -80,8 +88,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
80
88
  - !ruby/object:Gem::Version
81
89
  version: "0"
82
90
  version:
83
- requirements:
84
- - install the eventmachine gem to get better client support
91
+ requirements: []
92
+
85
93
  rubyforge_project:
86
94
  rubygems_version: 1.2.0
87
95
  signing_key: