rlyeh 0.0.2 → 0.1.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.
data/.gitignore CHANGED
@@ -3,6 +3,7 @@
3
3
  .bundle
4
4
  .config
5
5
  .yardoc
6
+ .rvmrc
6
7
  Gemfile.lock
7
8
  InstalledFiles
8
9
  _yardoc
@@ -0,0 +1,18 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ $:.unshift 'lib', '../lib'
4
+ require 'rlyeh'
5
+
6
+ class BasicServer < Rlyeh::Base
7
+ use Rlyeh::DeepOnes::Logger
8
+ use Rlyeh::DeepOnes::Auth::Basic, 'dankogai', 'kogaidan'
9
+
10
+ set :server_name, 'basic-server'
11
+ set :server_version, '1.0.0'
12
+
13
+ on :privmsg do |env|
14
+ puts "RECV: #{env.message}"
15
+ end
16
+ end
17
+
18
+ Rlyeh.emerge BasicServer, :host => '0.0.0.0'
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # -*- coding: utf-8 -*-
3
- $LOAD_PATH.unshift 'lib', '../lib'
3
+ $:.unshift 'lib', '../lib'
4
4
  require 'rlyeh'
5
5
 
6
6
  class Echo < Rlyeh::Base
@@ -5,7 +5,7 @@ require 'rlyeh'
5
5
  require 'logger'
6
6
 
7
7
  class MyMiddleware
8
- include Rlyeh::Dispatcher
8
+ include Rlyeh::Dispatchable
9
9
 
10
10
  on :privmsg do |env|
11
11
  p "Middleware: #{env.message}"
@@ -0,0 +1,21 @@
1
+ #!/usr/bin/env ruby
2
+ # -*- coding: utf-8 -*-
3
+ $LOAD_PATH.unshift 'lib', '../lib'
4
+ require 'rlyeh'
5
+
6
+ class OAuthServer < Rlyeh::Base
7
+ use Rlyeh::DeepOnes::Logger
8
+ use Rlyeh::DeepOnes::Auth::OAuth, 'consumer key', 'consumer secret', {
9
+ :authorize_path => '/oauth/authenticate',
10
+ :site => 'https://api.twitter.com'
11
+ }
12
+
13
+ set :server_name, 'oauth-server'
14
+ set :server_version, '1.0.0'
15
+
16
+ on :privmsg do |env|
17
+ puts "RECV: #{env.message}"
18
+ end
19
+ end
20
+
21
+ Rlyeh.emerge OAuthServer, :host => '0.0.0.0'
@@ -1,21 +1,39 @@
1
- require 'eventmachine'
1
+ require 'rlyeh/version'
2
2
 
3
- module Rlyeh
4
- autoload :VERSION, 'rlyeh/version'
3
+ require 'rlyeh/logger'
4
+ require 'rlyeh/dispatcher'
5
+ require 'rlyeh/sender'
6
+
7
+ require 'rlyeh/settings'
8
+ require 'rlyeh/server'
9
+ require 'rlyeh/connection'
10
+ require 'rlyeh/session'
11
+ require 'rlyeh/environment'
12
+ require 'rlyeh/numeric_reply'
13
+ require 'rlyeh/target'
14
+ require 'rlyeh/base'
15
+
16
+ require 'rlyeh/deep_ones'
5
17
 
6
- autoload :Server, 'rlyeh/server'
7
- autoload :Connection, 'rlyeh/connection'
8
- autoload :Session, 'rlyeh/session'
9
- autoload :Environment, 'rlyeh/environment'
10
- autoload :NumericReply, 'rlyeh/numeric_reply'
11
- autoload :Base, 'rlyeh/base'
18
+ module Rlyeh
19
+ class << self
20
+ attr_accessor :logger
12
21
 
13
- autoload :Dispatcher, 'rlyeh/dispatcher'
14
- autoload :Filter, 'rlyeh/filter'
15
- autoload :Settings, 'rlyeh/settings'
22
+ def run(app_class, options = {})
23
+ supervisor = Rlyeh::Server.supervise_as :server, app_class, options
16
24
 
17
- require 'rlyeh/deep_ones'
25
+ trap(:INT) do
26
+ supervisor.terminate
27
+ exit
28
+ end
18
29
 
19
- require 'rlyeh/runner'
20
- include Rlyeh::Runner
30
+ sleep
31
+ end
32
+ alias_method :emerge, :run
33
+ end
21
34
  end
35
+
36
+ require 'logger'
37
+ #Celluloid.logger = nil
38
+ Rlyeh.logger = ::Logger.new $stderr
39
+
@@ -10,6 +10,8 @@ module Rlyeh
10
10
 
11
11
  set :server_name, 'Rlyeh'
12
12
  set :server_version, Rlyeh::VERSION
13
+ set :available_user_modes, ''
14
+ set :available_channel_modes, ''
13
15
 
14
16
  class << self
15
17
  def middlewares
@@ -58,8 +60,7 @@ module Rlyeh
58
60
  end
