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 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.1"
15
+ gem "em-ucengine", "~> 0.3"
16
+
17
+ ## Usage
18
+
19
+ ### Client
19
20
 
20
- Then, you can use it in your code:
21
+ #### EventMachine
21
22
 
22
- require "em-ucengine"
23
- EventMachine::UCEngine.run do |uce|
24
- uce.connect("participant", "pwd") do |session|
25
- EM.add_periodic_timer(1) { session.publish("em-ucengine.example.ping", "demo") }
26
- session.subscribe("demo") do |event|
27
- puts "Hey, we received an event: #{event.inspect}"
28
- end
29
- end
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
- Don't hesitate to look at the specs for more examples ;-)
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
- TODO
36
- ----
52
+ #### Net/HTTP
37
53
 
38
- * Files API
39
- * ACL or Roles API
40
- * Better error handling
41
- * Complete the specs
42
- * Compatibility with em-synchrony
43
- * Yard documentation
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
- Issues or Suggestions
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
- ./spec/em-ucengine_spec.rb
102
+ rake test
55
103
 
56
104
 
57
105
  Copyright (c) 2011 af83, released under the LGPL license
@@ -1,188 +1,3 @@
1
- require "eventmachine"
2
- require "em-http-request"
3
- require "yajl"
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