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 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
- $> scripts/generate message echo:10:"//message[@type='chat']/body" subscribed:0:"//presence[@type='subscribe']"
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.yaml
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.5
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
- - Julien Genestoux
8
- - Astro
7
+ - julien Genestoux
9
8
  autorequire:
10
9
  bindir: bin
11
10
  cert_chain: []
12
11
 
13
- date: 2009-02-13 00:00:00 -08:00
14
- default_executable:
15
- dependencies:
16
- - !ruby/object:Gem::Dependency
17
- name: nokogiri
18
- type: :runtime
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
- - lib/babylon.rb
47
- - lib/babylon/base/controller.rb
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
- - spec
56
- - templates
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
@@ -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
@@ -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
@@ -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,13 +0,0 @@
1
- = Babylon::Base::Controller
2
-
3
- == Usage :
4
-
5
- Please see Babylon rdoc.
6
-
7
- == Example
8
-
9
- class MyController < Babylon::Base::Controller
10
- def my_action
11
- // Do something great!
12
- end
13
- 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,8 +0,0 @@
1
- require "rubygems"
2
- require "babylon"
3
-
4
- # Load the controllers
5
- Dir.glob(File.join(File.dirname(__FILE__), '../app/controllers/*_controller.rb')).each {|f| require f }
6
-
7
- # And start the App
8
- Babylon::Runner::run()
@@ -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,2 +0,0 @@
1
- #!/usr/bin/env ruby
2
- require File.dirname(__FILE__) + '/../config/boot'
@@ -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
-