59
61
 
60
62
  def call(env)
61
- name = env.message.command.to_s.downcase
62
- trigger name, env
63
+ dispatch env
63
64
  @app.call env if @app
64
65
  end
65
66
 
@@ -1,55 +1,110 @@
1
+ require 'celluloid'
2
+ require 'forwardable'
3
+ require 'rlyeh/logger'
4
+ require 'rlyeh/sender'
5
+ require 'rlyeh/worker'
1
6
  require 'rlyeh/environment'
2
- require 'rlyeh/filters'
3
- require 'rlyeh/sendable'
4
7
 
5
8
  module Rlyeh
6
- class Connection < EventMachine::Connection
7
- include EventMachine::Protocols::LineText2
8
- include Rlyeh::Sendable
9
+ class Connection
10
+ include Rlyeh::Logger
11
+ include Rlyeh::Sender
12
+ include Rlyeh::Worker
13
+ extend Forwardable
9
14
 
10
- attr_reader :server, :app_class, :options
11
- attr_reader :app, :session
15
+ attr_reader :server, :socket
16
+ attr_reader :app, :host, :port, :session
12
17
 
13
- def initialize(server, app_class, options)
18
+ def_delegators :@socket, :close, :closed?
19
+
20
+ BUFFER_SIZE = 4096
21
+
22
+ def initialize(server, socket)
14
23
  @server = server
15
- @app_class = app_class
16
- @options = options
17
- set_delimiter "\r\n"
24
+ @socket = socket
25
+ _, @port, @host = @socket.peeraddr
26
+ @app = @server.app_class.new nil
27
+
28
+ debug "Connection started: #{@host}:#{@port}"
18
29
  end
19
30
 
20
- def post_init
21
- @app = app_class.new nil, @options
31
+ def close
32
+ @socket.close unless @socket.closed?
33
+
34
+ if attached?
35
+ @session.detach self
36
+
37
+ if @session.empty?
38
+ @session.close
39
+ @server.sessions.delete @session.id
40
+ end
41
+ end
42
+
43
+ debug "Connection closed: #{@host}:#{@port}"
22
44
  end
23
45
 
24
- def unbind
25
- @server.unbind self
46
+ def run
47
+ catch :quit do
48
+ read_each do |data|
49
+ invoke do
50
+ process data
51
+ end
52
+ end
53
+ end
26
54
  end
27
55
 
28
- def receive_line(data)
56
+ def read_each(&block)
57
+ loop do
58
+ @buffer ||= ''.force_encoding('ASCII-8BIT')
59
+ @buffer << @socket.readpartial(BUFFER_SIZE).force_encoding('ASCII-8BIT')
60
+
61
+ while data = @buffer.slice!(/(.+)\n/, 1)
62
+ block.call data.chomp if block
63
+ end
64
+ end
65
+ rescue EOFError => e
66
+ # client disconnected
67
+ rescue Celluloid::Task::TerminatedError
68
+ # kill a running task
69
+ end
70
+
71
+ def process(data)
29
72
  env = Rlyeh::Environment.new
73
+ env.version = Rlyeh::VERSION
30
74
  env.data = data
31
75
  env.server = @server
32
76
  env.connection = self
33
- env.settings = @app_class.settings
77
+ env.settings = @server.app_class.settings
34
78
 
35
79
  catch :halt do
36
- @app.call env
80
+ begin
81
+ @app.call env
82
+ rescue Exception => e
83
+ crash e
84
+ end
85
+ end
86
+ end
87
+
88
+ def send_data(data, multicast = true)
89
+ data = data.to_s
90
+ if multicast && attached?
91
+ @session.send_data data
92
+ else
93
+ @socket.write data
37
94
  end
38
95
  end
39
96
 
40
- def attached(session)
97
+ def attach(session)
41
98
  @session = session
42
99
  end
43
100
 
44
- def detached(session)
101
+ def detach(session)
45
102
  @session = nil
46
103
  end
47
104
 
48
105
  def attached?
49
106
  !!@session
50
107
  end
51
-
52
- include Rlyeh::Filters
53
- define_filters :attached, :detached
54
108
  end
55
109
  end
110
+
@@ -1,72 +1,10 @@
1
- require 'socket'
2
- require 'rlyeh/dispatcher'
3
-
4
1
  module Rlyeh
5
2
  module DeepOnes
