hara 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
data/.travis.yml CHANGED
@@ -3,3 +3,7 @@ rvm:
3
3
  - 2.0.0
4
4
  - 1.9.3
5
5
  - jruby-19mode
6
+
7
+ matrix:
8
+ allow_failures:
9
+ - rvm: jruby-19mode
data/README.md CHANGED
@@ -3,7 +3,13 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/hara.png)](http://badge.fury.io/rb/hara)
4
4
  [![Build Status](https://travis-ci.org/jjyr/hara.png?branch=master)](https://travis-ci.org/jjyr/hara)
5
5
 
6
- Hara is a simple framework, help you build websocket server.
6
+ Hara is a simple framework, help you build async & concurrent websocket server.
7
+
8
+ Hara's async ability from [em-websocket](https://github.com/igrigorik/em-websocket),
9
+
10
+ Hara's concurrent ability from [celluloid](https://github.com/celluloid/celluloid).
11
+
12
+ Yes, hara just a combination of them, eventmachine and celluloid work well together :)
7
13
 
8
14
  ## Notice!!
9
15
 
@@ -32,7 +38,7 @@ require 'hara'
32
38
 
33
39
  class Echo < Hara::App
34
40
  define_action :echo do |str|
35
- socket << str
41
+ socket.send str
36
42
  end
37
43
  end
38
44
  ```
@@ -41,7 +47,9 @@ end
41
47
  ```javascript
42
48
  var msg = JSON.stringify({action: 'echo',args:['hello world']})
43
49
  var ws = new WebSocket('ws://localhost:3210')
44
- ws.onmessage = function(msg){console.log(msg.data)}
50
+ ws.onmessage = function(msg){alert(msg.data)}
51
+
52
+ //after a while
45
53
  ws.send(msg)
46
54
  //hello world
47
55
  ```
data/hara.gemspec CHANGED
@@ -8,8 +8,8 @@ Gem::Specification.new do |spec|
8
8
  spec.version = Hara::VERSION
9
9
  spec.authors = ["jjy"]
10
10
  spec.email = ["jjyruby@gmail.com"]
11
- spec.description = %q{Hara is a simple framework, help you build [web ]socket server. Power by reel and celluloid.}
12
- spec.summary = %q{Hara can help your build [web ]socket server easier.}
11
+ spec.description = %q{Hara help you build async & concurrent websocket server.}
12
+ spec.summary = %q{In fact, hara just a combination of em-websocket and celluloid, but it really help you write applications easily.}
13
13
  spec.homepage = "http://github.com/jjyr/hara"
14
14
  spec.license = "MIT"
15
15
 
@@ -19,7 +19,8 @@ Gem::Specification.new do |spec|
19
19
  spec.require_paths = ["lib"]
20
20
 
21
21
 
22
- spec.add_dependency "reel", "~> 0.3"
22
+ spec.add_dependency "em-websocket", "~> 0.5.0"
23
+ spec.add_dependency "celluloid", "~> 0.14.1"
23
24
 
24
25
  spec.add_development_dependency "bundler", "~> 1.3"
25
26
  spec.add_development_dependency "rake"
data/lib/hara/app.rb CHANGED
@@ -1,14 +1,32 @@
1
- require 'celluloid/io'
1
+ require "hara/version"
2
+ require 'celluloid'
2
3
  require 'json'
3
4
 
4
5
  module Hara
6
+ class << self
7
+ def env
8
+ @_env ||= (ENV['APP_ENV'] || :development).to_sym
9
+ end
10
+
11
+ def env= env
12
+ @_env = env
13
+ end
14
+
15
+ def decode_msg msg
16
+ msg = JSON.parse(msg)
17
+ msg.values_at 'action', 'args'
18
+ end
19
+
20
+ def encode_msg action, *args
21
+ {action: action, args: args}.to_json
22
+ end
23
+ end
24
+
5
25
  class App
6
- include Celluloid::IO
26
+ include Celluloid
7
27
  include Celluloid::Logger
8
28
 
9
- attr_reader :socket
10
-
11
- alias halt terminate
29
+ attr_reader :socket, :handshake
12
30
 
13
31
  finalizer :app_finalizer
14
32
 
@@ -16,34 +34,20 @@ module Hara
16
34
 
17
35
  class << self
18
36
  def inherited klass
19
- ::Hara.const_set :Application, klass
37
+ ::Hara.const_set :Application, klass
20
38
  end
21
39
 
22
40
  def define_action action, &block
23
- action = action.to_s
24
- warn "Action #{action} duplication defined" if Actions.has_key? action
25
- Hara::Application.send :define_method, action, &block
26
- method = Hara::Application.send :instance_method, action
27
- Hara::Application.send :remove_method, action
28
- Actions[action] = method
41
+ action = action.to_s
42
+ warn "Action #{action} duplication defined" if Actions.has_key? action
43
+ Hara::Application.send :define_method, action, &block
44
+ method = Hara::Application.send :instance_method, action
45
+ Hara::Application.send :remove_method, action
46
+ Actions[action] = method
29
47
  end
30
48
  end
31
49
 
32
-
33
- def init
34
- end
35
-
36
- #return true or false
37
- def authentication
38
- true
39
- end
40
-
41
- def authentication_failed
42
- halt
43
- end
44
-
45
- #after_authentication only called when authentication succeeded
46
- def after_authentication
50
+ def after_connect
47
51
  end
48
52
 
49
53
  def before_action action, args
@@ -52,83 +56,52 @@ module Hara
52
56
  def after_action action, args
53
57
  end
54
58
 
55
- def after_process
56
- end
57
-
58
59
  def action_missing action, args
59
- info " #{socket.remote_ip} request action: #{action} args: #{args.inspect}, action not defined"
60
+ info "#{headers['host']} request action: #{action} args: #{args.inspect}, action not defined"
61
+ raise NoMethodError, "undefined action '#{action}' for #{self}:#{self.class}"
60
62
  end
61
63
 
62
- def command
63
- @command || {}
64
+ def headers
65
+ handshake.headers_downcased
64
66
  end
65
67
 
66
68
  def on_close
67
69
  end
68
70
 
69
- private
70
- def initialize socket
71
- @socket = socket
72
- async.setup
73
- end
71
+ #below are internal functions(should not been overriding)
74
72
 
75
- def setup
76
- init
77
- info "#{socket.remote_ip} coming"
78
- unless authentication
79
- authentication_failed
80
- else
81
- after_authentication
82
- async.run
83
- end
73
+ def initialize handshake, socket
74
+ @handshake = handshake
75
+ @socket = socket
76
+ async.hara_setup
84
77
  end
85
78
 
86
- def run
87
- while msg = @socket.read
88
- process msg
89
- if @closed
90
- terminate
91
- return
92
- end
93
- end
94
- rescue Reel::SocketError, EOFError #client disconnect
95
- info "#{socket.remote_ip} disconnect"
96
- begin
97
- @closed = true unless @closed
98
- on_close
99
- ensure
100
- terminate
101
- end
79
+ def hara_setup
80
+ info "#{headers['host']} coming"
81
+ after_connect
102
82
  end
103
83
 
104
- def process message
105
- @command = JSON.parse(message)
106
- action = @command["action"]
107
- args = @command["args"]
108
- info "#{socket.remote_ip} request action: #{action} args: #{args.inspect}"
84
+ def process_msg message
85
+ action, args = Hara.decode_msg(message)
86
+ info "#{headers['host']} request action: #{action} args: #{args.inspect}"
109
87
  before_action action, *args
110
88
  call_action action, *args
111
89
  after_action action, *args
112
- rescue JSON::ParserError
113
- info "#{socket.remote_ip} message can't parse"
114
- terminate
115
90
  rescue StandardError => e
116
- info "#{socket.remote_ip} processing error:\n#{e.inspect}"
91
+ info "#{headers['host']} processing error:\n#{e.inspect}"
117
92
  terminate
118
- ensure
119
- after_process
120
93
  end
121
94
 
122
95
  def call_action action, *args
123
96
  if Actions.has_key? action
124
- Actions[action].bind(self).call *args
97
+ Actions[action].bind(self).call *args
125
98
  else
126
- action_missing action, *args
99
+ action_missing action, *args
127
100
  end
128
101
  end
129
102
 
130
103
  def app_finalizer
131
- on_close unless @closed
104
+ on_close
132
105
  ensure
133
106
  @socket.close if @socket
134
107
  end
data/lib/hara/base.rb CHANGED
@@ -1,24 +1,3 @@
1
1
  require "hara/version"
2
2
  require 'hara/app'
3
-
4
- module Hara
5
- class << self
6
- def env
7
- @_env ||= (ENV['APP_ENV'] || :development).to_sym
8
- end
9
-
10
- def env= env
11
- @_env = env
12
- end
13
-
14
- def request_handler &blk
15
- if blk
16
- @_request_handler = blk
17
- else
18
- @_request_handler
19
- end
20
- end
21
- end
22
- end
23
-
24
3
  require 'hara/server'
data/lib/hara/server.rb CHANGED
@@ -1,42 +1,34 @@
1
- require 'reel'
1
+ require 'em-websocket'
2
+ require 'eventmachine'
2
3
  require 'hara/base'
3
4
 
4
5
  module Hara
5
- class Server < Reel::Server
6
- include Celluloid::Logger
6
+ class Server
7
+ class << self
8
+ def start host, port
9
+ puts "Server starting on #{host}:#{port}"
10
+ EM.epoll
11
+ EM.run {
12
+ EM::WebSocket.run(host: host, port: port) do |ws|
13
+ actor = nil
7
14
 
8
- def initialize host, port
9
- info "Server starting on #{host}:#{port}"
10
- super(host, port, &method(:on_connection))
11
- end
12
-
13
- def on_connection(connection)
14
- while request = connection.request
15
- case request
16
- when Reel::WebSocket
17
- info "Received a WebSocket connection"
18
- handle_websocket request
19
- when Reel::Request
20
- Hara.request_handler.call connection, request
21
- else
22
- warn "Connection not support"
23
- request.close
24
- end
25
- end
26
- end
15
+ ws.onopen { |handshake|
16
+ actor = Hara::Application.new handshake, ws
17
+ }
27
18
 
28
- Hara.request_handler do |connection, request|
29
- info "#{request.remote_ip} request #{request.url}, not support"
30
- request.close
31
- end
32
-
33
- def handle_websocket socket
34
- Hara::Application.new socket
35
- end
19
+ ws.onclose {
20
+ begin
21
+ actor.terminate! if actor.alive?
22
+ rescue Celluloid::DeadActorError => e
23
+ end
24
+ }
36
25
 
37
- def self.start host, port
38
- self.supervise_as :hara, host, port
39
- sleep
26
+ ws.onmessage { |msg|
27
+ actor.async.process_msg msg
28
+ }
29
+ end
30
+ }
31
+ end
40
32
  end
41
33
  end
42
34
  end
data/lib/hara/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Hara
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/spec/app_spec.rb CHANGED
@@ -3,57 +3,40 @@ require 'spec_helper'
3
3
  describe Hara::App do
4
4
  before :all do
5
5
  Class.new Hara::App do
6
- def init
7
- @states = []
6
+ def after_connect
7
+ @states = []
8
8
  end
9
9
 
10
10
  def before_action action, *args
11
- @states << [:before_action, action, args]
11
+ @states << [:before_action, action, args]
12
12
  end
13
13
 
14
14
  def after_action action, *args
15
- @states << [:after_action, action, args]
16
- end
17
-
18
- def after_process
19
- @states << :after_process
15
+ @states << [:after_action, action, args]
20
16
  end
21
17
 
22
18
  def on_close
23
- @states << :closed
19
+ @states << :closed
24
20
  end
25
21
 
26
22
  def action_missing action, *args
27
- socket << [:action_missing, action, args]
23
+ socket.send [:action_missing, action, args]
28
24
  end
29
25
 
30
26
  define_action :hello do |msg|
31
- socket << "hello#{msg}"
32
- end
33
-
34
- def authentication
35
- socket.remote_ip == 'localhost'
36
- end
37
-
38
- def authentication_failed
39
- @states << :fail
40
- sleep 0.1
41
- end
42
-
43
- def after_authentication
44
- @states << :success
45
- sleep 0.1
27
+ socket.send "hello#{msg}"
46
28
  end
47
29
 
48
30
  def states
49
- @states
31
+ @states
50
32
  end
51
33
  end
52
34
  end
53
35
 
54
36
  before :each do
37
+ @handshake = FayeHandshake.new
55
38
  @socket = FayeSocket.new
56
- @socket.remote_ip = 'localhost'
39
+ @app = Hara::Application.new @handshake, @socket
57
40
  end
58
41
 
59
42
  after :each do
@@ -62,48 +45,32 @@ describe Hara::App do
62
45
  end
63
46
  end
64
47
 
65
- it 'authentication_failed' do
66
- @socket.remote_ip = '0.0.0.0'
67
- @app = Hara::Application.new @socket
68
- sleep 0.1 while @app.states.size == 0
69
- @app.states.should == [:fail]
70
- end
71
-
72
- it 'authentication_succeeded' do
73
- @app = Hara::Application.new @socket
74
- sleep 0.1 while @app.states.size == 0
75
- @app.states.should == [:success]
76
- end
77
-
78
48
  it 'remote call actions' do
79
- @socket.client_send({action: :hello, args: [' world']}.to_json)
80
- @app = Hara::Application.new @socket
49
+ @app.process_msg(Hara.encode_msg(:hello, ' world'))
81
50
  sleep 0.1 until msg = @socket.client_read
82
51
  msg.should == 'hello world'
83
52
  end
84
53
 
85
54
  it 'error remote call' do
86
- @socket.client_send("a error call")
87
- @app = Hara::Application.new @socket
55
+ @app.process_msg('a error call')
88
56
  sleep 0.1 while @app.alive?
89
57
  msg = @socket.client_read
90
58
  msg.should == nil
91
59
  end
92
60
 
93
61
  it 'action_missing should work' do
94
- @socket.client_send({action: :hello_world, args: ['hello', 'world']}.to_json)
95
- @app = Hara::Application.new @socket
62
+ @app.process_msg(Hara.encode_msg(:hello_world, 'hello', 'world'))
96
63
  sleep 0.1 until msg = @socket.client_read
97
64
  msg.should == [:action_missing, 'hello_world', ['hello', 'world']]
98
65
  end
99
66
 
100
67
  it 'callbacks should work' do
101
- @app = Hara::Application.new @socket
102
- @socket.client_send({action: :hello, args: [' world']}.to_json)
68
+ 2.times{ @app.process_msg(Hara.encode_msg(:hello, ' world'))}
103
69
  states = @app.states
104
70
  sleep 0.2
105
- @app.halt
71
+ @app.terminate
106
72
  sleep 0.1 while @app.alive?
107
- states.should == [:success, [:before_action, "hello", [" world"]], [:after_action, "hello", [" world"]], :after_process,:closed]
73
+ action_callbacks = [[:before_action, "hello", [" world"]], [:after_action, "hello", [" world"]]]
74
+ states.should == [*(action_callbacks * 2),:closed]
108
75
  end
109
76
  end
data/spec/hara_spec.rb CHANGED
@@ -10,13 +10,11 @@ describe Hara do
10
10
  Hara.env.should == :development
11
11
  end
12
12
 
13
- it 'Hara.request_handler should work' do
14
- handler = Hara.request_handler
15
- handler.class.should == Proc
16
- Hara.request_handler do |conn, req|
17
- conn + req
18
- end
19
- Hara.request_handler.call(40, 2).should == 42
20
- Hara.request_handler &handler
13
+ it 'Hara.encode_msg & decode_msg should work' do
14
+ action = 'hello_world'
15
+ args = ['hello', 'world']
16
+ action_d, args_d = Hara.decode_msg Hara.encode_msg(action, *args)
17
+ action.should == action_d
18
+ args.should == args_d
21
19
  end
22
20
  end
data/spec/spec_helper.rb CHANGED
@@ -1,9 +1,13 @@
1
1
  require 'hara/base'
2
2
 
3
- require 'json'
4
-
5
3
  Celluloid.logger = nil
6
4
 
5
+ class FayeHandshake
6
+ def headers_downcased
7
+ {'host' => 'localhost:8080'}
8
+ end
9
+ end
10
+
7
11
  class FayeSocket
8
12
  attr_accessor :remote_ip
9
13
 
@@ -20,19 +24,11 @@ class FayeSocket
20
24
  @alive = false
21
25
  end
22
26
 
23
- def read
24
- @server_messages.shift
25
- end
26
-
27
- def << message
27
+ def send message
28
28
  @client_messages << message
29
29
  end
30
30
 
31
31
  def client_read
32
32
  @client_messages.shift
33
33
  end
34
-
35
- def client_send message
36
- @server_messages << message
37
- end
38
34
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hara
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,16 +9,16 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-06-18 00:00:00.000000000 Z
12
+ date: 2013-07-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: reel
15
+ name: em-websocket
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: '0.3'
21
+ version: 0.5.0
22
22
  type: :runtime
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,23 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: '0.3'
29
+ version: 0.5.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: celluloid
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ~>
36
+ - !ruby/object:Gem::Version
37
+ version: 0.14.1
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ version: 0.14.1
30
46
  - !ruby/object:Gem::Dependency
31
47
  name: bundler
32
48
  requirement: !ruby/object:Gem::Requirement
@@ -59,8 +75,7 @@ dependencies:
59
75
  - - ! '>='
60
76
  - !ruby/object:Gem::Version
61
77
  version: '0'
62
- description: Hara is a simple framework, help you build [web ]socket server. Power
63
- by reel and celluloid.
78
+ description: Hara help you build async & concurrent websocket server.
64
79
  email:
65
80
  - jjyruby@gmail.com
66
81
  executables: []
@@ -107,7 +122,8 @@ rubyforge_project:
107
122
  rubygems_version: 1.8.23
108
123
  signing_key:
109
124
  specification_version: 3
110
- summary: Hara can help your build [web ]socket server easier.
125
+ summary: In fact, hara just a combination of em-websocket and celluloid, but it really
126
+ help you write applications easily.
111
127
  test_files:
112
128
  - spec/app_spec.rb
113
129
  - spec/hara_spec.rb