skates 0.1.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. data/LICENSE +20 -0
  2. data/README.rdoc +113 -0
  3. data/Rakefile +143 -0
  4. data/bin/skates +6 -0
  5. data/lib/skates.rb +108 -0
  6. data/lib/skates/base/controller.rb +116 -0
  7. data/lib/skates/base/stanza.rb +23 -0
  8. data/lib/skates/base/view.rb +58 -0
  9. data/lib/skates/client_connection.rb +210 -0
  10. data/lib/skates/component_connection.rb +87 -0
  11. data/lib/skates/generator.rb +139 -0
  12. data/lib/skates/router.rb +101 -0
  13. data/lib/skates/router/dsl.rb +61 -0
  14. data/lib/skates/runner.rb +137 -0
  15. data/lib/skates/xmpp_connection.rb +172 -0
  16. data/lib/skates/xmpp_parser.rb +117 -0
  17. data/lib/skates/xpath_helper.rb +13 -0
  18. data/spec/bin/babylon_spec.rb +0 -0
  19. data/spec/em_mock.rb +42 -0
  20. data/spec/lib/babylon/base/controller_spec.rb +205 -0
  21. data/spec/lib/babylon/base/stanza_spec.rb +15 -0
  22. data/spec/lib/babylon/base/view_spec.rb +92 -0
  23. data/spec/lib/babylon/client_connection_spec.rb +304 -0
  24. data/spec/lib/babylon/component_connection_spec.rb +135 -0
  25. data/spec/lib/babylon/generator_spec.rb +10 -0
  26. data/spec/lib/babylon/router/dsl_spec.rb +72 -0
  27. data/spec/lib/babylon/router_spec.rb +189 -0
  28. data/spec/lib/babylon/runner_spec.rb +213 -0
  29. data/spec/lib/babylon/xmpp_connection_spec.rb +197 -0
  30. data/spec/lib/babylon/xmpp_parser_spec.rb +275 -0
  31. data/spec/lib/babylon/xpath_helper_spec.rb +25 -0
  32. data/spec/spec_helper.rb +34 -0
  33. data/templates/skates/app/controllers/controller.rb +7 -0
  34. data/templates/skates/app/stanzas/stanza.rb +6 -0
  35. data/templates/skates/app/views/view.rb +6 -0
  36. data/templates/skates/config/boot.rb +16 -0
  37. data/templates/skates/config/config.yaml +24 -0
  38. data/templates/skates/config/dependencies.rb +1 -0
  39. data/templates/skates/config/routes.rb +22 -0
  40. data/templates/skates/log/development.log +0 -0
  41. data/templates/skates/log/production.log +0 -0
  42. data/templates/skates/log/test.log +0 -0
  43. data/templates/skates/script/component +36 -0
  44. data/templates/skates/tmp/pids/README +2 -0
  45. data/test/skates_test.rb +7 -0
  46. data/test/test_helper.rb +10 -0
  47. metadata +160 -0