6
- class Auth
7
- include Rlyeh::Dispatcher
8
-
9
- attr_reader :nick, :pass, :user, :real, :host
10
-
11
- def initialize(app, &authenticator)
12
- @app = app
13
- @authenticator = authenticator
14
- @authorized = false
15
- end
16
-
17
- def call(env)
18
- dispatch env
19
-
20
- if authorized?
21
- env.auth = self
22
- @app.call env
23
- end
24
- end
25
-
26
- def load_session(env, id)
27
- env.server.sessions[id] ||= Rlyeh::Session.new(id)
28
- end
29
-
30
- def authorized?
31
- @authorized
32
- end
33
-
34
- def authorized(env)
35
- name = env.settings.server_name
36
- version = env.settings.server_version
37
-
38
- env.connection.tap do |conn|
39
- conn.send_numeric_reply :welcome, @host, "Welcome to the Internet Relay Network #{@nick}!#{@user}@#{@host}"
40
- conn.send_numeric_reply :yourhost, @host, "Your host is #{name}, running version #{version}"
41
- conn.send_numeric_reply :created, @host, "This server was created #{Time.now}"
42
- conn.send_numeric_reply :myinfo, @host, "#{name} #{version} #{""} #{""}"
43
- end
44
- end
45
-
46
- on :pass do |env|
47
- @pass = env.message.params[0]
48
- end
49
-
50
- on :nick do |env|
51
- @nick = env.message.params[0]
52
- end
53
-
54
- on :user do |env|
55
- @user = env.message.params[0]
56
- @real = env.message.params[3]
57
- port, @host = Socket.unpack_sockaddr_in(env.connection.get_peername)
58
-
59
- session_id = @authenticator.call(self)
60
- if session_id.nil?
61
- p "Auth error"
62
- else
63
- @authorized = true
64
- session = load_session env, session_id
65
- session.attach env.connection
66
- authorized env
67
- p 'Session attached.'
68
- end
69
- end
3
+ module Auth
4
+ autoload :Base, 'rlyeh/deep_ones/auth/base'
5
+ autoload :Null, 'rlyeh/deep_ones/auth/null'
6
+ autoload :Basic, 'rlyeh/deep_ones/auth/basic'
7
+ autoload :OAuth, 'rlyeh/deep_ones/auth/oauth'
70
8
  end
71
9
  end
72
10
  end
@@ -0,0 +1,88 @@
1
+ require 'rlyeh/dispatcher'
2
+ require 'rlyeh/logger'
3
+
4
+ module Rlyeh
5
+ module DeepOnes
6
+ module Auth
7
+ class Base
8
+ include Rlyeh::Dispatcher
9
+ include Rlyeh::Logger
10
+
11
+ attr_reader :nick, :pass, :user, :real, :host
12
+
13
+ def initialize(app, &block)
14
+ @app = app
15
+ @authenticated = false
16
+ instance_eval &block if block
17
+ end
18
+
19
+ def call(env)
20
+ dispatch env
21
+
22
+ if authenticated?
23
+ env.auth = self
24
+ @app.call env if @app
25
+ end
26
+ end
27
+
28
+ def authenticated?
29
+ @authenticated
30
+ end
31
+
32
+ def try(env)
33
+ raise NotImplementedError
34
+ end
35
+
36
+ def session_id
37
+ @nick
38
+ end
39
+
40
+ def succeeded(env)
41
+ @authorized = true
42
+ session = env.server.load_session session_id
43
+ session.attach env.connection
44
+ debug "Authentication succeeded #{env.connection.host}:#{env.connection.port}"
45
+ end
46
+
47
+ def failed(env)
48
+ env.connection.send_numeric_reply :passwdmismatch, @host, ':Password incorrect'
49
+ debug "Authentication failed #{env.connection.host}:#{env.connection.port}"
50
+ end
51
+
52
+ def welcome(env)
53
+ name = env.settings.server_name
54
+ version = env.settings.server_version
55
+ user_modes = env.settings.available_user_modes
56
+ channel_modes = env.settings.available_channel_modes
57
+
58
+ messages = {
59
+ :welcome => "Welcome to the Internet Relay Network #{@nick}!#{@user}@#{@host}",
60
+ :yourhost => "Your host is #{name}, running version #{version}",
61
+ :created => "This server was created #{Time.now}",
62
+ :myinfo => "#{name} #{version} #{user_modes} #{channel_modes}"
63
+ }
64
+
65
+ messages.each do |type, message|
66
+ env.connection.send_numeric_reply type, @nick, ":#{message}", :prefix => {:servername => name}
67
+ end
68
+ end
69
+
70
+ on :pass do |env|
71
+ @pass = env.message.params[0]
72
+ end
73
+
74
+ on :nick do |env|
75
+ @nick = env.message.params[0]
76
+ end
77
+
78
+ on :user do |env|
79
+ @user = env.message.params[0]
80
+ @real = env.message.params[3]
81
+ @host = env.connection.host
82
+
83
+ try env
84
+ end
85
+ end
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,21 @@
1
+ module Rlyeh
2
+ module DeepOnes
3
+ module Auth
4
+ class Basic < Rlyeh::DeepOnes::Auth::Base
5
+ def initialize(app, nick, pass)
6
+ @basic = [nick, pass].freeze
7
+ super app
8
+ end
9
+
10
+ def try(env)
11
+ if @basic == [nick, pass]
12
+ succeeded env
13
+ welcome env
14
+ else
15
+ failed env
16
+ end
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,12 @@
1
+ module Rlyeh
2
+ module DeepOnes
3
+ module Auth
4
+ class Null < Rlyeh::DeepOnes::Auth::Base
5
+ def try(env)
6
+ succeeded env
7
+ welcome env
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,55 @@
1
+ require 'oauth'
2
+
3
+ module Rlyeh
4
+ module DeepOnes
5
+ module Auth
6
+ class OAuth < Rlyeh::DeepOnes::Auth::Base
7
+ attr_accessor :consumer_key, :consumer_secret, :client_options
8
+ attr_accessor :open_timeout, :read_timeout
9
+ attr_accessor :authorize_params, :request_params
10
+ attr_reader :access_token
11
+
12
+ def initialize(app, consumer_key, consumer_secret, client_options = {})
13
+ @consumer_key = consumer_key
14
+ @consumer_secret = consumer_secret
15
+ @client_options = client_options
16
+ @open_timeout = nil
17
+ @read_timeout = nil
18
+ @authorize_params = {}
19
+ @request_params = {}
20
+ super app
21
+ end
22
+
23
+ def consumer
24
+ consumer = ::OAuth::Consumer.new @consumer_key, @consumer_secret, @client_options
25
+ consumer.http.open_timeout = @open_timeout if @open_timeout
26
+ consumer.http.read_timeout = @read_timeout if @read_timeout
27
+ consumer
28
+ end
29
+
30
+ def try(env)
31
+ welcome env
32
+ request_phase env
33
+ end
34
+
35
+ def request_phase(env)
36
+ request_token = consumer.get_request_token @request_params
37
+
38
+ talk env, 'Access this URL get the PIN and paste it here.'
39
+ talk env, request_token.authorize_url(@authorize_params)
40
+ end
41
+
42
+ protected
43
+
44
+ def sender_nick
45
+ '$oauth'
46
+ end
47
+
48
+ def talk(env, text)
49
+ target = Target.new env, @nick
50
+ target.privmsg text, sender_nick
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -4,14 +4,18 @@ module Rlyeh
4
4
  module DeepOnes
