hara 0.3.0 → 0.4.0
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.
- checksums.yaml +4 -4
- data/Gemfile +1 -0
- data/README.md +63 -37
- data/client/hara.js +78 -0
- data/lib/hara.rb +3 -4
- data/lib/hara/app.rb +27 -10
- data/lib/hara/client_interaction.rb +9 -1
- data/lib/hara/version.rb +1 -1
- data/spec/app_spec.rb +8 -10
- data/spec/filter_spec.rb +2 -2
- data/spec/hara_spec.rb +2 -5
- data/spec/spec_helper.rb +10 -3
- metadata +4 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 5863328a79f220ece656a08e162f10675e758dd7
|
4
|
+
data.tar.gz: addd1f740c431d625188663a8640aebdcf6f9ea2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 13a36a9802a40a714c18e357cf70e43c5a806cdc6181e69a3688403e965def6f0d6a25510687fcbdb67f4025bc9831b7ff3ac48beeb8f0c441bffb2956281e50
|
7
|
+
data.tar.gz: 104134728df1f2a724fdad3b68f3a4757b54d4d40e45de4906deb18ba2586dda4b90a66e837a2f4dd3f5b17dc04d6856ddae0f3fbd5a3e60f2fd779969eea6d9
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,13 +1,13 @@
|
|
1
|
-
# Hara
|
1
|
+
# Hara(袚)
|
2
2
|
|
3
3
|
[](http://badge.fury.io/rb/hara)
|
4
4
|
[](https://travis-ci.org/jjyr/hara)
|
5
5
|
|
6
|
-
Hara is a websocket based application framework, build upon [em-websocket](https://github.com/igrigorik/em-websocket).
|
6
|
+
Hara(袚) is a websocket based application framework, build upon [em-websocket](https://github.com/igrigorik/em-websocket).
|
7
7
|
|
8
8
|
* Simple, easy to use.
|
9
|
-
*
|
10
|
-
* Event-
|
9
|
+
* Actor model(celluloid).
|
10
|
+
* Event-IO(em-websocket).
|
11
11
|
|
12
12
|
## Installation
|
13
13
|
|
@@ -23,18 +23,21 @@ Or install it yourself as:
|
|
23
23
|
|
24
24
|
$ gem install hara
|
25
25
|
|
26
|
+
Client:
|
27
|
+
|
28
|
+
copy it from ./client
|
29
|
+
|
26
30
|
## Basic Usage
|
27
31
|
|
28
32
|
*server*
|
29
33
|
```ruby
|
30
|
-
#test.rb
|
31
34
|
require 'hara'
|
32
35
|
|
33
|
-
class
|
36
|
+
class Test
|
34
37
|
include Hara::App
|
35
38
|
|
36
|
-
define_action :
|
37
|
-
|
39
|
+
define_action :reverse do |str|
|
40
|
+
response_msg str.reverse
|
38
41
|
end
|
39
42
|
end
|
40
43
|
|
@@ -43,28 +46,19 @@ Hara::Server.start 'localhost', '3000'
|
|
43
46
|
|
44
47
|
*client*
|
45
48
|
```javascript
|
46
|
-
var
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
//after a while
|
51
|
-
ws.send(msg)
|
52
|
-
//hello world
|
49
|
+
var client = new Hara();
|
50
|
+
client.connect('ws://localhost:3000');
|
51
|
+
client.send('reverse', ['hello world'], function(msg){alert(msg)});
|
53
52
|
```
|
54
53
|
|
55
|
-
*start server*
|
56
|
-
`ruby test.rb`
|
57
|
-
|
58
|
-
`ruby test.rb -h` to view options
|
59
|
-
|
60
54
|
## Full Usages
|
61
55
|
|
56
|
+
*server*
|
62
57
|
```ruby
|
63
58
|
require 'hara'
|
64
59
|
|
65
|
-
class
|
66
|
-
#include Hara::App make your
|
67
|
-
#hara use actor per connection
|
60
|
+
class Clock
|
61
|
+
#include Hara::App make your class become Celluloid::Actor
|
68
62
|
include Hara::App
|
69
63
|
|
70
64
|
#Hara::App provide some callbacks
|
@@ -72,22 +66,29 @@ class Echo
|
|
72
66
|
def after_connect
|
73
67
|
puts 'first called'
|
74
68
|
p headers
|
69
|
+
# push message to client
|
70
|
+
send_msg "connected"
|
75
71
|
end
|
76
72
|
|
77
73
|
def before_action action, *args
|
78
74
|
puts 'called when action comming'
|
79
75
|
end
|
80
76
|
|
81
|
-
define_action :
|
77
|
+
define_action :start do
|
82
78
|
puts "#{client_ip} #{client_port}"
|
83
|
-
|
84
|
-
|
79
|
+
|
80
|
+
# push time to client every 1 sec
|
81
|
+
@timer = every(1){ send_msg Time.now.to_s}
|
82
|
+
|
83
|
+
# different between send_msg & response_msg
|
84
|
+
# send_msg means push to client, trigger client onmessage callback
|
85
|
+
# response_msg respond client request, and trigger send callback(if it present)
|
86
|
+
response_msg 'started'
|
85
87
|
end
|
86
88
|
|
87
|
-
define_action :
|
88
|
-
|
89
|
-
|
90
|
-
close
|
89
|
+
define_action :stop do
|
90
|
+
@timer.cancel
|
91
|
+
response_msg 'stoped'
|
91
92
|
end
|
92
93
|
|
93
94
|
def after_action action, *args
|
@@ -95,7 +96,7 @@ class Echo
|
|
95
96
|
end
|
96
97
|
|
97
98
|
def action_missing action, *args
|
98
|
-
|
99
|
+
puts 'error'
|
99
100
|
super
|
100
101
|
end
|
101
102
|
|
@@ -109,8 +110,36 @@ server_options = {
|
|
109
110
|
#...some options, same as EM::Websocket.run
|
110
111
|
}
|
111
112
|
Hara::Server.start 'localhost', '3000', server_options
|
113
|
+
```
|
112
114
|
|
115
|
+
*client*
|
116
|
+
```javascript
|
117
|
+
var client = new Hara();
|
118
|
+
|
119
|
+
//handle pushed messages(send_msg)
|
120
|
+
client.onmessage = function(msg){
|
121
|
+
console.log("current time:" + msg);
|
122
|
+
}
|
123
|
+
|
124
|
+
//connect to server
|
125
|
+
client.connect('ws://localhost:3000');
|
126
|
+
|
127
|
+
//call server side action
|
128
|
+
client.send('start', [], function(msg){console.log(msg)});
|
129
|
+
//started
|
130
|
+
//current time:2013-08-05 10:48:04 +0800
|
131
|
+
//current time:2013-08-05 10:48:05 +0800
|
132
|
+
//current time:2013-08-05 10:48:06 +0800
|
133
|
+
//current time:2013-08-05 10:48:07 +0800
|
134
|
+
client.send('stop', [], function(msg){console.log(msg)});
|
135
|
+
//stoped
|
136
|
+
|
137
|
+
//close connection
|
138
|
+
client.close();
|
139
|
+
```
|
113
140
|
|
141
|
+
####use filter####
|
142
|
+
```ruby
|
114
143
|
# Hara::Filter
|
115
144
|
# Filter can help you filter some connections before dispatched to app actor.
|
116
145
|
# Example: use Filter to authentication
|
@@ -124,6 +153,9 @@ end
|
|
124
153
|
#class name is not matter
|
125
154
|
class Authentication
|
126
155
|
include Hara::Filter
|
156
|
+
|
157
|
+
#default value is 10
|
158
|
+
self.pool_size = 20
|
127
159
|
|
128
160
|
# You must implement filter method, return value should be ture or false
|
129
161
|
def filter
|
@@ -135,12 +167,6 @@ end
|
|
135
167
|
Hara::Server.start 'localhost', '3000'
|
136
168
|
```
|
137
169
|
|
138
|
-
## Client
|
139
|
-
|
140
|
-
js client is processing
|
141
|
-
|
142
|
-
current format is JSON.stringify({action: 'echo',args:['hello world']})
|
143
|
-
|
144
170
|
## Contributing
|
145
171
|
|
146
172
|
1. Fork it
|
data/client/hara.js
ADDED
@@ -0,0 +1,78 @@
|
|
1
|
+
function Hara() {
|
2
|
+
this.send_message_ids = {};
|
3
|
+
this.onopen = this.onclose = this.onerror = this.onmessage = function(){};
|
4
|
+
}
|
5
|
+
|
6
|
+
Hara.prototype.send = function(action, args, callback){
|
7
|
+
var id = Hara.uuid();
|
8
|
+
var message = Hara.encode({_id: id, action: action, args: args});
|
9
|
+
this.send_message_ids[id] = callback;
|
10
|
+
this.websocket.send(message);
|
11
|
+
}
|
12
|
+
|
13
|
+
Hara.prototype.connect = function(url, protocol){
|
14
|
+
this.websocket = new WebSocket(url, protocol);
|
15
|
+
this.setup_callbacks();
|
16
|
+
}
|
17
|
+
|
18
|
+
Hara.prototype.setup_callbacks = function(){
|
19
|
+
var that = this;
|
20
|
+
this.websocket.onopen = function(){
|
21
|
+
that.onopen.apply(that, arguments);
|
22
|
+
};
|
23
|
+
this.websocket.onclose = function(){
|
24
|
+
that.onclose.apply(that, arguments);
|
25
|
+
};
|
26
|
+
this.websocket.onerror = function(){
|
27
|
+
that.onerror.apply(that, arguments);
|
28
|
+
};
|
29
|
+
this.websocket.onmessage = function(msg){
|
30
|
+
var message = Hara.decode(msg.data);
|
31
|
+
switch(message.type){
|
32
|
+
case 'response':
|
33
|
+
that.handle_response(message);
|
34
|
+
break;
|
35
|
+
case 'push':
|
36
|
+
that.handle_push(message);
|
37
|
+
break;
|
38
|
+
default:
|
39
|
+
throw 'Unknown message type'
|
40
|
+
}
|
41
|
+
};
|
42
|
+
};
|
43
|
+
|
44
|
+
Hara.prototype.handle_response = function(message) {
|
45
|
+
var id = message._id;
|
46
|
+
var args = message.args;
|
47
|
+
var callback = this.send_message_ids[id];
|
48
|
+
if(callback) {
|
49
|
+
callback.apply(this, [args]);
|
50
|
+
}
|
51
|
+
delete this.send_message_ids[id];
|
52
|
+
};
|
53
|
+
|
54
|
+
Hara.prototype.handle_push = function(message) {
|
55
|
+
var args = message.args;
|
56
|
+
this.onmessage(args);
|
57
|
+
};
|
58
|
+
|
59
|
+
Hara.prototype.close = function(){
|
60
|
+
this.websocket.close.apply(this.websocket, arguments);
|
61
|
+
this.send_message_ids = {};
|
62
|
+
}
|
63
|
+
|
64
|
+
Hara.uuid = function() {
|
65
|
+
var uuid = ('xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
66
|
+
var r = Math.random()*16|0, v = c == 'x' ? r : (r&0x3|0x8);
|
67
|
+
return v.toString(16);
|
68
|
+
}));
|
69
|
+
return uuid;
|
70
|
+
}
|
71
|
+
|
72
|
+
Hara.encode = function(args) {
|
73
|
+
return JSON.stringify(args);
|
74
|
+
}
|
75
|
+
|
76
|
+
Hara.decode = function(args){
|
77
|
+
return JSON.parse(args);
|
78
|
+
}
|
data/lib/hara.rb
CHANGED
@@ -4,12 +4,11 @@ module Hara
|
|
4
4
|
class << self
|
5
5
|
#decode message, return action and args
|
6
6
|
def decode_msg msg
|
7
|
-
|
8
|
-
msg.values_at 'action', 'args'
|
7
|
+
JSON.parse msg
|
9
8
|
end
|
10
9
|
|
11
|
-
def encode_msg
|
12
|
-
|
10
|
+
def encode_msg msg
|
11
|
+
msg.to_json
|
13
12
|
end
|
14
13
|
|
15
14
|
def filter_class
|
data/lib/hara/app.rb
CHANGED
@@ -46,13 +46,13 @@ module Hara
|
|
46
46
|
|
47
47
|
##################
|
48
48
|
|
49
|
-
#like method_missing
|
49
|
+
# like method_missing
|
50
50
|
def action_missing action, args
|
51
51
|
info "#{client_ip} request action: #{action} args: #{args.inspect}, action not defined"
|
52
52
|
raise NoMethodError, "undefined action '#{action}' for #{self}:#{self.class}"
|
53
53
|
end
|
54
54
|
|
55
|
-
#below are internal functions(should not been overriding)
|
55
|
+
# below are internal functions(should not been overriding)
|
56
56
|
|
57
57
|
def initialize handshake, socket
|
58
58
|
socket_setup handshake, socket
|
@@ -65,14 +65,31 @@ module Hara
|
|
65
65
|
end
|
66
66
|
|
67
67
|
def process_msg message
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
68
|
+
exclusive do
|
69
|
+
begin
|
70
|
+
id, action, args = Hara.decode_msg(message).values_at('_id', 'action', 'args')
|
71
|
+
info "#{client_ip} request action: #{action} args: #{args.inspect}"
|
72
|
+
before_action action, *args
|
73
|
+
call_action action, *args
|
74
|
+
after_action action, *args
|
75
|
+
send_response_msg id
|
76
|
+
rescue StandardError => e
|
77
|
+
info "#{client_ip} processing error:\n#{e.inspect}"
|
78
|
+
terminate
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# respond to client
|
84
|
+
def response_msg msg
|
85
|
+
raise DuplicateResponseError if @_response_msg
|
86
|
+
@_response_msg = msg
|
87
|
+
end
|
88
|
+
|
89
|
+
def send_response_msg message_id
|
90
|
+
msg, @_response_msg = @_response_msg, nil
|
91
|
+
message = Hara.encode_msg(_id: message_id, type: :response, args: msg)
|
92
|
+
socket.send message
|
76
93
|
end
|
77
94
|
|
78
95
|
def call_action action, *args
|
@@ -1,4 +1,7 @@
|
|
1
1
|
module Hara
|
2
|
+
class DuplicateResponseError < StandardError
|
3
|
+
end
|
4
|
+
|
2
5
|
module ClientInteraction
|
3
6
|
attr_reader :socket, :handshake, :client_ip, :client_port
|
4
7
|
|
@@ -15,7 +18,12 @@ module Hara
|
|
15
18
|
|
16
19
|
# send msg to client
|
17
20
|
def send_msg msg
|
18
|
-
|
21
|
+
message = Hara.encode_msg(type: :push, args: msg)
|
22
|
+
socket.send message
|
23
|
+
end
|
24
|
+
|
25
|
+
def response_msg msg
|
26
|
+
raise NotImplementedError
|
19
27
|
end
|
20
28
|
|
21
29
|
# close connection
|
data/lib/hara/version.rb
CHANGED
data/spec/app_spec.rb
CHANGED
@@ -40,8 +40,8 @@ describe Hara::App do
|
|
40
40
|
end
|
41
41
|
|
42
42
|
before :each do
|
43
|
-
@handshake =
|
44
|
-
@socket =
|
43
|
+
@handshake = FakeHandshake.new
|
44
|
+
@socket = FakeSocket.new
|
45
45
|
@app = Hara::Application.new @handshake, @socket
|
46
46
|
@socket.app = @app
|
47
47
|
end
|
@@ -59,35 +59,33 @@ describe Hara::App do
|
|
59
59
|
end
|
60
60
|
|
61
61
|
it 'remote call actions' do
|
62
|
-
@
|
62
|
+
@socket.client_send(:hello, [' world'])
|
63
63
|
msg = nil
|
64
64
|
wait_until{msg = @socket.client_read}
|
65
65
|
msg.should == 'hello world'
|
66
66
|
end
|
67
67
|
|
68
68
|
it 'close should close connection' do
|
69
|
-
@
|
69
|
+
@socket.client_send(:exit, [])
|
70
70
|
wait_until{!@app.alive?}
|
71
71
|
@socket.alive?.should == false
|
72
72
|
@socket.close_info.should == [3333, 'Bye']
|
73
73
|
end
|
74
74
|
|
75
75
|
it 'error remote call' do
|
76
|
-
@
|
76
|
+
@socket.client_send(:hello, [])
|
77
77
|
wait_until{!@app.alive?}
|
78
|
-
msg = @socket.client_read
|
79
|
-
msg.should == nil
|
80
78
|
end
|
81
79
|
|
82
80
|
it 'action_missing should work' do
|
83
|
-
@
|
81
|
+
@socket.client_send(:hello_world, ['hello', 'world'])
|
84
82
|
msg = nil
|
85
83
|
wait_until{msg = @socket.client_read}
|
86
|
-
msg.should == [
|
84
|
+
msg.should == ['action_missing', 'hello_world', ['hello', 'world']]
|
87
85
|
end
|
88
86
|
|
89
87
|
it 'callbacks should work' do
|
90
|
-
2.times{ @
|
88
|
+
2.times{ @socket.client_send(:hello, [' world'])}
|
91
89
|
states = @app.states
|
92
90
|
sleep 0.2
|
93
91
|
@app.terminate
|
data/spec/filter_spec.rb
CHANGED
@@ -27,8 +27,8 @@ describe Hara::Filter do
|
|
27
27
|
|
28
28
|
describe 'filter' do
|
29
29
|
before :each do
|
30
|
-
@handshake =
|
31
|
-
@socket =
|
30
|
+
@handshake = FakeHandshake.new
|
31
|
+
@socket = FakeSocket.new
|
32
32
|
@socket.alive = true
|
33
33
|
wait_until{Hara.filter_pool}
|
34
34
|
end
|
data/spec/hara_spec.rb
CHANGED
@@ -9,10 +9,7 @@ describe Hara do
|
|
9
9
|
end
|
10
10
|
|
11
11
|
it 'Hara.encode_msg & decode_msg should work' do
|
12
|
-
|
13
|
-
|
14
|
-
action_d, args_d = Hara.decode_msg Hara.encode_msg(action, *args)
|
15
|
-
action.should == action_d
|
16
|
-
args.should == args_d
|
12
|
+
msg = {'action' => 'hello_world', 'args' => ['hello', 'world']}
|
13
|
+
Hara.decode_msg(Hara.encode_msg msg).should == msg
|
17
14
|
end
|
18
15
|
end
|
data/spec/spec_helper.rb
CHANGED
@@ -8,13 +8,13 @@ def wait_until wait_time = 3
|
|
8
8
|
end
|
9
9
|
end
|
10
10
|
|
11
|
-
class
|
11
|
+
class FakeHandshake
|
12
12
|
def headers_downcased
|
13
13
|
{'host' => 'localhost:8080'}
|
14
14
|
end
|
15
15
|
end
|
16
16
|
|
17
|
-
class
|
17
|
+
class FakeSocket
|
18
18
|
attr_accessor :remote_ip, :close_info, :app
|
19
19
|
|
20
20
|
def initialize
|
@@ -22,6 +22,7 @@ class FayeSocket
|
|
22
22
|
@server_messages = []
|
23
23
|
@mri_peername = "\x02\x00\x00P\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
|
24
24
|
@jruby_peername = "\x00\x02\x8Av\x7F\x00\x00\x01\x00\x00\x00\x00\x00\x00\x00\x00"
|
25
|
+
@send_message_ids = {}
|
25
26
|
end
|
26
27
|
|
27
28
|
def app= app
|
@@ -49,12 +50,18 @@ class FayeSocket
|
|
49
50
|
end
|
50
51
|
end
|
51
52
|
|
53
|
+
def client_send action, args
|
54
|
+
id = SecureRandom.uuid
|
55
|
+
@server_messages << Hara.encode_msg(id: id, action: action, args: args)
|
56
|
+
@app.process_msg @server_messages.shift
|
57
|
+
end
|
58
|
+
|
52
59
|
def send message
|
53
60
|
@client_messages << message
|
54
61
|
end
|
55
62
|
|
56
63
|
def client_read
|
57
|
-
@client_messages.shift
|
64
|
+
Hara.decode_msg(@client_messages.shift)['args']
|
58
65
|
end
|
59
66
|
|
60
67
|
[:onclose, :onmessage, :onopen].each do |method|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hara
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- jjy
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2013-
|
11
|
+
date: 2013-08-13 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: em-websocket
|
@@ -93,6 +93,7 @@ files:
|
|
93
93
|
- LICENSE.txt
|
94
94
|
- README.md
|
95
95
|
- Rakefile
|
96
|
+
- client/hara.js
|
96
97
|
- hara.gemspec
|
97
98
|
- lib/hara.rb
|
98
99
|
- lib/hara/app.rb
|
@@ -125,7 +126,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
125
126
|
version: '0'
|
126
127
|
requirements: []
|
127
128
|
rubyforge_project:
|
128
|
-
rubygems_version: 2.0.
|
129
|
+
rubygems_version: 2.0.6
|
129
130
|
signing_key:
|
130
131
|
specification_version: 4
|
131
132
|
summary: Hara build upon em-websocket, easy to use.
|