rlyeh 0.0.2 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
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