5
5
  class Closer
6
6
  include Rlyeh::Dispatcher
7
-
7
+
8
8
  def initialize(app)
9
9
  @app = app
10
10
  end
11
11
 
12
+ def call(env)
13
+ dispatch env
14
+ @app.call env if @app
15
+ end
16
+
12
17
  on :quit do |env|
13
- env.connection.close_connection_after_writing
14
- throw :halt
18
+ throw :quit
15
19
  end
16
20
  end
17
21
  end
@@ -1,21 +1,23 @@
1
- require 'logger'
1
+ require 'rlyeh/logger'
2
2
 
3
3
  module Rlyeh
4
4
  module DeepOnes
5
5
  class Logger
6
- def initialize(app, options = {})
6
+ include Rlyeh::Logger
7
+
8
+ def initialize(app, logger = nil, level = :debug)
7
9
  @app = app
8
- @logger = options[:logger] || ::Logger.new(STDOUT)
9
- @logger.level = options[:level] || ::Logger::INFO
10
+ @logger = logger || self
11
+ @level = level
10
12
  end
11
13
 
12
14
  def call(env)
13
15
  write env
14
- @app.call env
16
+ @app.call env if @app
15
17
  end
16
18
 
17
19
  def write(env)
18
- @logger.debug env.data
20
+ @logger.__send__ @level, "Message received: #{env.data}"
19
21
  end
20
22
  end
21
23
  end
@@ -1,8 +1,11 @@
1
1
  require 'ircp'
2
+ require 'rlyeh/logger'
2
3
 
3
4
  module Rlyeh
4
5
  module DeepOnes
5
6
  class Parser
7
+ include Rlyeh::Logger
8
+
6
9
  def initialize(app)
7
10
  @app = app
8
11
  end
@@ -11,9 +14,10 @@ module Rlyeh
11
14
  begin
12
15
  message = Ircp.parse env.data
13
16
  env.message = message
14
- @app.call env
17
+ env.event = message.command.to_s.downcase
18
+ @app.call env if @app
15
19
  rescue Ircp::ParseError => e
16
- p e
20
+ debug "#{e.class}: #{e.to_s}"
17
21
  end
18
22
  end
19
23
  end
@@ -6,7 +6,7 @@ module Rlyeh
6
6
  end
7
7
 
8
8
  def call(env)
9
- @app.call env
9
+ @app.call env if @app
10
10
  end
11
11
  end
12
12
  end
@@ -7,45 +7,43 @@ module Rlyeh
7
7
  end
8
8
 
9
9
  module ClassMethods
10
- def callbacks(name)
11
- @dispatchers ||= {}
12
-
13
- callbacks = @dispatchers.select do |key, value|
14
- if key.is_a?(Regexp)
15
- key =~ name
16
- else
17
- key == name
10
+ def callbacks(target)
11
+ @dispatchers ||= []
12
+ callbacks = @dispatchers.select do |item|
13
+ key = item[0]
14
+ case key
15
+ when Regexp then key =~ target
16
+ else key == target
18
17
  end