@@ -0,0 +1,117 @@
1
+ module Skates
2
+
3
+ ##
4
+ # This is the XML SAX Parser that accepts "pushed" content
5
+ class XmppParser < Nokogiri::XML::SAX::Document
6
+
7
+ attr_accessor :elem, :doc, :parser
8
+
9
+ ##
10
+ # Initialize the parser and adds the callback that will be called upon stanza completion
11
+ def initialize(callback)
12
+ @callback = callback
13
+ @buffer = ""
14
+ super()
15
+ reset
16
+ end
17
+
18
+ ##
19
+ # Resets the Pushed SAX Parser.
20
+ def reset
21
+ @parser = Nokogiri::XML::SAX::PushParser.new(self, "UTF-8")
22
+ @elem = @doc = nil
23
+ end
24
+
25
+ ##
26
+ # Pushes the received data to the parser. The parser will then callback the document's methods (start_tag, end_tag... etc)
27
+ def push(data)
28
+ @parser << data
29
+ end
30
+
31
+ ##
32
+ # Adds characters to the current element (being parsed)
33
+ def characters(string)
34
+ @buffer ||= ""
35
+ @buffer += string
36
+ end
37
+
38
+ ##
39
+ # Instantiate a new current Element, adds the corresponding attributes and namespaces.
40
+ # The new element is eventually added to a parent element (if present).
41
+ # If no element is being parsed, then, we create a new document, to which we add this new element as root. (we create one document per stanza to avoid memory problems)
42
+ def start_element(qname, attributes = [])
43
+ clear_characters_buffer
44
+ @doc ||= Nokogiri::XML::Document.new
45
+ @elem ||= @doc # If we have no current element, then, we take the doc
46
+ @elem = @elem.add_child(Nokogiri::XML::Element.new(qname, @doc))
47
+
48
+ add_namespaces_and_attributes_to_current_node(attributes)
49
+
50
+ if @elem.name == "stream:stream"
51
+ # We activate the callback since this element will never end.
52
+ @callback.call(@elem)
53
+ @doc = @elem = nil # Let's prepare for the next stanza
54
+ # And then, we start a new Sax Push Parser
55
+ end
56
+ end
57
+
58
+ ##
59
+ # Clears the characters buffer
60
+ def clear_characters_buffer
61
+ if @buffer && @elem
62
+ @buffer.strip!
63
+ @elem.add_child(Nokogiri::XML::Text.new(Skates.decode_xml(@buffer), @doc)) unless @buffer.empty?
64
+ @buffer = nil # empty the buffer
65
+ end
66
+ end
67
+
68
+ ##
69
+ # Terminates the current element and calls the callback
70
+ def end_element(name)
71
+ clear_characters_buffer
72
+ if @elem
73
+ if @elem.parent == @doc
74
+ # If we're actually finishing the stanza (a stanza is always a document's root)
75
+ @callback.call(@elem)
76
+ # We delete the current element and the doc (1 doc per stanza policy)
77
+ @elem = @doc = nil
78
+ else
79
+ @elem = @elem.parent
80
+ end
81
+ else
82
+ # Not sure what to do since it seems we're not processing any element at this time, so how can one end?
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ ##
89
+ # Adds namespaces and attributes. Nokogiri passes them as a array of [[ns_name, ns_url], [ns_name, ns_url]..., key, value, key, value]...
90
+ def add_namespaces_and_attributes_to_current_node(attrs)
91
+ # Namespaces
92
+ attrs.select {|k| k.is_a? Array}.each do |pair|
93
+ # set_namespace(pair[0], pair[1])
94
+ set_normal_attribute(pair[0], pair[1])
95
+ end
96
+ # Attributes
97
+ attrs.select {|k| k.is_a? String}.in_groups_of(2) do |pair|
98
+ set_normal_attribute(pair[0], pair[1])
99
+ end
100
+ end
101
+
102
+ def set_normal_attribute(key, value)
103
+ @elem.set_attribute key, Skates.decode_xml(value)
104
+ end
105
+
106
+ def set_namespace(key, value)
107
+ if key.include? ':'
108
+ @elem.add_namespace(key.split(':').last, value)
109
+ else
110
+ @elem.add_namespace(nil, value)
111
+ end
112
+ end
113
+
114
+
115
+
116
+ end
117
+ end
@@ -0,0 +1,13 @@
1
+ module Skates
2
+
3
+ # Custom XPath functions for stanza-routing.
4
+ class XpathHelper
5
+
6
+ # Match nodes of the given name with the given namespace URI.
7
+ def namespace(set, name, nsuri)
8
+ set.find_all do |n|
9
+ n.name == name && n.namespaces.values.include?(nsuri)
10
+ end
11
+ end
12
+ end
13
+ end
File without changes
data/spec/em_mock.rb ADDED
@@ -0,0 +1,42 @@
1
+ ##
2
+ # Mock for EventMachine
3
+ module EventMachine
4
+
5
+ def self.mock?
6
+ true
7
+ end
8
+
9
+ ##
10
+ # Mock for the Connection Class
11
+ class Connection
12
+ def self.new(*args)
13
+ allocate.instance_eval do
14
+ # Call a superclass's #initialize if it has one
15
+ initialize(*args)
16
+ # Store signature and run #post_init
17
+ post_init
18
+ self
19
+ end
20
+ end
21
+ end
22
+
23
+ ##
24
+ # Stub for run
25
+ def self.run(proc)
26
+ proc.call
27
+ end
28
+
29
+ ##
30
+ # Stub for epoll
31
+ def self.epoll; end
32
+
33
+ ##
34
+ # Stub! to stop the event loop.
35
+ def self.stop_event_loop; end
36
+
37
+ ##
38
+ # Stub for connect (should return a connection object)
39
+ def self.connect(host, port, handler, params)
40
+ handler.new(params)
41
+ end
42
+ end
@@ -0,0 +1,205 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ describe Skates::Base::Controller do
4
+
5
+ before(:each) do
6
+ Skates.views.stub!(:[]).and_return("") # Stubbing read for view
7
+ end
8
+
9
+ describe ".initialize" do
10
+ before(:each) do
11
+ @params = {:a => "a", :b => 1, :c => {:key => "value"}, :stanza => "<hello>world</hello>"}
12
+ end
13
+
14
+ it "should have a stanza instance" do
15
+ stanza = mock(Object)
16
+ c = Skates::Base::Controller.new(stanza)
17
+
18
+ c.instance_variables.should be_include "@stanza"
19
+ c.instance_variable_get("@stanza").should == stanza
20
+ end
21
+
22
+ it "should not be rendered yet" do
23
+ c = Skates::Base::Controller.new(@params)
24
+ c.rendered.should_not be_true
25
+ end
26
+ end
27
+
28
+ describe ".perform" do
29
+ before(:each) do
30
+ @action = :subscribe
31
+ params = {:stanza => "<hello>world</hello>"}
32
+ @controller = Skates::Base::Controller.new(params)
33
+ @controller.class.send(:define_method, @action) do # Defining the action method
34
+ # Do something
35
+ end
36
+ end
37
+
38
+ it "should setup the action to the param" do
39
+ @controller.perform(@action) do
40
+ # Do something
41
+ end
42
+ @controller.instance_variable_get("@action_name").should == @action
43
+ end
44
+
45
+ it "should call the action" do
46
+ @controller.should_receive(:send).with(@action).and_return()
47
+ @controller.perform(@action) do
48
+ # Do something
49
+ end
50
+ end
51
+
52
+ it "should write an error to the log in case of failure of the action" do
53
+ @controller.stub!(:send).with(@action).and_raise(StandardError)
54
+ Skates.logger.should_receive(:error)
55
+ @controller.perform(@action) do
56
+ # Do something
57
+ end
58
+ end
59
+
60
+ it "should call render" do
61
+ @controller.should_receive(:render)
62
+ @controller.perform(@action) do
63
+ # Do something
64
+ end
65
+ end
66
+ end
67
+
68
+ describe ".render" do
69
+ before(:each) do
70
+ @controller = Skates::Base::Controller.new({})
71
+ @controller.action_name = :subscribe
72
+ end
73
+
74
+ it "should assign a value to view" do
75
+ @controller.render
76
+ @controller.instance_variable_get("@view").should_not be_nil
77
+ end
78
+
79
+ describe "with :nothing option" do
80
+ it "should not render any file" do
81
+ @controller.should_not_receive(:render_for_file)
82
+ @controller.render :nothing => true
83
+ end
84
+ end
85
+
86
+ describe "with no option" do
87
+ it "should call render with default_file_name if no option is provided" do
88
+ @controller.should_receive(:default_template_name)
89
+ @controller.render
90
+ end
91
+ end
92
+
93
+ describe "with an :action option" do
94
+ it "should call render with the file name corresponding to the action given as option" do
95
+ action = :unsubscribe
96
+ @controller.should_receive(:default_template_name).with("#{action}")
97
+ @controller.render(:action => action)
98
+ end
99
+ end
100
+
101
+ describe "with a file option" do
102
+ it "should call render_for_file with the correct path if an option file is provided" do
103
+ file = "myfile"
104
+ @controller.should_receive(:render_for_file)
105
+ @controller.render(:file => file)
106
+ end
107
+ end
108
+
109
+ it "should not render twice when called twice" do
110
+ @controller.render
111
+ @controller.should_not_receive(:render_for_file)
112
+ @controller.render
113
+ end
114
+ end
115
+
116
+ describe ".assigns" do
117
+
118
+ before(:each) do
119
+ @stanza = mock(Skates::Base::Stanza)
120
+ @controller = Skates::Base::Controller.new(@stanza)
121
+ end
122
+
123
+ it "should be a hash" do
124
+ @controller.assigns.should be_an_instance_of(Hash)
125
+ end
126
+
127
+ it "should only contain the @stanza if the action hasn't been called yet" do
128
+ @controller.assigns.should_not be_empty
129
+ @controller.assigns["stanza"].should == @stanza
130
+ end
131
+
132
+ it "should return an hash containing all instance variables defined in the action" do
133
+ vars = {"a" => 1, "b" => "b", "c" => { "d" => 4}}
134
+ class MyController < Skates::Base::Controller
135
+ def do_something
136
+ @a = 1
137
+ @b = "b"
138
+ @c = { "d" => 4 }
139
+ end
140
+ end
141
+ @controller = MyController.new(@stanza)
142
+ @controller.do_something
143
+ @controller.assigns.should == vars.merge("stanza" => @stanza)
144
+ end
145
+ end
146
+
147
+ describe ".evaluate" do
148
+ before(:each) do
149
+ @controller = Skates::Base::Controller.new()
150
+ end
151
+
152
+ it "should evaluate the view" do
153
+ view = mock(Skates::Base::View)
154
+ response = "hello"
155
+ @controller.instance_variable_set("@view", view)
156
+ view.should_receive(:evaluate).and_return(response)
157
+ @controller.evaluate.should == response
158
+ end
159
+ end
160
+
161
+ describe ".view_path" do
162
+ it "should return complete file path to the file given in param" do
163
+ @controller = Skates::Base::Controller.new()
164
+ file_name = "myfile"
165
+ @controller.__send__(:view_path, file_name).should == File.join("app/views", "#{"Skates::Base::Controller".gsub("Controller","").downcase}", file_name)
166
+ end
167
+ end
168
+
169
+ describe ".default_template_name" do
170
+ before(:each) do
171
+ @controller = Skates::Base::Controller.new()
172
+ end
173
+
174
+ it "should return the view file name if a file is given in param" do
175
+ @controller.__send__(:default_template_name, "myaction").should == "myaction.xml.builder"
176
+ end
177
+
178
+ it "should return the view file name based on the action_name if no file has been given" do
179
+ @controller.action_name = "a_great_action"
180
+ @controller.__send__(:default_template_name).should == "a_great_action.xml.builder"
181
+ end
182
+ end
183
+
184
+ describe ".render_for_file" do
185
+
186
+ before(:each) do
187
+ @controller = Skates::Base::Controller.new()
188
+ @block = Proc.new {
189
+ # Do something
190
+ }
191
+ @controller.class.send(:define_method, "action") do # Defining the action method
192
+ # Do something
193
+ end
194
+ @controller.perform(:action, &@block)
195
+ @view = Skates::Base::View.new("path_to_a_file", {})
196
+ end
197
+
198
+ it "should instantiate a new view, with the file provided and the hashed_variables" do
199
+ Skates::Base::View.should_receive(:new).with("path_to_a_file",an_instance_of(Hash)).and_return(@view)
200
+ @controller.__send__(:render_for_file, "path_to_a_file")
201
+ end
202
+
203
+ end
204
+
205
+ end
@@ -0,0 +1,15 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ describe Skates::Base::Stanza do
4
+
5
+ describe "initialize" do
6
+ before(:each) do
7
+ @stanza_string = "<presence />"
8
+ @stanza = Skates::Base::Stanza.new(@stanza_string)
9
+ end
10
+
11
+ it "should call parse with the string passed to the builder"
12
+
13
+ end
14
+
15
+ end
@@ -0,0 +1,92 @@
1
+ require File.dirname(__FILE__) + '/../../../spec_helper'
2
+
3
+ describe Skates::Base::View do
4
+ describe ".initialize" do
5
+
6
+ before(:each) do
7
+ @view = Skates::Base::View.new("/a/path/to/a/view/file", {:a => "a", :b => 123, :c => {:d => "d", :e => "123"}})
8
+ end
9
+
10
+ it "should assign @view_template to path" do
11
+ @view.view_template == "/a/path/to/a/view/file"
12
+ end
13
+
14
+ it "should assign any variable passed in hash" do
15
+ {:a => "a", :b => 123, :c => {:d => "d", :e => "123"}}.each do |key, value|
16
+ @view.instance_variable_get("@#{key}").should == value
17
+ end
18
+ end
19
+ end
20
+
21
+ describe ".evaluate" do
22
+ before(:each) do
23
+ @view_template = "/a/path/to/a/view/file"
24
+ @view = Skates::Base::View.new(@view_template, {:a => "a", :b => 123, :c => {:d => "d", :e => "123"}})
25
+ @xml_string = <<-eoxml
26
+ xml.message(:to => "you", :from => "me", :type => :chat) do
27
+ xml.body("salut")
28
+ end
29
+ eoxml
30
+ Skates.views.stub!(:[]).with(@view_template).and_return(@xml_string)
31
+ end
32
+
33
+ it "should read the template file" do
34
+ Skates.views.should_receive(:[]).twice.with(@view_template).and_return(@xml_string)
35
+ @view.evaluate
36
+ end
37
+
38
+ it "should raise an error if the view file couldn't be found" do
39
+ Skates.views.stub!(:[]).with(@view_template).and_raise(nil)
40
+ lambda {
41
+ @view.evaluate
42
+ }.should raise_error(Skates::Base::ViewFileNotFound)
43
+ end
44
+
45
+ it "should return a Nokogiri NodeSet" do
46
+ Skates.views.stub!(:[]).with(@view_template).and_return(@xml_string)
47
+ @view.evaluate.should be_an_instance_of(Nokogiri::XML::NodeSet)
48
+ end
49
+
50
+ it "should call eval on the view file" do
51
+ Skates.views.stub!(:[]).with(@view_template).and_return(@xml_string)
52
+ @view.should_receive(:eval).with(@xml_string, an_instance_of(Binding), @view_template, 1)
53
+ @view.evaluate
54
+ end
55
+
56
+ it "should be able to access context's variables" do
57
+ @view = Skates::Base::View.new("/a/path/to/a/view/file", {:a => "a", :b => 123, :c => {:d => "d", :e => "123"}})
58
+ @view.instance_variable_get("@a").should == "a"
59
+ @view.instance_variable_get("@b").should == 123
60
+ @view.instance_variable_get("@c").should == {:e=>"123", :d=>"d"}
61
+ end
62
+ end
63
+
64
+ describe "render" do
65
+ before(:each) do
66
+ @view_template = "/a/path/to/a/view/file"
67
+ @view = Skates::Base::View.new(@view_template, {:a => "a", :b => 123, :c => {:d => "d", :e => "123"}})
68
+ @xml_string = <<-eoxml
69
+ xml.message(:to => "you", :from => "me", :type => :chat) do |message|
70
+ message.body("salut")
71
+ render(message, {:partial => "partial", :locals => {:subtitle => "bonjour monde"}})
72
+ end
73
+ eoxml
74
+ @partial_string = <<-eoxml
75
+ xml.title("hello word")
76
+ xml.subtitle(subtitle)
77
+ eoxml
78
+ Skates.views.stub!(:[]).with(@view_template).and_return(@xml_string)
79
+ Skates.views.stub!(:[]).with("/a/path/to/a/view/partial.xml.builder").and_return(@partial_string)
80
+ end
81
+
82
+ it "should render the partial in the right context" do
83
+ @view.evaluate.xpath("//message/title").text.should == "hello word"
84
+ end
85
+
86
+ it "should allocate the locals variables" do
87
+ @view.evaluate.xpath("//message/subtitle").text.should == "bonjour monde"
88
+ end
89
+
90
+ end
91
+
92
+ end