julien51-babylon 0.0.5 → 0.0.7
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.rdoc +5 -4
- metadata +23 -58
- data/lib/babylon.rb +0 -25
- data/lib/babylon/base/controller.rb +0 -65
- data/lib/babylon/base/view.rb +0 -50
- data/lib/babylon/client_connection.rb +0 -147
- data/lib/babylon/component_connection.rb +0 -76
- data/lib/babylon/router.rb +0 -95
- data/lib/babylon/runner.rb +0 -42
- data/lib/babylon/xmpp_connection.rb +0 -158
- data/templates/babylon/app/controllers/README.rdoc +0 -13
- data/templates/babylon/app/models/README.rdoc +0 -1
- data/templates/babylon/app/views/README.rdoc +0 -12
- data/templates/babylon/config/boot.rb +0 -8
- data/templates/babylon/config/config.yaml +0 -29
- data/templates/babylon/config/routes.yaml +0 -15
- data/templates/babylon/scripts/component +0 -2
- data/templates/babylon/scripts/generate +0 -47
data/LICENSE
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2009 julien
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
CHANGED
@@ -9,7 +9,6 @@ This framework can use both an XMPP Component (XEP-0114) and an XMPP Client. How
|
|
9
9
|
== FEATURES/PROBLEMS:
|
10
10
|
|
11
11
|
* This hasn't been tested.
|
12
|
-
* Problems with quotes and double-quotes in text that are not handled correctly... I spent too many hours on this! Please help!
|
13
12
|
|
14
13
|
== ROADMAP :
|
15
14
|
|
@@ -31,9 +30,9 @@ To create an Application with Babylon:
|
|
31
30
|
|
32
31
|
3. Use the generator or write your own controllers :
|
33
32
|
|
34
|
-
$>
|
33
|
+
$> script/generate message echo:10:"//message[@type='chat']/body" subscribed:0:"//presence[@type='subscribe']"
|
35
34
|
|
36
|
-
This will generate a "MessageController" class with 2 methods : echo and subscribed. "echo" will be called when the component receives message stanzas of type 'chat', while "subscribed" will be called for presence stanzas of type 'subscribe'. 10 and 0 are the priority : useful when a stanza matches 2 XPath. Also, try to put high priorities to the "most frequent" stanzas to improve your component's performance. This will also generate 2 'views' used to build your stanzas. And finally, this will write 2 routes in the config/routes.
|
35
|
+
This will generate a "MessageController" class with 2 methods : echo and subscribed. "echo" will be called when the component receives message stanzas of type 'chat', while "subscribed" will be called for presence stanzas of type 'subscribe'. 10 and 0 are the priority : useful when a stanza matches 2 XPath. Also, try to put high priorities to the "most frequent" stanzas to improve your component's performance. This will also generate 2 'views' used to build your stanzas. And finally, this will write 2 routes in the config/routes.rb
|
37
36
|
|
38
37
|
4. Write your application's code and views :
|
39
38
|
|
@@ -88,6 +87,8 @@ Compared to Rails, we are using accessors (and not @variables assigned in the co
|
|
88
87
|
|
89
88
|
This code hasn't been tested at all! (yes, i know it's bad, but I couldn't have rspec working with eventmachine) Feel free to pull, branch, improve {code|specs|tests|docs} and we will merge it!
|
90
89
|
|
90
|
+
If you used a version before 0.0.6, please change your routes to use the new router DSL. (See template for more help). It shouldn't be long... sorry for that.
|
91
|
+
|
91
92
|
== REQUIREMENTS:
|
92
93
|
|
93
94
|
Gems : Eventmachine, nokogiri, YAML
|
@@ -121,4 +122,4 @@ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
|
121
122
|
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
122
123
|
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
123
124
|
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
124
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
125
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
metadata
CHANGED
@@ -1,76 +1,37 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: julien51-babylon
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.7
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
-
|
8
|
-
- Astro
|
7
|
+
- julien Genestoux
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
11
|
|
13
|
-
date: 2009-
|
14
|
-
default_executable:
|
15
|
-
dependencies:
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
version_requirement:
|
20
|
-
version_requirements: !ruby/object:Gem::Requirement
|
21
|
-
requirements:
|
22
|
-
- - ">="
|
23
|
-
- !ruby/object:Gem::Version
|
24
|
-
version: "0"
|
25
|
-
version:
|
26
|
-
- !ruby/object:Gem::Dependency
|
27
|
-
name: eventmachine
|
28
|
-
type: :runtime
|
29
|
-
version_requirement:
|
30
|
-
version_requirements: !ruby/object:Gem::Requirement
|
31
|
-
requirements:
|
32
|
-
- - ">="
|
33
|
-
- !ruby/object:Gem::Version
|
34
|
-
version: "0"
|
35
|
-
version:
|
36
|
-
description: Babylon is a framework to create EventMachine based XMPP External Components in Ruby.
|
37
|
-
email: babylon@notifixio.us
|
12
|
+
date: 2009-03-18 00:00:00 -07:00
|
13
|
+
default_executable: babylon
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description:
|
17
|
+
email: julien.genestoux@gmail.com
|
38
18
|
executables:
|
39
19
|
- babylon
|
40
20
|
extensions: []
|
41
21
|
|
42
|
-
extra_rdoc_files:
|
43
|
-
|
44
|
-
files:
|
22
|
+
extra_rdoc_files:
|
45
23
|
- README.rdoc
|
46
|
-
-
|
47
|
-
|
48
|
-
- lib/babylon/base/view.rb
|
49
|
-
- lib/babylon/component_connection.rb
|
50
|
-
- lib/babylon/client_connection.rb
|
51
|
-
- lib/babylon/router.rb
|
52
|
-
- lib/babylon/runner.rb
|
53
|
-
- lib/babylon/xmpp_connection.rb
|
24
|
+
- LICENSE
|
25
|
+
files:
|
54
26
|
- bin/babylon
|
55
|
-
-
|
56
|
-
-
|
57
|
-
- templates/babylon
|
58
|
-
- templates/babylon/app
|
59
|
-
- templates/babylon/app/controllers/README.rdoc
|
60
|
-
- templates/babylon/app/models/README.rdoc
|
61
|
-
- templates/babylon/app/views/README.rdoc
|
62
|
-
- templates/babylon/config
|
63
|
-
- templates/babylon/config/routes.yaml
|
64
|
-
- templates/babylon/config/config.yaml
|
65
|
-
- templates/babylon/config/boot.rb
|
66
|
-
- templates/babylon/scripts
|
67
|
-
- templates/babylon/scripts/component
|
68
|
-
- templates/babylon/scripts/generate
|
27
|
+
- README.rdoc
|
28
|
+
- LICENSE
|
69
29
|
has_rdoc: true
|
70
|
-
homepage: http://github.com/julien51/babylon
|
30
|
+
homepage: http://github.com/julien51/babylon
|
71
31
|
post_install_message:
|
72
|
-
rdoc_options:
|
73
|
-
|
32
|
+
rdoc_options:
|
33
|
+
- --inline-source
|
34
|
+
- --charset=UTF-8
|
74
35
|
require_paths:
|
75
36
|
- lib
|
76
37
|
required_ruby_version: !ruby/object:Gem::Requirement
|
@@ -85,8 +46,12 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
85
46
|
- !ruby/object:Gem::Version
|
86
47
|
version: "0"
|
87
48
|
version:
|
88
|
-
requirements:
|
89
|
-
|
49
|
+
requirements:
|
50
|
+
- eventmachine
|
51
|
+
- yaml
|
52
|
+
- fileutils
|
53
|
+
- log4r
|
54
|
+
- nokogiri
|
90
55
|
rubyforge_project:
|
91
56
|
rubygems_version: 1.2.0
|
92
57
|
signing_key:
|
data/lib/babylon.rb
DELETED
@@ -1,25 +0,0 @@
|
|
1
|
-
$:.unshift(File.dirname(__FILE__)) unless
|
2
|
-
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
3
|
-
|
4
|
-
require 'eventmachine'
|
5
|
-
require 'nokogiri'
|
6
|
-
|
7
|
-
require 'babylon/xmpp_connection.rb'
|
8
|
-
require 'babylon/component_connection.rb'
|
9
|
-
require 'babylon/client_connection.rb'
|
10
|
-
require 'babylon/router.rb'
|
11
|
-
require 'babylon/runner.rb'
|
12
|
-
require 'babylon/base/controller.rb'
|
13
|
-
require 'babylon/base/view.rb'
|
14
|
-
|
15
|
-
# Babylon is a XMPP Component Framework based on EventMachine. It uses the Nokogiri GEM, which is a Ruby wrapper for Libxml2.
|
16
|
-
# It implements the MVC paradigm.
|
17
|
-
# You can create your own application by running :
|
18
|
-
# $> babylon app_name
|
19
|
-
# This will generate some folders and files for your application. Please see README for further instructions
|
20
|
-
|
21
|
-
module Babylon
|
22
|
-
# 0.0.5 : Not suited for production, use at your own risks
|
23
|
-
VERSION = '0.0.5'
|
24
|
-
end
|
25
|
-
|
@@ -1,65 +0,0 @@
|
|
1
|
-
module Babylon
|
2
|
-
module Base
|
3
|
-
|
4
|
-
# Your application's controller should be descendant of this class.
|
5
|
-
|
6
|
-
class Controller
|
7
|
-
|
8
|
-
attr_accessor :stanza # Stanza received by the controller (Nokogiri::XML::Node)
|
9
|
-
|
10
|
-
# Creates a new controller (you should not override this class) and assigns the stanza
|
11
|
-
def initialize(params = {})
|
12
|
-
params.each do |key, value|
|
13
|
-
instance_variable_set("@#{key}", value)
|
14
|
-
end
|
15
|
-
@rendered = false
|
16
|
-
end
|
17
|
-
|
18
|
-
# Performs the action and calls back the optional block argument : you should not override this function
|
19
|
-
def perform(action, &block)
|
20
|
-
@action_name = action
|
21
|
-
@block = block
|
22
|
-
self.send(@action_name)
|
23
|
-
self.render
|
24
|
-
end
|
25
|
-
|
26
|
-
# Called by default after each action to "build" a XMPP stanza. By default, it will use the /controller_name/action.xml.builder
|
27
|
-
def render(options = nil)
|
28
|
-
return if @rendered # Avoid double rendering
|
29
|
-
|
30
|
-
if options.nil? # default rendering
|
31
|
-
return render(:file => default_template_name)
|
32
|
-
elsif action_name = options[:action]
|
33
|
-
return render(:file => default_template_name(action_name.to_s))
|
34
|
-
end
|
35
|
-
render_for_file(File.join("app/views", "#{self.class.name.gsub("Controller","").downcase}", options[:file]))
|
36
|
-
|
37
|
-
# And finally, we set up rendered to be true
|
38
|
-
@rendered = true
|
39
|
-
end
|
40
|
-
|
41
|
-
protected
|
42
|
-
|
43
|
-
# Used to transfer the assigned variables from the controller to the views
|
44
|
-
def hashed_variables
|
45
|
-
vars = Hash.new
|
46
|
-
instance_variables.each do |var|
|
47
|
-
vars[var[1..-1]] = instance_variable_get(var)
|
48
|
-
end
|
49
|
-
return vars
|
50
|
-
end
|
51
|
-
|
52
|
-
# Default template name used to build stanzas
|
53
|
-
def default_template_name(action_name = nil)
|
54
|
-
path = "#{action_name || @action_name}.xml.builder"
|
55
|
-
return path
|
56
|
-
end
|
57
|
-
|
58
|
-
# Creates the view and "evaluates" it to build the XML for the stanza
|
59
|
-
def render_for_file(file)
|
60
|
-
view = Babylon::Base::View.new(file, hashed_variables)
|
61
|
-
@block.call(view.evaluate)
|
62
|
-
end
|
63
|
-
end
|
64
|
-
end
|
65
|
-
end
|
data/lib/babylon/base/view.rb
DELETED
@@ -1,50 +0,0 @@
|
|
1
|
-
module Babylon
|
2
|
-
module Base
|
3
|
-
|
4
|
-
##
|
5
|
-
# Your application's views (stanzas) should be descendant of this class.
|
6
|
-
class View
|
7
|
-
attr_reader :output
|
8
|
-
|
9
|
-
##
|
10
|
-
# Instantiate a new view with the various varibales passed in assigns and the path of the template to render.
|
11
|
-
def initialize(path, assigns)
|
12
|
-
@assigns = assigns
|
13
|
-
@output = ""
|
14
|
-
@view_template = path
|
15
|
-
end
|
16
|
-
|
17
|
-
##
|
18
|
-
# "Loads" the view file, and uses the Nokogiri Builder to build the XML stanzas that will be sent.
|
19
|
-
def evaluate
|
20
|
-
evaluate_assigns
|
21
|
-
view_content = File.read(@view_template)
|
22
|
-
xml = Nokogiri::XML::Builder.new do
|
23
|
-
instance_eval(view_content)
|
24
|
-
end
|
25
|
-
return xml.doc.root #we return the doc's root (to avoid the instruct)
|
26
|
-
end
|
27
|
-
|
28
|
-
##
|
29
|
-
# Evaluate the local assigns and pushes them to the view.
|
30
|
-
def evaluate_assigns
|
31
|
-
unless @assigns_added
|
32
|
-
assign_variables_from_controller
|
33
|
-
@assigns_added = true
|
34
|
-
end
|
35
|
-
end
|
36
|
-
|
37
|
-
##
|
38
|
-
# Assigns instance variables from the controller to the view.
|
39
|
-
def assign_variables_from_controller
|
40
|
-
@assigns.each do |key, value|
|
41
|
-
instance_variable_set("@#{key}", value)
|
42
|
-
self.class.send(:define_method, key) do # Defining accessors
|
43
|
-
value
|
44
|
-
end
|
45
|
-
end
|
46
|
-
end
|
47
|
-
|
48
|
-
end
|
49
|
-
end
|
50
|
-
end
|
@@ -1,147 +0,0 @@
|
|
1
|
-
module Babylon
|
2
|
-
|
3
|
-
##
|
4
|
-
# ClientConnection is in charge of the XMPP connection for a Regular XMPP Client.
|
5
|
-
# So far, SASL Plain authenticationonly is supported
|
6
|
-
# Upon stanza reception, and depending on the status (connected... etc), this component will handle or forward the stanzas.
|
7
|
-
class ClientConnection < XmppConnection
|
8
|
-
require 'digest/sha1'
|
9
|
-
require 'base64'
|
10
|
-
|
11
|
-
attr_reader :binding_iq_id, :session_iq_id
|
12
|
-
|
13
|
-
##
|
14
|
-
# Returns true only if we're in connected state
|
15
|
-
def connected?
|
16
|
-
@state == :connected
|
17
|
-
end
|
18
|
-
|
19
|
-
##
|
20
|
-
# Creates a new ClientConnection and waits for data in the stream
|
21
|
-
def initialize(params)
|
22
|
-
super(params)
|
23
|
-
@state = :wait_for_stream
|
24
|
-
end
|
25
|
-
|
26
|
-
##
|
27
|
-
# Connection_completed is called when the connection (socket) has been established and is in charge of "building" the XML stream
|
28
|
-
# to establish the XMPP connection itself.
|
29
|
-
# We use a "tweak" here to send only the starting tag of stream:stream
|
30
|
-
def connection_completed
|
31
|
-
super
|
32
|
-
builder = Nokogiri::XML::Builder.new do
|
33
|
-
self.send('stream:stream', {'xmlns' => @context.stream_namespace(), 'xmlns:stream' => 'http://etherx.jabber.org/streams', 'to' => @context.config['host'], 'version' => '1.0'}) do
|
34
|
-
paste_content_here # The stream:stream element should be cut here ;)
|
35
|
-
end
|
36
|
-
end
|
37
|
-
@outstream = builder.doc
|
38
|
-
start_stream, stop_stream = builder.to_xml.split('<paste_content_here/>')
|
39
|
-
send_data(start_stream)
|
40
|
-
end
|
41
|
-
|
42
|
-
##
|
43
|
-
# Called upon stanza reception
|
44
|
-
# Marked as connected when the client has been SASLed, authenticated, biund to a resource and when the session has been created
|
45
|
-
def receive_stanza(stanza)
|
46
|
-
case @state
|
47
|
-
when :connected
|
48
|
-
super # Can be dispatched
|
49
|
-
|
50
|
-
when :wait_for_stream
|
51
|
-
if stanza.name == "stream:stream" && stanza.attributes['id']
|
52
|
-
@state = :wait_for_auth_mechanisms unless @success
|
53
|
-
@state = :wait_for_bind if @success
|
54
|
-
end
|
55
|
-
|
56
|
-
when :wait_for_auth_mechanisms
|
57
|
-
if stanza.name == "stream:features"
|
58
|
-
if stanza.at("starttls") # we shall start tls
|
59
|
-
starttls = Nokogiri::XML::Node.new("starttls", @outstream)
|
60
|
-
starttls["xmlns"] = stanza.at("starttls").namespaces.first.last
|
61
|
-
send(starttls)
|
62
|
-
@state = :wait_for_proceed
|
63
|
-
elsif stanza.at("mechanisms") # tls is ok
|
64
|
-
if stanza.at("mechanisms/[contains(mechanism,'PLAIN')]")
|
65
|
-
# auth_text = "#{jid.strip}\x00#{jid.node}\x00#{password}"
|
66
|
-
auth = Nokogiri::XML::Node.new("auth", @outstream)
|
67
|
-
auth['mechanism'] = "PLAIN"
|
68
|
-
auth['xmlns'] = stanza.at("mechanisms").namespaces.first.last
|
69
|
-
auth.content = Base64::encode64([@config['jid'],@config['jid'].split("@").first,@config['password']].join("\000")).gsub(/\s/, '')
|
70
|
-
send(auth)
|
71
|
-
@state = :wait_for_success
|
72
|
-
end
|
73
|
-
end
|
74
|
-
end
|
75
|
-
|
76
|
-
when :wait_for_success
|
77
|
-
if stanza.name == "success" # Yay! Success
|
78
|
-
@success = true
|
79
|
-
@state = :wait_for_stream
|
80
|
-
send_data @outstream.root.to_xml.split('<paste_content_here/>').first
|
81
|
-
else
|
82
|
-
# Hum Failure...
|
83
|
-
end
|
84
|
-
|
85
|
-
when :wait_for_bind
|
86
|
-
if stanza.name == "stream:features"
|
87
|
-
if stanza.at("bind")
|
88
|
-
# Let's build the binding_iq
|
89
|
-
@binding_iq_id = Integer(rand(10000))
|
90
|
-
builder = Nokogiri::XML::Builder.new do
|
91
|
-
iq(:type => "set", :id => @context.binding_iq_id) do
|
92
|
-
bind(:xmlns => "urn:ietf:params:xml:ns:xmpp-bind") do
|
93
|
-
if @context.config['resource']
|
94
|
-
resource(@context.config['resource'] )
|
95
|
-
else
|
96
|
-
resource("babylon_client_#{@context.binding_iq_id}")
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
100
|
-
end
|
101
|
-
iq = @outstream.add_child(builder.doc.root)
|
102
|
-
send(iq)
|
103
|
-
@state = :wait_for_confirmed_binding
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
when :wait_for_confirmed_binding
|
108
|
-
if stanza.name == "iq" && stanza["type"] == "result" && Integer(stanza["id"]) == @binding_iq_id
|
109
|
-
if stanza.at("jid")
|
110
|
-
@jid = stanza.at("jid").text
|
111
|
-
end
|
112
|
-
end
|
113
|
-
# And now, we must initiate the session
|
114
|
-
@session_iq_id = Integer(rand(10000))
|
115
|
-
builder = Nokogiri::XML::Builder.new do
|
116
|
-
iq(:type => "set", :id => @context.session_iq_id) do
|
117
|
-
session(:xmlns => "urn:ietf:params:xml:ns:xmpp-session")
|
118
|
-
end
|
119
|
-
end
|
120
|
-
iq = @outstream.add_child(builder.doc.root)
|
121
|
-
send(iq)
|
122
|
-
@state = :wait_for_confirmed_session
|
123
|
-
|
124
|
-
when :wait_for_confirmed_session
|
125
|
-
if stanza.name == "iq" && stanza["type"] == "result" && Integer(stanza["id"]) == @session_iq_id && stanza.at("session")
|
126
|
-
# And now, send a presence!
|
127
|
-
presence = Nokogiri::XML::Node.new("presence", @outstream)
|
128
|
-
send(presence)
|
129
|
-
@state = :connected
|
130
|
-
end
|
131
|
-
|
132
|
-
when :wait_for_proceed
|
133
|
-
start_tls() # starting TLS
|
134
|
-
@state = :wait_for_stream
|
135
|
-
send_data @outstream.root.to_xml.split('<paste_content_here/>').first
|
136
|
-
end
|
137
|
-
|
138
|
-
end
|
139
|
-
|
140
|
-
##
|
141
|
-
# Namespace of the client
|
142
|
-
def stream_namespace
|
143
|
-
"jabber:client"
|
144
|
-
end
|
145
|
-
|
146
|
-
end
|
147
|
-
end
|
@@ -1,76 +0,0 @@
|
|
1
|
-
module Babylon
|
2
|
-
##
|
3
|
-
# ComponentConnection is in charge of the XMPP connection itself.
|
4
|
-
# Upon stanza reception, and depending on the status (connected... etc), this component will handle or forward the stanzas.
|
5
|
-
class ComponentConnection < XmppConnection
|
6
|
-
require 'digest/sha1'
|
7
|
-
|
8
|
-
##
|
9
|
-
# Returns true only if we're in connected state
|
10
|
-
def connected?
|
11
|
-
@state == :connected
|
12
|
-
end
|
13
|
-
|
14
|
-
##
|
15
|
-
# Creates a new ComponentConnection and waits for data in the stream
|
16
|
-
def initialize(*a)
|
17
|
-
super
|
18
|
-
@state = :wait_for_stream
|
19
|
-
end
|
20
|
-
|
21
|
-
##
|
22
|
-
# Connection_completed is called when the connection (socket) has been established and is in charge of "building" the XML stream
|
23
|
-
# to establish the XMPP connection itself.
|
24
|
-
# We use a "tweak" here to send only the starting tag of stream:stream
|
25
|
-
def connection_completed
|
26
|
-
super
|
27
|
-
builder = Nokogiri::XML::Builder.new do
|
28
|
-
self.send('stream:stream', {'xmlns' => "jabber:component:accept", 'xmlns:stream' => 'http://etherx.jabber.org/streams', 'to' => @context.config['jid']}) do
|
29
|
-
paste_content_here # The stream:stream element should be cut here ;)
|
30
|
-
end
|
31
|
-
end
|
32
|
-
@start_stream, @stop_stream = builder.to_xml.split('<paste_content_here/>')
|
33
|
-
send_data(@start_stream)
|
34
|
-
end
|
35
|
-
|
36
|
-
##
|
37
|
-
# XMPP Component handshake as defined in XEP-0114:
|
38
|
-
# http://xmpp.org/extensions/xep-0114.html
|
39
|
-
def receive_stanza(stanza)
|
40
|
-
case @state
|
41
|
-
when :connected # Most frequent case
|
42
|
-
super # Can be dispatched
|
43
|
-
|
44
|
-
when :wait_for_stream
|
45
|
-
if stanza.name == "stream:stream" && stanza.attributes['id']
|
46
|
-
# This means the XMPP session started!
|
47
|
-
# We must send the handshake now.
|
48
|
-
hash = Digest::SHA1::hexdigest(stanza.attributes['id'].content + @config['password'])
|
49
|
-
handshake = Nokogiri::XML::Node.new("handshake", stanza.document)
|
50
|
-
handshake.content = hash
|
51
|
-
send(handshake)
|
52
|
-
@state = :wait_for_handshake
|
53
|
-
else
|
54
|
-
raise
|
55
|
-
end
|
56
|
-
|
57
|
-
when :wait_for_handshake
|
58
|
-
if stanza.name == "handshake"
|
59
|
-
# Awesome, we're now connected and authentified, let's tell the CentralRouter we're connecter
|
60
|
-
CentralRouter.connected(self)
|
61
|
-
@state = :connected
|
62
|
-
else
|
63
|
-
raise
|
64
|
-
end
|
65
|
-
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
##
|
70
|
-
# Namespace of the component
|
71
|
-
def stream_namespace
|
72
|
-
'jabber:component:accept'
|
73
|
-
end
|
74
|
-
|
75
|
-
end
|
76
|
-
end
|
data/lib/babylon/router.rb
DELETED
@@ -1,95 +0,0 @@
|
|
1
|
-
module Babylon
|
2
|
-
|
3
|
-
##
|
4
|
-
# The router is in charge of sending the right stanzas to the right controllers based on user defined Routes.
|
5
|
-
module Router
|
6
|
-
|
7
|
-
@@connection = nil
|
8
|
-
|
9
|
-
##
|
10
|
-
# Add several routes to the router
|
11
|
-
# Routes should be of form {name => params}
|
12
|
-
def add_routes(routes)
|
13
|
-
routes.each do |name, params|
|
14
|
-
add_route(Route.new(name, params))
|
15
|
-
end
|
16
|
-
end
|
17
|
-
|
18
|
-
##
|
19
|
-
# Connected is called by the XmppConnection to indicate that the XMPP connection has been established
|
20
|
-
def connected(connection)
|
21
|
-
@@connection = connection
|
22
|
-
end
|
23
|
-
|
24
|
-
##
|
25
|
-
# Accessor for @@connection
|
26
|
-
def connection
|
27
|
-
@@connection
|
28
|
-
end
|
29
|
-
|
30
|
-
##
|
31
|
-
# Insert a route and makes sure that the routes are sorted
|
32
|
-
def add_route(route)
|
33
|
-
@routes ||= []
|
34
|
-
@routes << route
|
35
|
-
@routes.sort! { |r1,r2|
|
36
|
-
r2.priority <=> r1.priority
|
37
|
-
}
|
38
|
-
end
|
39
|
-
|
40
|
-
# Look for the first martching route and calls the correspondong action for the corresponding controller.
|
41
|
-
# Sends the response on the XMPP stream/
|
42
|
-
def route(stanza)
|
43
|
-
return false if !@@connection
|
44
|
-
@routes ||= []
|
45
|
-
@routes.each { |route|
|
46
|
-
if route.accepts?(stanza)
|
47
|
-
# Here should happen the magic : call the controller
|
48
|
-
controller = route.controller.new({:stanza => stanza})
|
49
|
-
controller.perform(route.action) do |response|
|
50
|
-
# Response should be a Nokogiri::Node Object
|
51
|
-
@@connection.send(response)
|
52
|
-
end
|
53
|
-
return true
|
54
|
-
end
|
55
|
-
}
|
56
|
-
false
|
57
|
-
end
|
58
|
-
|
59
|
-
# Throw away all added routes from this router. Helpful for
|
60
|
-
# testing.
|
61
|
-
def purge_routes!
|
62
|
-
@routes = []
|
63
|
-
end
|
64
|
-
end
|
65
|
-
|
66
|
-
##
|
67
|
-
# Main router where all dispatchers shall register.
|
68
|
-
module CentralRouter
|
69
|
-
extend Router
|
70
|
-
end
|
71
|
-
|
72
|
-
##
|
73
|
-
# Route class which associate an XPATH match and a priority to a controller and an action
|
74
|
-
class Route
|
75
|
-
|
76
|
-
attr_reader :priority, :controller, :action
|
77
|
-
|
78
|
-
##
|
79
|
-
# Creates a new route
|
80
|
-
def initialize(name, params)
|
81
|
-
@priority = params["priority"]
|
82
|
-
@xpath = params["xpath"]
|
83
|
-
@controller = Kernel.const_get("#{params["controller"].capitalize}Controller")
|
84
|
-
@action = params["action"]
|
85
|
-
end
|
86
|
-
|
87
|
-
##
|
88
|
-
# Checks that the route matches the stanzas and calls the the action on the controller
|
89
|
-
def accepts?(stanza)
|
90
|
-
stanza.xpath(@xpath, stanza.namespaces).first ? self : false
|
91
|
-
end
|
92
|
-
|
93
|
-
end
|
94
|
-
|
95
|
-
end
|
data/lib/babylon/runner.rb
DELETED
@@ -1,42 +0,0 @@
|
|
1
|
-
module Babylon
|
2
|
-
|
3
|
-
##
|
4
|
-
# Runner is in charge of running the application.
|
5
|
-
class Runner
|
6
|
-
|
7
|
-
##
|
8
|
-
# When run is called, it loads the configuration, the routes and add them into the router
|
9
|
-
# It then loads the models.
|
10
|
-
# Finally it starts the EventMachine and connect the ComponentConnection
|
11
|
-
# You can pass an additional block that will be called upon launching, when the eventmachine has been started.
|
12
|
-
def self.run(env = "development", &callback)
|
13
|
-
config = YAML::load(File.new('config/config.yaml'))[env]
|
14
|
-
routes = YAML::load(File.new('config/routes.yaml')) || []
|
15
|
-
|
16
|
-
# Adding Routes
|
17
|
-
CentralRouter.add_routes(routes)
|
18
|
-
|
19
|
-
# Requiring all models
|
20
|
-
Dir.glob('app/models/*.rb').each do |f|
|
21
|
-
require f
|
22
|
-
end
|
23
|
-
|
24
|
-
# Starting the EventMachine
|
25
|
-
EventMachine.epoll
|
26
|
-
EventMachine::run do
|
27
|
-
if config["application_type"] == "client"
|
28
|
-
Babylon::ClientConnection.connect(config) do |stanza|
|
29
|
-
CentralRouter.route stanza # Upon reception of new stanza, we Route them through the controller
|
30
|
-
end
|
31
|
-
else
|
32
|
-
Babylon::ComponentConnection.connect(config) do |stanza|
|
33
|
-
CentralRouter.route stanza # Upon reception of new stanza, we Route them through the controller
|
34
|
-
end
|
35
|
-
end
|
36
|
-
# And finally, let's allow the application to do all it wants to do after we started the EventMachine!
|
37
|
-
callback.call if callback
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
end
|
42
|
-
end
|
@@ -1,158 +0,0 @@
|
|
1
|
-
module Babylon
|
2
|
-
|
3
|
-
##
|
4
|
-
# Connection Exception
|
5
|
-
class NotConnected < Exception; end
|
6
|
-
|
7
|
-
##
|
8
|
-
# This class is in charge of handling the network connection to the XMPP server.
|
9
|
-
class XmppConnection < EventMachine::Connection
|
10
|
-
|
11
|
-
attr_reader :config
|
12
|
-
|
13
|
-
##
|
14
|
-
# Connects the XmppConnection to the right host with the right port.
|
15
|
-
# It passes itself (as handler) and the configuration
|
16
|
-
def self.connect(config, &block)
|
17
|
-
EventMachine::connect config['host'], config['port'], self, config.merge({:callback => block})
|
18
|
-
end
|
19
|
-
|
20
|
-
##
|
21
|
-
# Called when the connection is terminated and stops the event loop
|
22
|
-
def unbind()
|
23
|
-
EventMachine::stop_event_loop
|
24
|
-
end
|
25
|
-
|
26
|
-
##
|
27
|
-
# Instantiate the Handler (called internally by EventMachine) and attaches a new XmppParser
|
28
|
-
def initialize(config)
|
29
|
-
@config = config
|
30
|
-
super()
|
31
|
-
@parser = XmppParser.new(&method(:receive_stanza))
|
32
|
-
end
|
33
|
-
|
34
|
-
##
|
35
|
-
# Called when a full stanza has been received and returns it to the central router to be sent to the corresponding controller. Eventually it displays this data for debugging purposes
|
36
|
-
def receive_stanza(stanza)
|
37
|
-
# If not handled by subclass (for authentication)
|
38
|
-
@config[:callback].call(stanza) if @config[:callback]
|
39
|
-
end
|
40
|
-
|
41
|
-
##
|
42
|
-
# Sends the Nokogiri::XML data (after converting to string) on the stream. It also appends the right "from" to be the component's JId if none has been mentionned. Eventually it displays this data for debugging purposes
|
43
|
-
def send(xml)
|
44
|
-
if !xml.attributes["from"]
|
45
|
-
xml["from"] = jid
|
46
|
-
end
|
47
|
-
puts "SENDING #{xml}\n" if debug? # Very low level Logging
|
48
|
-
send_data "#{xml}"
|
49
|
-
end
|
50
|
-
|
51
|
-
##
|
52
|
-
# Memoizer for jid. The jid can actually be changed in subclasses (client will probbaly want to change it to include the resource)
|
53
|
-
def jid
|
54
|
-
@jid ||= config['jid']
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
|
59
|
-
##
|
60
|
-
# receive_data is called when data is received. It is then passed to the parser.
|
61
|
-
def receive_data(data)
|
62
|
-
puts "RECEIVED #{data}\n" if debug? # Low level Logging
|
63
|
-
@parser.parse data
|
64
|
-
end
|
65
|
-
|
66
|
-
##
|
67
|
-
# Pretty self-explanatory ;)
|
68
|
-
def debug?
|
69
|
-
@config["debug"]
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
##
|
74
|
-
# This is the XML SAX Parser that accepts "pushed" content
|
75
|
-
class XmppParser < Nokogiri::XML::SAX::Document
|
76
|
-
|
77
|
-
##
|
78
|
-
# Initialize the parser and adds the callback that will be called upen stanza completion
|
79
|
-
def initialize(&callback)
|
80
|
-
@callback = callback
|
81
|
-
super()
|
82
|
-
@parser = Nokogiri::XML::SAX::Parser.new(self)
|
83
|
-
@doc = nil
|
84
|
-
@elem = nil
|
85
|
-
end
|
86
|
-
|
87
|
-
##
|
88
|
-
# Parses the received data
|
89
|
-
def parse(data)
|
90
|
-
@parser.parse data
|
91
|
-
end
|
92
|
-
|
93
|
-
##
|
94
|
-
# Called when the document received in the stream is started
|
95
|
-
def start_document
|
96
|
-
@doc = Nokogiri::XML::Document.new
|
97
|
-
end
|
98
|
-
|
99
|
-
##
|
100
|
-
# Adds characters to the current element (being parsed)
|
101
|
-
def characters(string)
|
102
|
-
@elem.add_child(Nokogiri::XML::Text.new(string, @doc))
|
103
|
-
end
|
104
|
-
|
105
|
-
##
|
106
|
-
# Instantiate a new current Element, adds the corresponding attributes and namespaces
|
107
|
-
# The new element is eventually added to a parent element (if present).
|
108
|
-
# If this element is the first element (the root of the document), then instead of adding it to a parent, we add it to the document itself. In this case, the current element will not be terminated, so we activate the callback immediately.
|
109
|
-
def start_element(qname, attributes = [])
|
110
|
-
e = Nokogiri::XML::Element.new(qname, @doc)
|
111
|
-
add_namespaces_and_attributes_to_node(attributes, e)
|
112
|
-
|
113
|
-
# If we don't have any elem yet, we are at the root
|
114
|
-
@elem = @elem ? @elem.add_child(e) : (@root = e)
|
115
|
-
|
116
|
-
if @elem.name == "stream:stream"
|
117
|
-
# Should be called only for stream:stream
|
118
|
-
@doc = Nokogiri::XML::Document.new
|
119
|
-
@root = @elem
|
120
|
-
@doc.root = @elem
|
121
|
-
@callback.call(@elem)
|
122
|
-
end
|
123
|
-
end
|
124
|
-
|
125
|
-
##
|
126
|
-
# Terminates the current element and calls the callback
|
127
|
-
def end_element(name)
|
128
|
-
if @elem
|
129
|
-
if @elem.parent == @root
|
130
|
-
@callback.call(@elem)
|
131
|
-
# And we also need to remove @elem from its tree
|
132
|
-
@elem.unlink
|
133
|
-
# And the current elem is the root
|
134
|
-
@elem = @root
|
135
|
-
else
|
136
|
-
@elem = @elem.parent
|
137
|
-
end
|
138
|
-
end
|
139
|
-
end
|
140
|
-
|
141
|
-
private
|
142
|
-
|
143
|
-
##
|
144
|
-
# Adds namespaces and attributes. Nokogiri passes them as a array of [name, value, name, value]...
|
145
|
-
def add_namespaces_and_attributes_to_node(attrs, node)
|
146
|
-
(attrs.size / 2).times do |i|
|
147
|
-
name, value = attrs[2 * i], attrs[2 * i + 1]
|
148
|
-
if name =~ /xmlns/
|
149
|
-
node.add_namespace(name, value)
|
150
|
-
else
|
151
|
-
node.set_attribute name, value
|
152
|
-
end
|
153
|
-
end
|
154
|
-
end
|
155
|
-
|
156
|
-
end
|
157
|
-
|
158
|
-
end
|
@@ -1 +0,0 @@
|
|
1
|
-
You can define you class models here. It is totally ok to reuse your ActiveRecord from another application!
|
@@ -1,12 +0,0 @@
|
|
1
|
-
= Babylon::Base::View
|
2
|
-
|
3
|
-
== Usage
|
4
|
-
|
5
|
-
Please see Babylon Rdoc. Put all the views related to controller MyController into app/views/my/...
|
6
|
-
This file are Xml Builder Files (see Nokogiri Documentation for any doubt).
|
7
|
-
|
8
|
-
== Example
|
9
|
-
|
10
|
-
self.message(:to => to, :from => from, :type => :chat) do
|
11
|
-
self.body(body) // Same as self.send(:body, body) (
|
12
|
-
end
|
@@ -1,29 +0,0 @@
|
|
1
|
-
# This contains the global configuration of your component.
|
2
|
-
# env:
|
3
|
-
# jid: your.component.jid
|
4
|
-
# password: your.component.password
|
5
|
-
# host: host on which the XMPP server is running
|
6
|
-
# port: port to which your component should connect
|
7
|
-
# debug: outputs the xmpp stanzas
|
8
|
-
# application_type: client | component (by default it is component and we strongly discourage any client application in production)
|
9
|
-
|
10
|
-
development:
|
11
|
-
jid: component.server.com
|
12
|
-
password: password
|
13
|
-
host: localhost
|
14
|
-
port: 5278
|
15
|
-
debug: true
|
16
|
-
|
17
|
-
test:
|
18
|
-
jid: component.server.com
|
19
|
-
password: password
|
20
|
-
host: localhost
|
21
|
-
port: 5278
|
22
|
-
debug: false
|
23
|
-
|
24
|
-
production:
|
25
|
-
jid: component.server.com
|
26
|
-
password: password
|
27
|
-
host: localhost
|
28
|
-
port: 5278
|
29
|
-
debug: false
|
@@ -1,15 +0,0 @@
|
|
1
|
-
# This determines the routing for your controllers
|
2
|
-
# The macthing is based on XPATH
|
3
|
-
|
4
|
-
# route_name:
|
5
|
-
# priority: if a stanza matches several XPath conditions, the one with the greater priority will be executed. If they have the same priority, one will be chose randomly, so choose your priority very carefully! It is a good practice to attribute a different priority to each of your routes, so that there is no conflict
|
6
|
-
# controller: name of the controller that should handle the stanza
|
7
|
-
# action: action to be called to handle the stanza
|
8
|
-
# xpath: used to macth the stanza
|
9
|
-
|
10
|
-
# my_route_1:
|
11
|
-
# priority: 1
|
12
|
-
# controller: main
|
13
|
-
# xpath: "//message"
|
14
|
-
# action: echo
|
15
|
-
|
@@ -1,47 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
|
3
|
-
##
|
4
|
-
# This is a generator that will build scaffold controller and add the corresponding routes
|
5
|
-
#
|
6
|
-
# == Usage
|
7
|
-
#
|
8
|
-
# $> scripts/generate controller_name [action_name:priority;Xpath, ...]
|
9
|
-
#
|
10
|
-
# == Example
|
11
|
-
#
|
12
|
-
# $> scripts/generate message echo:10:"//message/body" reverse:0:"//message"
|
13
|
-
#
|
14
|
-
# This will generate a an echo controller with 2 actions : echo and reverse. Echo will be called for stanza of type message witch a body than contains "echo", while reverse will be called for any other stanza of type message.
|
15
|
-
|
16
|
-
|
17
|
-
# Create the views subdirectory
|
18
|
-
Dir.mkdir "app/views/#{ARGV[0]}" if !File.exists?("app/views/#{ARGV[0]}")
|
19
|
-
# CReate the controller file and write the first lines
|
20
|
-
controller_file = File.open("app/controllers/#{ARGV[0]}_controller.rb", "w+")
|
21
|
-
route_file = File.open("config/routes.yaml", "a")
|
22
|
-
controller_file.puts "class #{ARGV[0].capitalize}Controller < Babylon::Base::Controller"
|
23
|
-
controller_file.puts ""
|
24
|
-
ARGV[1..-1].each do |action|
|
25
|
-
action_name, priority, xpath = action.split(":")
|
26
|
-
# Add the action to the controller
|
27
|
-
controller_file.puts ""
|
28
|
-
controller_file.puts " def #{action_name}"
|
29
|
-
controller_file.puts " # Called when the stanza matches #{xpath}. Priority #{priority}"
|
30
|
-
controller_file.puts " "
|
31
|
-
controller_file.puts " end"
|
32
|
-
# Create the file views
|
33
|
-
view_file = File.open("app/views/#{ARGV[0]}/#{action_name}.xml.builder", "w+")
|
34
|
-
view_file.puts "self.message(:to => to, :from => from, :type => :chat) do"
|
35
|
-
view_file.puts " self.body(content) # Same as self.send(:body, body) "
|
36
|
-
view_file.puts "end"
|
37
|
-
view_file.close
|
38
|
-
# And now add the route (we must be careful and not delete other routes)
|
39
|
-
route_file.puts ""
|
40
|
-
route_file.puts "#{ARGV[0]}_#{action_name}_route:"
|
41
|
-
route_file.puts " priority: #{priority}"
|
42
|
-
route_file.puts " controller: #{ARGV[0]}"
|
43
|
-
route_file.puts " xpath: #{xpath}"
|
44
|
-
route_file.puts " action: #{action_name}"
|
45
|
-
end
|
46
|
-
controller_file.puts "end"
|
47
|
-
|