19
- end.values.flatten
18
+ end.map { |item| item[1] }.flatten
20
19
 
21
20
  if superclass.respond_to?(:callbacks)
22
- superclass.callbacks(name) + callbacks
21
+ superclass.callbacks(target) + callbacks
23
22
  else
24
23
  callbacks
25
24
  end
26
25
  end
27
26
 
28
- def on(name, &block)
29
- @dispatchers ||= {}
30
- name = name.to_s if name.is_a?(Symbol)
31
- callbacks = (@dispatchers[name] ||= [])
32
- callbacks << Rlyeh::Utils.generate_method(self, "__on_#{name}", block)
33
- callbacks.uniq!
34
- end
35
- end
27
+ def on(*args, &block)
28
+ options = Rlyeh::Utils.extract_options! args
29
+ target = args.shift
30
+ target = Array(options[:scope]) + Array(target) if [String, Symbol].any? { |type| target.is_a?(type) }
31
+ target = target.join('.') if target.is_a?(Array)
36
32
 
37
- def call(env)
38
- dispatch env
39
- @app.call env if @app
33
+ @dispatchers ||= []
34
+ @dispatchers << [target, Rlyeh::Utils.generate_method(self, "__on_#{target}", block)]
35
+ end
40
36
  end
41
37
 
42
38
  def dispatch(env)
43
- name = env.message.command.to_s.downcase
44
- trigger name, env
39
+ if env.has_event?
40
+ target = env.event
41
+ trigger target, env
42
+ end
45
43
  end
46
44
 
47
- def trigger(name, *args, &block)
48
- callbacks = self.class.callbacks name
45
+ def trigger(target, *args, &block)
46
+ callbacks = self.class.callbacks target
49
47
  callbacks.each do |callback|
50
48
  break if callback.bind(self).call(*args, &block) == false
51
49
  end
@@ -4,9 +4,10 @@ module Rlyeh
4
4
  super || respond_to_missing?(method_id, include_private)
5
5
  end
6
6
 
7
- def respond_to_missing?(method_name, include_private)
7
+ def respond_to_missing?(method_id, include_private)
8
8
  method_name = method_id.to_s
9
9
  return true if method_name =~ /^(.+)=$/
10
+ return true if method_name =~ /^has_(.+)\?$/
10
11
  return true if self.key? method_name
11
12
  super
12
13
  end
@@ -14,8 +15,9 @@ module Rlyeh
14
15
  def method_missing(method_id, *args, &block)
15
16
  method_name = method_id.to_s
16
17
  return self[$1] = args.first if method_name =~ /^(.+)=$/
18
+ return self.has_key? $1 if method_name =~ /^has_(.+)\?$/
17
19
  return self[method_name] if self.key? method_name
18
- super method_id, *args, &block
20
+ super
19
21
  end
20
22
  end
21
23
  end
@@ -0,0 +1,20 @@
1
+ module Rlyeh
2
+ module Logger
3
+ module_function
4
+
5
+ [:debug, :info, :warn, :error, :fatal].each do |level|
6
+ define_method level do |message|
7
+ Rlyeh.logger.__send__(level, message) if Rlyeh.logger
8
+ end
9
+ end
10
+
11
+ def crash(exception, message = nil)
12
+ error [message, format_exception(exception)].compact.join("\n")
13
+ end
14
+
15
+ def format_exception(exception)
16
+ str = "#{exception.class}: #{exception.to_s}\n"
17
+ str << exception.backtrace.map { |s| " #{s}" }.join("\n")
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,18 @@
1
+ require 'ircp'
2
+ require 'rlyeh/numeric_reply'
3
+ require 'rlyeh/utils'
4
+
5
+ module Rlyeh
6
+ module Sender
7
+ def send_message(command, *args)
8
+ options = Rlyeh::Utils.extract_options! args
9
+ send_data Ircp::Message.new(*args, options.merge(:command => command))
10
+ end
11
+
12
+ def send_numeric_reply(type, target, *args)
13
+ options = Rlyeh::Utils.extract_options! args
14
+ numeric = Rlyeh::NumericReply.to_value type
15
+ send_data Ircp::Message.new(target, *args, options.merge(:command => numeric))
16
+ end
17
+ end
18
+ end
@@ -1,57 +1,51 @@
1
+ require 'celluloid/io'
1
2
  require 'rlyeh/connection'
2
- require 'rlyeh/utils'
3
- require 'rlyeh/filters'
3
+ require 'rlyeh/logger'
4
4
 
5
5
  module Rlyeh
6
6
  class Server
7
+ include Celluloid::IO
8
+ include Rlyeh::Logger
9
+
7
10
  attr_reader :options, :host, :port
