em-ucengine 0.1.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +75 -27
- data/lib/em-ucengine.rb +3 -188
- data/lib/em-ucengine/brick.rb +177 -0
- data/lib/em-ucengine/brick_test.rb +27 -0
- data/lib/em-ucengine/client.rb +268 -0
- data/lib/em-ucengine/client_em.rb +239 -0
- data/lib/em-ucengine/client_nethttp.rb +97 -0
- data/lib/em-ucengine/errors.rb +21 -0
- data/lib/em-ucengine/utils.rb +46 -0
- metadata +132 -90
data/README.md
CHANGED
@@ -1,13 +1,10 @@
|
|
1
|
-
EventMachine library for U.C.Engine
|
2
|
-
===================================
|
1
|
+
# EventMachine library for U.C.Engine
|
3
2
|
|
4
3
|
em-ucengine is a Ruby library for [U.C.Engine](http://ucengine.org/) powered
|
5
4
|
by [EventMachine](https://github.com/eventmachine/eventmachine). It can
|
6
5
|
connect, subscribe and publish events to U.C.Engine.
|
7
6
|
|
8
|
-
|
9
|
-
How to use it
|
10
|
-
-------------
|
7
|
+
## Install
|
11
8
|
|
12
9
|
Install with Rubygems:
|
13
10
|
|
@@ -15,43 +12,94 @@ Install with Rubygems:
|
|
15
12
|
|
16
13
|
If you use bundler, add it to your `Gemfile`:
|
17
14
|
|
18
|
-
gem "em-ucengine", "~>0.
|
15
|
+
gem "em-ucengine", "~> 0.3"
|
16
|
+
|
17
|
+
## Usage
|
18
|
+
|
19
|
+
### Client
|
19
20
|
|
20
|
-
|
21
|
+
#### EventMachine
|
21
22
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
23
|
+
We have a classic block style API:
|
24
|
+
|
25
|
+
```ruby
|
26
|
+
require "em-ucengine"
|
27
|
+
EventMachine::UCEngine::Client.run do |uce|
|
28
|
+
uce.connect("participant", "pwd") do |err, session|
|
29
|
+
EM.add_periodic_timer(1) { session.publish("em-ucengine.example.ping", "demo") }
|
30
|
+
session.subscribe("demo") do |err, event|
|
31
|
+
puts "Hey, we received an event: #{event.inspect}"
|
30
32
|
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
```
|
31
36
|
|
32
|
-
|
37
|
+
Each method call return a deferable.
|
33
38
|
|
39
|
+
```ruby
|
40
|
+
require "em-ucengine"
|
41
|
+
EventMachine::UCEngine::Client.run do |uce|
|
42
|
+
req = uce.connect("participant", "pwd")
|
43
|
+
req.callback do |session|
|
44
|
+
session.publish("em-ucengine.example.ping", "demo")
|
45
|
+
end
|
46
|
+
req.errback do |error|
|
47
|
+
puts "error"
|
48
|
+
end
|
49
|
+
end
|
50
|
+
```
|
34
51
|
|
35
|
-
|
36
|
-
----
|
52
|
+
#### Net/HTTP
|
37
53
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
54
|
+
```ruby
|
55
|
+
require "em-ucengine"
|
56
|
+
|
57
|
+
uce = UCEngine::Client.new
|
58
|
+
session = uce.connect("participant", "pwd")
|
59
|
+
session.publish("em-ucengine.example.ping", "demo")
|
60
|
+
```
|
61
|
+
|
62
|
+
### Brick
|
63
|
+
|
64
|
+
```ruby
|
65
|
+
require "em-ucengine"
|
66
|
+
require "em-ucengine/brick"
|
67
|
+
|
68
|
+
class MyBrick
|
69
|
+
include EM::UCEngine::Brick
|
44
70
|
|
71
|
+
on "ping" do |event|
|
72
|
+
puts event
|
73
|
+
end
|
74
|
+
end
|
45
75
|
|
46
|
-
|
47
|
-
|
76
|
+
brick = MyBrick.run
|
77
|
+
```
|
78
|
+
|
79
|
+
Don't hesitate to look at the specs for more examples ;-)
|
80
|
+
|
81
|
+
## TODO
|
82
|
+
|
83
|
+
* Release the gem with another name
|
84
|
+
* Complete the specs
|
85
|
+
* Implements the download and upload methods for the Net::HTTP backend
|
86
|
+
|
87
|
+
## Issues or Suggestions
|
48
88
|
|
49
89
|
Found an issue or have a suggestion? Please report it on
|
50
90
|
[Github's issue tracker](http://github.com/af83/ucengine.em/issues).
|
51
91
|
|
92
|
+
First you must have an ucengine instance goto the source directory and start:
|
93
|
+
|
94
|
+
make run
|
95
|
+
|
96
|
+
Once the console started successfully, in an other shell
|
97
|
+
|
98
|
+
./rel/ucengine/bin/demo.sh localhost
|
99
|
+
|
52
100
|
If you wants to make a pull request, please check the specs before:
|
53
101
|
|
54
|
-
|
102
|
+
rake test
|
55
103
|
|
56
104
|
|
57
105
|
Copyright (c) 2011 af83, released under the LGPL license
|
data/lib/em-ucengine.rb
CHANGED
@@ -1,188 +1,3 @@
|
|
1
|
-
require "
|
2
|
-
require "em-
|
3
|
-
require "
|
4
|
-
|
5
|
-
|
6
|
-
module EventMachine
|
7
|
-
class UCEngine
|
8
|
-
|
9
|
-
def self.run(*args)
|
10
|
-
EM.run { yield self.new(*args) }
|
11
|
-
end
|
12
|
-
|
13
|
-
def initialize(host="localhost", port=5280, api_root="/api", api_version="0.4")
|
14
|
-
@host = host
|
15
|
-
@port = port
|
16
|
-
@root = api_root
|
17
|
-
@version = api_version
|
18
|
-
end
|
19
|
-
|
20
|
-
def time
|
21
|
-
Session.new(self, nil, nil).time { |time| yield time }
|
22
|
-
end
|
23
|
-
|
24
|
-
def connect(user, password, metadata=nil)
|
25
|
-
body = { "name" => user, "credential" => password }
|
26
|
-
body["metadata"] = metadata if metadata
|
27
|
-
http = EM::HttpRequest.new(url "presence").post :body => body
|
28
|
-
http.errback { yield nil }
|
29
|
-
http.callback do
|
30
|
-
data = Yajl::Parser.parse(http.response)
|
31
|
-
data = data["result"] if data
|
32
|
-
yield Session.new(self, data["uid"], data["sid"])
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
def url(path)
|
37
|
-
"http://#{@host}:#{@port}#{@root}/#{@version}/#{path}"
|
38
|
-
end
|
39
|
-
|
40
|
-
class Session < Struct.new(:uce, :uid, :sid)
|
41
|
-
|
42
|
-
### Time - http://docs.ucengine.org/api.html#time ###
|
43
|
-
|
44
|
-
def time
|
45
|
-
get("/time") { |result| yield result if block_given? }
|
46
|
-
end
|
47
|
-
|
48
|
-
### Presence - http://docs.ucengine.org/api.html#authentication ###
|
49
|
-
|
50
|
-
def presence(sid)
|
51
|
-
get("/presence/#{sid}") { |result| yield result if block_given? }
|
52
|
-
end
|
53
|
-
|
54
|
-
def disconnect
|
55
|
-
delete("/presence/#{sid}") { |result| yield result if block_given? }
|
56
|
-
end
|
57
|
-
|
58
|
-
### Users - http://docs.ucengine.org/api.html#user ###
|
59
|
-
|
60
|
-
def users
|
61
|
-
get("/user") { |result| yield result if block_given? }
|
62
|
-
end
|
63
|
-
|
64
|
-
def user(uid)
|
65
|
-
get("/user/#{uid}") { |result| yield result if block_given? }
|
66
|
-
end
|
67
|
-
|
68
|
-
def create_user(data)
|
69
|
-
post("/user", data) { |result| yield result && result.to_i if block_given? }
|
70
|
-
end
|
71
|
-
|
72
|
-
def update_user(uid, data)
|
73
|
-
put("/user/#{uid}", data) { |result| yield result if block_given? }
|
74
|
-
end
|
75
|
-
|
76
|
-
def delete_user(uid)
|
77
|
-
delete("/user/#{uid}") { |result| yield result if block_given? }
|
78
|
-
end
|
79
|
-
|
80
|
-
### General infos - http://docs.ucengine.org/api.html#infos ###
|
81
|
-
|
82
|
-
def infos
|
83
|
-
get("/infos") { |result| yield result if block_given? }
|
84
|
-
end
|
85
|
-
|
86
|
-
def update_infos(metadata)
|
87
|
-
put("/infos", :metadata => metadata) { |result| yield result if block_given? }
|
88
|
-
end
|
89
|
-
|
90
|
-
### Meetings - http://docs.ucengine.org/api.html#meeting ###
|
91
|
-
|
92
|
-
def meetings(status=nil)
|
93
|
-
get("/meeting/#{status}") { |result| yield result if block_given? }
|
94
|
-
end
|
95
|
-
|
96
|
-
def meeting(meeting)
|
97
|
-
get("/meeting/all/#{meeting}") { |result| yield result if block_given? }
|
98
|
-
end
|
99
|
-
|
100
|
-
def create_meeting(meeting, body={})
|
101
|
-
body.merge!(:meeting => meeting)
|
102
|
-
post("/meeting/all", body) { |result| yield result if block_given? }
|
103
|
-
end
|
104
|
-
|
105
|
-
def update_meeting(meeting, body={})
|
106
|
-
put("/meeting/all/#{meeting}", body) { |result| yield result if block_given? }
|
107
|
-
end
|
108
|
-
|
109
|
-
def delete_meeting(meeting)
|
110
|
-
delete("/meeting/all/#{meeting}") { |result| yield result if block_given? }
|
111
|
-
end
|
112
|
-
|
113
|
-
### Rosters - http://docs.ucengine.org/api.html#join-a-meeting ###
|
114
|
-
|
115
|
-
def roster(meeting)
|
116
|
-
get("/meeting/all/#{meeting}/roster") { |result| yield result if block_given? }
|
117
|
-
end
|
118
|
-
|
119
|
-
def join_roster(meeting)
|
120
|
-
post("/meeting/all/#{meeting}/roster") { |result| yield result if block_given? }
|
121
|
-
end
|
122
|
-
|
123
|
-
def quit_roster(meeting, uid=nil)
|
124
|
-
delete("/meeting/all/#{meeting}/roster/#{uid || @uid}") { |result| yield result if block_given? }
|
125
|
-
end
|
126
|
-
|
127
|
-
### Events - http://docs.ucengine.org/api.html#events ###
|
128
|
-
|
129
|
-
def events(meeting, params={})
|
130
|
-
params[:_async] = "no"
|
131
|
-
get("/event/#{meeting}", params) { |result| yield result if block_given? }
|
132
|
-
end
|
133
|
-
|
134
|
-
def subscribe(meeting, params={}, &blk)
|
135
|
-
params[:_async] = "lp"
|
136
|
-
recurse = Proc.new do |result|
|
137
|
-
next unless result
|
138
|
-
blk.call(result)
|
139
|
-
params[:start] = result.last["datetime"].to_i + 1
|
140
|
-
get("/event/#{meeting}", params, &recurse) if EM.reactor_running?
|
141
|
-
end
|
142
|
-
time do |time|
|
143
|
-
params[:start] = time
|
144
|
-
get("/event/#{meeting}", params, &recurse)
|
145
|
-
end
|
146
|
-
end
|
147
|
-
|
148
|
-
def publish(type, meeting=nil, metadata=nil)
|
149
|
-
body = { :type => type }
|
150
|
-
body[:metadata] = metadata if metadata
|
151
|
-
post("/event/#{meeting}", body) { |result| yield result if block_given? }
|
152
|
-
end
|
153
|
-
|
154
|
-
def search(params)
|
155
|
-
get("/search/event/", params) { |result| yield result if block_given? }
|
156
|
-
end
|
157
|
-
|
158
|
-
protected
|
159
|
-
|
160
|
-
def get(path, params={})
|
161
|
-
http_request(:get, path, :query => params) { |result| yield result }
|
162
|
-
end
|
163
|
-
|
164
|
-
def post(path, body=nil)
|
165
|
-
http_request(:post, path, :body => body) { |result| yield result }
|
166
|
-
end
|
167
|
-
|
168
|
-
def put(path, body=nil)
|
169
|
-
http_request(:put, path, :body => body) { |result| yield result }
|
170
|
-
end
|
171
|
-
|
172
|
-
def delete(path)
|
173
|
-
http_request(:delete, path) { |result| yield result }
|
174
|
-
end
|
175
|
-
|
176
|
-
def http_request(method, path, opts={})
|
177
|
-
opts[:query] ||= {}
|
178
|
-
opts[:query].merge!(:uid => uid, :sid => sid)
|
179
|
-
http = EM::HttpRequest.new(uce.url path).send(method, opts)
|
180
|
-
http.errback { yield nil }
|
181
|
-
http.callback do
|
182
|
-
data = Yajl::Parser.parse(http.response)
|
183
|
-
yield data && data.has_key?("result") && data["result"]
|
184
|
-
end
|
185
|
-
end
|
186
|
-
end
|
187
|
-
end
|
188
|
-
end
|
1
|
+
require "em-ucengine/client"
|
2
|
+
require "em-ucengine/client_nethttp"
|
3
|
+
require "em-ucengine/client_em"
|
@@ -0,0 +1,177 @@
|
|
1
|
+
require 'em-ucengine/utils'
|
2
|
+
|
3
|
+
module EventMachine
|
4
|
+
module UCEngine
|
5
|
+
# This module allows to create a new brick for UCE. A brick may be either
|
6
|
+
# an independent entity or a composition of several bricks, resulting in a
|
7
|
+
# "meta-brick". On this case, only the meta-brick should be runned and only
|
8
|
+
# one connection to UCE will be used.
|
9
|
+
#
|
10
|
+
# When creating a brick, one would provide a bootstrap block and declare at
|
11
|
+
# least one event handler for the brick to manage. Handlers will be executed
|
12
|
+
# in the context of the Brick instance, so one may also write arbitrary code
|
13
|
+
# to be used in handlers blocks.
|
14
|
+
#
|
15
|
+
# See examples/bricks.rb and specs for more details.
|
16
|
+
module Brick
|
17
|
+
def self.included(klass)
|
18
|
+
klass.extend(ClassMethods)
|
19
|
+
end
|
20
|
+
|
21
|
+
# Methods defined here will be made available as class methods on the
|
22
|
+
# class including Brick. Some class variables are pushed as well:
|
23
|
+
# +@@routes+, +@@bricks+, +@@bootstrap+.
|
24
|
+
module ClassMethods
|
25
|
+
|
26
|
+
def self.extended(klass)
|
27
|
+
klass.class_eval {
|
28
|
+
class_variable_set("@@routes", {})
|
29
|
+
class_variable_set("@@bricks", [])
|
30
|
+
class_variable_set("@@bootstrap", Proc.new {})
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
# Define a bootstrap block, which will be called before subscribing
|
35
|
+
# to events.
|
36
|
+
def bootstrap(&block)
|
37
|
+
class_variable_set("@@bootstrap", block)
|
38
|
+
end
|
39
|
+
|
40
|
+
# Define an event handler.
|
41
|
+
#
|
42
|
+
# @param [String] route
|
43
|
+
# @yield [Event] a callback to be executed when a matching event is received
|
44
|
+
# @example
|
45
|
+
# on "my.event" do |event|
|
46
|
+
# # do something
|
47
|
+
# end
|
48
|
+
#
|
49
|
+
def on(route, &callback)
|
50
|
+
routes = class_variable_get("@@routes")
|
51
|
+
routes[route] ||= []
|
52
|
+
routes[route] << callback
|
53
|
+
end
|
54
|
+
|
55
|
+
# Add a sub-brick to the current brick.
|
56
|
+
#
|
57
|
+
# @param [Constant] Brick's class/module name
|
58
|
+
# @example
|
59
|
+
# use MySpecializedBrick
|
60
|
+
def use(name)
|
61
|
+
class_variable_get("@@bricks") << name
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
attr_reader :uce
|
66
|
+
|
67
|
+
# Create a new brick.
|
68
|
+
#
|
69
|
+
# @param [Hash] config
|
70
|
+
def initialize(config={})
|
71
|
+
@uce = nil
|
72
|
+
@config = config
|
73
|
+
end
|
74
|
+
|
75
|
+
# Start EventMachine, initialize and start the brick.
|
76
|
+
#
|
77
|
+
# When composing several bricks, only the meta-brick should be
|
78
|
+
# runned.
|
79
|
+
#
|
80
|
+
# @param [Hash] config
|
81
|
+
def self.run(config={})
|
82
|
+
brick = self.new config
|
83
|
+
EM::run do
|
84
|
+
brick.start
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Start the brick.
|
89
|
+
#
|
90
|
+
# @param [EM::UCEngine::Client] uce (nil) shared instance of the client,
|
91
|
+
# used by sub-bricks when composing so that only one UCE connection is
|
92
|
+
# made and used
|
93
|
+
def start(uce=nil)
|
94
|
+
# Not in a sub-brick, connect to UCE.
|
95
|
+
if uce.nil?
|
96
|
+
@uce = EM::UCEngine::Client.new(@config[:host], @config[:port])
|
97
|
+
@uce.connect(@config[:name], @config[:credential]) do |err, uce|
|
98
|
+
@uce = uce
|
99
|
+
ready(bricks.map do |brick|
|
100
|
+
b = brick.new
|
101
|
+
b.start @uce
|
102
|
+
b
|
103
|
+
end)
|
104
|
+
end
|
105
|
+
else # Sub-brick, init with the shared UCE client instance.
|
106
|
+
@uce = uce
|
107
|
+
self.instance_eval &bootstrap
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
# Accessor for +@@bootstrap+.
|
112
|
+
def bootstrap
|
113
|
+
self.class.class_variable_get("@@bootstrap")
|
114
|
+
end
|
115
|
+
|
116
|
+
# Accessor for +@@routes+.
|
117
|
+
def routes
|
118
|
+
self.class.class_variable_get("@@routes")
|
119
|
+
end
|
120
|
+
|
121
|
+
# Accessor for +@@bricks+.
|
122
|
+
def bricks
|
123
|
+
self.class.class_variable_get("@@bricks")
|
124
|
+
end
|
125
|
+
|
126
|
+
# Wrapper around EventMachine's +Timer+. Pass it a block to be
|
127
|
+
# executed after a time range.
|
128
|
+
#
|
129
|
+
# @param [Integer] n time range in milliseconds
|
130
|
+
# @yield
|
131
|
+
def after(n, &block)
|
132
|
+
EventMachine::Timer.new(n, &block)
|
133
|
+
end
|
134
|
+
|
135
|
+
# Wrapper around EventMachine's +PeriodicTimer+. Pass it a block
|
136
|
+
# to be executed on a defined time frame.
|
137
|
+
#
|
138
|
+
# @param [Integer] n time range in milliseconds
|
139
|
+
# @yield
|
140
|
+
def every(n, &block)
|
141
|
+
EventMachine::PeriodicTimer.new(n, &block)
|
142
|
+
end
|
143
|
+
|
144
|
+
protected
|
145
|
+
|
146
|
+
# Hook sub-brick's declared event handlers into the event loop, by subscribing to
|
147
|
+
# UCE core.
|
148
|
+
#
|
149
|
+
# @param [Array<Brick>] brick_instances
|
150
|
+
def ready(bricks_instances)
|
151
|
+
r = routes.keys
|
152
|
+
|
153
|
+
# Bootstrap.
|
154
|
+
self.instance_eval &bootstrap
|
155
|
+
|
156
|
+
# Merge routes when composing bricks.
|
157
|
+
bricks_instances.each do |brick|
|
158
|
+
r += brick.routes.keys
|
159
|
+
end
|
160
|
+
|
161
|
+
# Subscribe to UCE for declared event handlers.
|
162
|
+
@uce.subscribe "", :type => r.uniq.join(',') do |err, events|
|
163
|
+
events.each do |event|
|
164
|
+
routes[event["type"]].each do |callback|
|
165
|
+
self.instance_exec(event, &callback)
|
166
|
+
end unless routes[event["type"]].nil?
|
167
|
+
bricks_instances.each do |brick|
|
168
|
+
brick.routes[event["type"]].each do |callback|
|
169
|
+
brick.instance_exec(event, &callback)
|
170
|
+
end unless brick.routes[event["type"]].nil?
|
171
|
+
end
|
172
|
+
end
|
173
|
+
end
|
174
|
+
end
|
175
|
+
end
|
176
|
+
end
|
177
|
+
end
|