8
- attr_reader :app_class, :signature, :sessions
9
-
10
- def initialize(*args)
11
- @options = Rlyeh::Utils.extract_options! args
12
- @host = @options.delete(:host) || "127.0.0.1"
13
- @port = @options.delete(:port) || 46667
14
- @app_class = args.shift
15
- @signature = nil
11
+ attr_reader :app_class, :sessions, :server
12
+
13
+ def initialize(app_class, options = {})
14
+ @app_class = app_class
15
+ @options = options.dup
16
+ @host = @options.delete(:host) { '127.0.0.1' }
17
+ @port = @options.delete(:port) { 46667 }
16
18
  @sessions = {}
17
- end
19
+ @server = Celluloid::IO::TCPServer.new @host, @port
18
20
 
19
- def self.start(*args)
20
- new(*args).start
21
+ info "Rlyeh has emerged on #{@host}:#{@port}"
22
+ async.run
21
23
  end
22
24
 
23
- def start
24
- args = [self, @app_class, options]
25
- @signature = EventMachine.start_server @host, @port, Rlyeh::Connection, *args do |connection|
26
- bind connection
27
- end
28
- self
25
+ def finalize
26
+ @server.close
27
+ info 'Rlyeh has sunk...'
29
28
  end
30
29
 
31
- def stop
32
- EventMachine.stop_server @signature if @signature
33
- @signature = nil
30
+ def run
31
+ loop do
32
+ socket = @server.accept
33
+ async.handle_connection socket
34
+ end
34
35
  end
35
36
 
36
- def bind(connection)
37
- puts 'bind'
37
+ def handle_connection(socket)
38
+ connection = Connection.new(self, socket)
39
+ connection.run
40
+ rescue Exception => e
41
+ crash e
42
+ ensure
43
+ connection.close rescue nil
38
44
  end
39
45
 
40
- def unbind(connection)
41
- if connection.attached?
42
- session = connection.session
43
- session.detach connection
44
-
45
- if session.empty?
46
- session.close
47
- @sessions.delete session.id
48
- end
49
- end
50
-
51
- puts 'unbind'
46
+ def load_session(session_id)
47
+ @sessions[session_id] ||= Rlyeh::Session.new(session_id)
52
48
  end
53
-
54
- include Rlyeh::Filters
55
- define_filters :start, :stop, :bind, :unbind
56
49
  end
57
50
  end
51
+
@@ -1,42 +1,41 @@
1
- require 'rlyeh/filters'
1
+ require 'celluloid'
2
+ require 'set'
3
+ require 'forwardable'
4
+ require 'rlyeh/logger'
2
5
 
3
6
  module Rlyeh
4
7
  class Session
5
- attr_reader :id, :channel, :connections
8
+ include Celluloid
9
+ include Rlyeh::Logger
10
+ extend Forwardable
11
+
12
+ attr_reader :id, :connections
13
+ def_delegators :@connections, :include?, :empty?
6
14
 
7
15
  def initialize(id)
8
16
  @id = id
9
- @channel = EventMachine::Channel.new
10
- @connections = {}
17
+ @connections = Set.new
18
+ debug "Session started: #{@id}"
11
19
  end
12
20
 
13
- def attach(connection)
14
- connection.attached self
15
-
16
- @connections[connection] = @channel.subscribe do |msg|
17
- connection.send_data msg
18
- end
21
+ def close
22
+ debug "Session closed: #{@id}"
19
23
  end
20
24
 
21
- def detach(connection)
22
- id = @connections.delete connection
23
- @channel.unsubscribe id if id
24
-
25
- connection.detached self
25
+ def attach(connection)
26
+ connection.attach self
27
+ @connections.add connection
26
28
  end
27
29
 
28
- def close
30
+ def detach(connection)
31
+ @connections.delete connection
32
+ connection.detach self
29
33
  end
30
34
 
31
35
  def send_data(data)
32
- @channel.push data
33
- end
34
-
35
- def empty?
36
- @connections.empty?
36
+ @connections.each do |connection|
37
+ connection.send_data data, false
38
+ end
37
39
  end
38
-
39
- include Rlyeh::Filters
40
- define_filters :attach, :detach, :close, :send_data
41
40
  end
42
41
  end
@@ -0,0 +1,27 @@
1
+ module Rlyeh
2
+ class Target
3
+ def initialize(env, target)
4
+ @env = env
5
+ @target = target
6
+ end
7
+
8
+ def send_message(command, text, prefix)
9
+ prefix = {:nick => prefix.to_s} unless prefix.is_a?(Hash)
10
+ unless prefix.key? :servername
11
+ prefix[:nick] ||= 'rlyeh'
12
+ prefix[:user] ||= 'rlyeh'
13
+ prefix[:host] ||= @env.settings.server_name
14
+ end
15
+
16
+ @env.connection.send_message command, @target, ":#{text}", :prefix => prefix
17
+ end
18
+
19
+ def privmsg(text, prefix = nil)
20
+ send_message 'PRIVMSG', text, prefix
21
+ end
22
+
23
+ def notice(text, prefix = nil)
24
+ send_message 'NOTICE', text, prefix
25
+ end
26
+ end
27
+ end
@@ -1,3 +1,3 @@
1
1
  module Rlyeh
2
- VERSION = "0.0.2"
2
+ VERSION = "0.1.1"
3
3
  end
@@ -0,0 +1,38 @@
1
+ require 'celluloid'
2
+
3
+ module Rlyeh
4
+ module Worker
5
+ class ThreadPool
6
+ include Celluloid
7
+
8
+ def invoke(*args, &block)
9
+ block.call *args if block
10
+ end
11
+ end
12
+
13
+ class << self
14
+ def included(base)
15
+ base.extend ClassMethods
16
+ end
17
+
18
+ def setup(options = {})
19
+ @worker_pool = ThreadPool.pool options
20
+ end
21
+
22
+ def worker_pool
23
+ @worker_pool ||= setup
24
+ end
25
+ end
26
+
27
+ module ClassMethods
28
+ def worker_pool
29
+ @worker_pool ||= Rlyeh::Worker.worker_pool
30
+ end
31
+ end
32
+
33
+ def invoke(*args, &block)
34
+ self.class.worker_pool.invoke *args, &block
35
+ end
36
+ end
37
+ end
38
+
@@ -15,8 +15,8 @@ Gem::Specification.new do |gem|
15
15
  gem.require_paths = ["lib"]
16
16
  gem.version = Rlyeh::VERSION
17
17
 
18
+ gem.add_dependency 'celluloid-io'
18
19
  gem.add_dependency 'ircp'
19
- gem.add_dependency 'eventmachine'
20
20
  gem.add_development_dependency 'rake'
21
21
  gem.add_development_dependency 'rspec'
22
22
  end
@@ -0,0 +1,42 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rlyeh::Environment do
4
+ before do
5
+ @env = Rlyeh::Environment.new
6
+ @env.foo = 1
7
+ @env.bar = 2
8
+ end
9
+ subject { @env }
10
+
11
+ describe '#respond_to?' do
12
+ it { should be_respond_to :foo }
13
+ it { should be_respond_to :bar }
14
+ it { should_not be_respond_to :buzz }
15
+ end
16
+
17
+ describe '#method_missing' do
18
+ context 'fetch' do
19
+ its(:foo) { should eq 1 }
20
+ its(:bar) { should eq 2 }
21
+ it { lambda { subject.buzz }.should raise_error NoMethodError }
22
+ end
23
+
24
+ context 'assign' do
25
+ context 'foo' do
26
+ before { subject.foo = 'assigned' }
27
+ its(:foo) { should eq 'assigned' }
28
+ end
29
+
30
+ context 'buzz' do
31
+ before { subject.buzz = 'assigned' }
32
+ its(:buzz) { should eq 'assigned' }
33
+ end
34
+ end
35
+
36
+ context 'has' do
37
+ it { should be_has_foo }
38
+ it { should be_has_bar }
39
+ it { should_not be_has_buzz }
40
+ end
41
+ end
42
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rlyeh
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.1.1
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-03-26 00:00:00.000000000 Z
12
+ date: 2012-09-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: ircp
16
- requirement: &24772480 !ruby/object:Gem::Requirement
15
+ name: celluloid-io
16
+ requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,15 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *24772480
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
25
30
  - !ruby/object:Gem::Dependency
26
- name: eventmachine
27
- requirement: &24772060 !ruby/object:Gem::Requirement
31
+ name: ircp
32
+ requirement: !ruby/object:Gem::Requirement
28
33
  none: false
29
34
  requirements:
30
35
  - - ! '>='
@@ -32,10 +37,15 @@ dependencies:
32
37
  version: '0'
33
38
  type: :runtime
34
39
  prerelease: false
35
- version_requirements: *24772060
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
36
46
  - !ruby/object:Gem::Dependency
37
47
  name: rake
38
- requirement: &24771600 !ruby/object:Gem::Requirement
48
+ requirement: !ruby/object:Gem::Requirement
39
49
  none: false
40
50
  requirements:
41
51
  - - ! '>='
@@ -43,10 +53,15 @@ dependencies:
43
53
  version: '0'
44
54
  type: :development
45
55
  prerelease: false
46
- version_requirements: *24771600
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
47
62
  - !ruby/object:Gem::Dependency
48
63
  name: rspec
49
- requirement: &24771160 !ruby/object:Gem::Requirement
64
+ requirement: !ruby/object:Gem::Requirement
50
65
  none: false
51
66
  requirements:
52
67
  - - ! '>='
@@ -54,7 +69,12 @@ dependencies:
54
69
  version: '0'
55
70
  type: :development
56
71
  prerelease: false
57
- version_requirements: *24771160
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: '0'
58
78
  description: Welcome to the deep sea
59
79
  email:
60
80
  - mail@mashiro.org
@@ -68,14 +88,19 @@ files:
68
88
  - LICENSE
69
89
  - README.md
70
90
  - Rakefile
91
+ - examples/basic.rb
71
92
  - examples/echo.rb
72
93
  - examples/middleware.rb
73
- - examples/simple.rb
94
+ - examples/oauth.rb
74
95
  - lib/rlyeh.rb
75
96
  - lib/rlyeh/base.rb
76
97
  - lib/rlyeh/connection.rb
77
98
  - lib/rlyeh/deep_ones.rb
78
99
  - lib/rlyeh/deep_ones/auth.rb
100
+ - lib/rlyeh/deep_ones/auth/base.rb
101
+ - lib/rlyeh/deep_ones/auth/basic.rb
102
+ - lib/rlyeh/deep_ones/auth/null.rb
103
+ - lib/rlyeh/deep_ones/auth/oauth.rb
79
104
  - lib/rlyeh/deep_ones/builder.rb
80
105
  - lib/rlyeh/deep_ones/closer.rb
81
106
  - lib/rlyeh/deep_ones/logger.rb
@@ -84,15 +109,18 @@ files:
84
109
  - lib/rlyeh/dispatcher.rb
85
110
  - lib/rlyeh/environment.rb
86
111
  - lib/rlyeh/filters.rb
112
+ - lib/rlyeh/logger.rb
87
113
  - lib/rlyeh/numeric_reply.rb
88
- - lib/rlyeh/runner.rb
89
- - lib/rlyeh/sendable.rb
114
+ - lib/rlyeh/sender.rb
90
115
  - lib/rlyeh/server.rb
91
116
  - lib/rlyeh/session.rb
92
117
  - lib/rlyeh/settings.rb
118
+ - lib/rlyeh/target.rb
93
119
  - lib/rlyeh/utils.rb
94
120
  - lib/rlyeh/version.rb
121
+ - lib/rlyeh/worker.rb
95
122
  - rlyeh.gemspec
123
+ - spec/rlyeh/environment_spec.rb
96
124
  - spec/rlyeh/numeric_reply_spec.rb
97
125
  - spec/rlyeh/server_spec.rb
98
126
  - spec/rlyeh/settings_spec.rb
@@ -111,7 +139,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
111
139
  version: '0'
112
140
  segments:
113
141
  - 0
114
- hash: -3713533615144336693
142
+ hash: -2088290763749664641
115
143
  required_rubygems_version: !ruby/object:Gem::Requirement
116
144
  none: false
117
145
  requirements:
@@ -120,10 +148,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
120
148
  version: '0'
121
149
  segments:
122
150
  - 0
123
- hash: -3713533615144336693
151
+ hash: -2088290763749664641
124
152
  requirements: []
125
153
  rubyforge_project:
126
- rubygems_version: 1.8.17
154
+ rubygems_version: 1.8.21
127
155
  signing_key:
128
156
  specification_version: 3
129
157
  summary: IRC gateway server framework
@@ -1,20 +0,0 @@
1
- #!/usr/bin/env ruby
2
- # -*- coding: utf-8 -*-
3
- $LOAD_PATH.unshift 'lib', '../lib'
4
- require 'rlyeh'
5
-
6
- class MyApp < Rlyeh::Base
7
- use Rlyeh::DeepOnes::Logger, :level => 0
8
- use Rlyeh::DeepOnes::Auth do |auth|
9
- auth.nick if [auth.nick, auth.pass] == ['dankogai', 'kogaidan']
10
- end
11
-
12
- set :server_name, 'MyApp'
13
- set :server_version, '1.0.0'
14
-
15
- on :privmsg do |env|
16
- puts "RECV: #{env.message}"
17
- end
18
- end
19
-
20
- Rlyeh.emerge MyApp, :host => '0.0.0.0'
@@ -1,42 +0,0 @@
1
- require 'rlyeh/utils'
2
-
3
- module Rlyeh
4
- module Runner
5
- def self.included(base)
6
- base.extend ClassMethods
7
- end
8
-
9
- module ClassMethods
10
- def servers
11
- @servers ||= []
12
- end
13
-
14
- def run(*args, &block)
15
- default_options = {:signal_trap => true}.freeze
16
- options = default_options.merge(Rlyeh::Utils.extract_options args)
17
- signal_trap = options.delete(:signal_trap)
18
-
19
- trap(:INT) { stop } if signal_trap
20
-
21
- starter = proc do
22
- server = Server.start *args, &block
23
- servers << server
24
- end
25
-
26
- if EventMachine.reactor_running?
27
- starter.call
28
- else
29
- EventMachine.run &starter
30
- end
31
- end
32
- alias_method :emerge, :run
33
-
34
- def stop
35
- servers.each { |server| server.stop }
36
- servers.clear
37
-
38
- EventMachine.stop if EventMachine.reactor_running?
39
- end
40
- end
41
- end
42
- end
@@ -1,11 +0,0 @@
1
- require 'ircp'
2
- require 'rlyeh/numeric_reply'
3
-
4
- module Rlyeh
5
- module Sendable
6
- def send_numeric_reply(numeric, target, *args)
7
- numeric = Rlyeh::NumericReply.to_value numeric
8
- send_data Ircp::Message.new(target, *args, :command => numeric)
9
- end
10
- end
11
- end