radamanthus-skates 0.3.5

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 ADDED
@@ -0,0 +1,113 @@
1
+ = Skates
2
+
3
+ == DESCRIPTION:
4
+
5
+ Skates is a framework to build XMPP Applications in Ruby. The framework uses EventMachine to handle network connections.
6
+
7
+ This framework can use both an XMPP Component (XEP-0114) and an XMPP Client (and XMPP Servers should come soon). However, we strongly discourage any production application using a regular client.
8
+
9
+ == FEATURES/PROBLEMS:
10
+
11
+ Please link "report/request here.":http://github.com/julien51/skates/issues
12
+
13
+ The current version is a good candidate for version 0.1. We will probably not add any important features before that release, but we need some help with the tests and documentation.
14
+
15
+ == ROADMAP :
16
+
17
+ - Implement the ServerConnection for S2S
18
+ - Implement templates for disco-info... and common XEPs
19
+ - Delete route priorities? And rely on the order only? (As Rails does)
20
+ - Review doc
21
+ - Write tests
22
+ - Evangelize!
23
+
24
+ You can help with at least one of these points, don't turn your back on Skates!
25
+
26
+ == DOCUMENTATION :
27
+
28
+ You can find it on our "Rubyforge page":http://skates.rubyforge.org/
29
+ Feel free to fork the "github":http://github.com/julien51/skates/tree/master repo and add some documentation if you think anything is missing.
30
+
31
+ __You can't be a bad coder, a bad tester and a bad documenter at the same time ;)__
32
+
33
+ == SYNOPSIS :
34
+
35
+ You can build applications directly with Skates, or you can use the Skates::ClientConnection and Skates::ComponentConnection to create simple apps, but you will then have to handle stanza routing and creation yourself. You can also use these classes in external gems.
36
+
37
+ === To create an Application with Skates:
38
+
39
+ 1. Install the gem
40
+ 2. The app contains a generator that will "build" a scaffold for your application.
41
+
42
+ $> skates application __myapp__
43
+
44
+ 3. Use the generator or write your own controllers :
45
+
46
+ $> skates controller __messages__ __echo__:__10__:__//stream:message[@type='chat']/body__,__subscribed__:__0__:__//stream:presence[@type='subscribe']__
47
+
48
+ This will generate a __MessagesController__ class with 2 methods : __echo__ and __subscribed__.
49
+ - "echo" will be called when the component receives message stanzas of type 'chat',
50
+ - "subscribed" will be called for presence stanzas of type 'subscribe'.
51
+ 10 and 0 are the priority : useful when a stanza matches 2 XPath.
52
+
53
+ Each of these actions will be called with stanza objects. You have to define your own objects in __stanzas/echo.rb__ and __stanzas/subscribed.rb__
54
+ By implementing them, you can choose which elements and attributes you want to have access to. These attributes will be populated upon instantiation of the Stanza objects.
55
+
56
+ This will also generate 2 'views' used to build your responses stanzas.
57
+
58
+ And finally, this will write 2 routes in the config/routes.rb
59
+
60
+ 4. Customize your controllers, stanzas and views!
61
+
62
+ 5. Make sure that the XMPP settings are correct in config/config.yaml.
63
+
64
+ 6. And finally start the component :
65
+
66
+ script/component
67
+
68
+ 7. We running in production, you may run the script/component too, but with something like runit to deamonize the process.
69
+
70
+ === To use the Connection Classes only (Client or Component), you can just call the following :
71
+
72
+ Skates::ClientConnection.connect(params, handler)
73
+ or,
74
+ Skates::ComponentConnection.connect(params, handler)
75
+
76
+ where params is a hash for all the necessary information to connect, and handler is an object that will receive the callbacks. Right now 3 callbacks are supported:
77
+
78
+ on_connected(connection), on_disconnected and on_stanza(stanza)
79
+
80
+ == ADDITIONAL INFORMATION :
81
+
82
+ Feel free to pull, branch, improve and commit the {code|specs|tests|docs} : we will merge it if it's a step ahead!
83
+
84
+ Skates's edge versions are located at Github : http://github.com/julien51/skates/tree/master
85
+
86
+ == REQUIREMENTS :
87
+
88
+ Gems : Eventmachine, nokogiri, YAML, log4r, templater
89
+
90
+ == LICENSE:
91
+
92
+ (The MIT License)
93
+
94
+ Copyright (c) 2009 Julien Genestoux http://notifixio.us
95
+
96
+ Permission is hereby granted, free of charge, to any person obtaining
97
+ a copy of this software and associated documentation files (the
98
+ 'Software'), to deal in the Software without restriction, including
99
+ without limitation the rights to use, copy, modify, merge, publish,
100
+ distribute, sublicense, and/or sell copies of the Software, and to
101
+ permit persons to whom the Software is furnished to do so, subject to
102
+ the following conditions:
103
+
104
+ The above copyright notice and this permission notice shall be
105
+ included in all copies or substantial portions of the Software.
106
+
107
+ THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
108
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
109
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
110
+ IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
111
+ CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
112
+ TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
113
+ SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/Rakefile ADDED
@@ -0,0 +1,142 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "skates"
8
+ gem.summary = %Q{Skates is a framework to create EventMachine based XMPP External Components in Ruby.}
9
+ gem.email = "julien.genestoux@gmail.com"
10
+ gem.homepage = "http://github.com/julien51/skates"
11
+ gem.authors = ["julien Genestoux"]
12
+
13
+ gem.add_dependency('eventmachine', ">= 0.12.10")
14
+ gem.add_dependency('log4r')
15
+ gem.add_dependency('nokogiri', "= 1.4.2")
16
+ gem.add_dependency('utf8cleaner')
17
+ gem.requirements = ["eventmachine", "yaml", "fileutils", "log4r", "nokogiri", "optparse", "digest/sha1", "base64", "resolv", "utf8cleaner"]
18
+ gem.executables = "skates"
19
+ gem.files = [ "bin/skates",
20
+ "lib/skates.rb",
21
+ "lib/skates/ext/array.rb",
22
+ "lib/skates/base/controller.rb",
23
+ "lib/skates/base/view.rb",
24
+ "lib/skates/base/stanza.rb",
25
+ "lib/skates/client_connection.rb",
26
+ "lib/skates/component_connection.rb",
27
+ "lib/skates/router/dsl.rb",
28
+ "lib/skates/router.rb",
29
+ "lib/skates/runner.rb",
30
+ "lib/skates/generator.rb",
31
+ "lib/skates/xmpp_connection.rb",
32
+ "lib/skates/xmpp_parser.rb",
33
+ "LICENSE",
34
+ "Rakefile",
35
+ "README.rdoc",
36
+ "templates/skates/app/controllers/controller.rb",
37
+ "templates/skates/app/views/view.rb",
38
+ "templates/skates/app/stanzas/stanza.rb",
39
+ "templates/skates/config/boot.rb",
40
+ "templates/skates/config/config.yaml",
41
+ "templates/skates/config/dependencies.rb",
42
+ "templates/skates/config/routes.rb",
43
+ "templates/skates/script/component",
44
+ "templates/skates/log/test.log",
45
+ "templates/skates/log/development.log",
46
+ "templates/skates/log/production.log",
47
+ "templates/skates/tmp/pids/README"
48
+ ]
49
+ gem.rubyforge_project = 'skates'
50
+ end
51
+ rescue LoadError
52
+ puts "Jeweler not available. Install it with: sudo gem jeweler"
53
+ end
54
+
55
+ require 'rake/rdoctask'
56
+ Rake::RDocTask.new do |rdoc|
57
+ rdoc.rdoc_dir = 'rdoc'
58
+ rdoc.title = 'Skates : a framework to create EventMachine based XMPP External Components in Ruby.'
59
+ rdoc.rdoc_files.include('README*')
60
+ rdoc.rdoc_files.include('lib/**/*.rb')
61
+ rdoc.options << '--line-numbers'
62
+ end
63
+
64
+ require 'rake/testtask'
65
+ Rake::TestTask.new(:test) do |test|
66
+ test.libs << 'lib' << 'test'
67
+ test.pattern = 'test/**/*_test.rb'
68
+ test.verbose = false
69
+ end
70
+
71
+ begin
72
+ require 'rcov/rcovtask'
73
+ Rcov::RcovTask.new do |test|
74
+ test.libs << 'test'
75
+ test.pattern = 'test/**/*_test.rb'
76
+ test.verbose = true
77
+ end
78
+ rescue LoadError
79
+ task :rcov do
80
+ abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
81
+ end
82
+ end
83
+
84
+ begin
85
+ require 'spec/rake/spectask'
86
+ desc "Run all Spec"
87
+ Spec::Rake::SpecTask.new('spec') do |spec|
88
+ spec.spec_files = FileList['spec/**/*.rb']
89
+ spec.verbose = true
90
+ spec.warning = false
91
+ spec.rcov = true
92
+ end
93
+ rescue LoadError
94
+ task :spec do
95
+ abort "Rspec is not available. In order to run rspec, you must: sudo gem install rspec"
96
+ end
97
+ end
98
+
99
+ begin
100
+ require 'spec/rake/verify_rcov'
101
+
102
+ RCov::VerifyTask.new(:verify_rcov => 'spec') do |t|
103
+ t.threshold = 100.0
104
+ t.index_html = 'coverage/index.html'
105
+ end
106
+ rescue LoadError
107
+ task :spec do
108
+ abort "Rcov is not available. In order to run rcov, you must: sudo gem install rcov"
109
+ end
110
+ end
111
+
112
+ # These are Rubyforge tasks
113
+ begin
114
+ require 'rake/contrib/sshpublisher'
115
+ namespace :rubyforge do
116
+
117
+ desc "Release gem and RDoc documentation to RubyForge"
118
+ task :release => ["rubyforge:release:gem", "rubyforge:release:docs"]
119
+
120
+ namespace :release do
121
+ desc "Publish RDoc to RubyForge."
122
+ task :docs => [:rdoc] do
123
+ config = YAML.load(
124
+ File.read(File.expand_path('~/.rubyforge/user-config.yml'))
125
+ )
126
+
127
+ host = "#{config['username']}@rubyforge.org"
128
+ remote_dir = "/var/www/gforge-projects/skates"
129
+ local_dir = 'rdoc'
130
+
131
+ Rake::SshDirPublisher.new(host, remote_dir, local_dir).upload
132
+ end
133
+ end
134
+ end
135
+ rescue LoadError
136
+ puts "Rake SshDirPublisher is unavailable or your rubyforge environment is not configured."
137
+ end
138
+
139
+
140
+ task :install => :build
141
+
142
+ task :default => :test
data/bin/skates ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'rubygems'
4
+ require 'templater'
5
+ require 'skates'
6
+ require 'skates/generator'
7
+
8
+ Skates::Generator.run_cli(Dir.pwd, 'skates_app', "0.1", ARGV)
data/lib/skates.rb ADDED
@@ -0,0 +1,154 @@
1
+ $:.unshift(File.dirname(__FILE__)) unless
2
+ $:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
3
+
4
+ require 'eventmachine'
5
+ require 'log4r'
6
+ require 'nokogiri'
7
+ require 'yaml'
8
+ require 'fileutils'
9
+ require 'digest/sha1'
10
+ require 'base64'
11
+ require 'resolv'
12
+ require 'cgi'
13
+ require 'utf8cleaner'
14
+ require 'pathname'
15
+
16
+ require 'skates/ext/array.rb'
17
+ require 'skates/xmpp_connection'
18
+ require 'skates/xmpp_parser'
19
+ require 'skates/component_connection'
20
+ require 'skates/client_connection'
21
+ require 'skates/router'
22
+ require 'skates/runner'
23
+ require 'skates/base/controller'
24
+ require 'skates/base/view'
25
+ require 'skates/base/stanza'
26
+
27
+ # Skates is a XMPP Component Framework based on EventMachine. It uses the Nokogiri GEM, which is a Ruby wrapper for Libxml2.
28
+ # It implements the MVC paradigm.
29
+ # You can create your own application by running :
30
+ # $> skates app_name
31
+ # This will generate some folders and files for your application. Please see README.rdoc for further instructions
32
+
33
+ module Skates
34
+
35
+ @@config_file = nil
36
+
37
+ def self.environment=(_env)
38
+ @@env = _env
39
+ end
40
+
41
+ def self.environment
42
+ unless self.class_variable_defined?("@@env")
43
+ @@env = "development"
44
+ end
45
+ @@env
46
+ end
47
+
48
+ ##
49
+ # Sets up the router
50
+ def self.router=(router)
51
+ @@router = router
52
+ end
53
+
54
+ ##
55
+ # Retruns the router
56
+ def self.router
57
+ unless self.class_variable_defined?("@@router")
58
+ @@router = nil
59
+ end
60
+ @@router
61
+ end
62
+
63
+ ##
64
+ # Caches the view files to improve performance.
65
+ def self.cache_views
66
+ @@views= {}
67
+ Dir.glob('app/views/*/*').each do |f|
68
+ @@views[f] = File.read(f)
69
+ end
70
+ end
71
+
72
+ def self.views
73
+ unless self.class_variable_defined?("@@views")
74
+ @@views= {}
75
+ end
76
+ @@views
77
+ end
78
+
79
+ ##
80
+ # Returns a shared logger for this component.
81
+ def self.logger
82
+ unless self.class_variable_defined?("@@logger")
83
+ reopen_logs
84
+ end
85
+ @@logger
86
+ end
87
+
88
+ ##
89
+ # Returns the log directory
90
+ def self.log_dir
91
+ unless self.class_variable_defined?("@@log_dir")
92
+ @@log_dir = "log"
93
+ end
94
+ @@log_dir
95
+ end
96
+
97
+ def self.log_dir=(log_dir)
98
+ @@log_dir = log_dir
99
+ reopen_logs
100
+ end
101
+
102
+ ##
103
+ # Re-opens the logs
104
+ # In "development" environment, the log will be on stdout
105
+ def self.reopen_logs
106
+ # Open a new logger
107
+ logger = Log4r::Logger.new("")
108
+ logger.add(Log4r::Outputter.stdout) if Skates.environment == "development"
109
+ log_file = Log4r::RollingFileOutputter.new("#{Skates.environment}", :filename => "#{Skates.log_dir}/#{Skates.environment}.log", :trunc => false)
110
+ case Skates.environment
111
+ when "production"
112
+ log_file.formatter = Log4r::PatternFormatter.new(:pattern => "%d (#{Process.pid}) [%l] :: %m #{Skates.router.nil? ? "" : (Skates.router.connection.nil? ? "" : "(" + Skates.router.connection.jid + ")") }", :date_pattern => "%d/%m %H:%M")
113
+ when "development"
114
+ log_file.formatter = Log4r::PatternFormatter.new(:pattern => "%d (#{Process.pid}) [%l] :: %m #{Skates.router.nil? ? "" : (Skates.router.connection.nil? ? "" : "(" + Skates.router.connection.jid + ")") }", :date_pattern => "%d/%m %H:%M")
115
+ else
116
+ log_file.formatter = Log4r::PatternFormatter.new(:pattern => "%d (#{Process.pid}) [%l] :: %m #{Skates.router.nil? ? "" : (Skates.router.connection.nil? ? "" : "(" + Skates.router.connection.jid + ")") }", :date_pattern => "%d/%m %H:%M")
117
+ end
118
+ logger.add(log_file)
119
+ # Set up the variable.
120
+ @@logger = logger
121
+ end
122
+
123
+ ##
124
+ # Set the configuration file for this component.
125
+ def self.config_file=(file)
126
+ @@config_file = file
127
+ end
128
+
129
+ ##
130
+ # Return the configuration file for this component.
131
+ def self.config_file
132
+ @@config_file
133
+ end
134
+
135
+ ##
136
+ # Set the configuration for this component.
137
+ def self.config=(conf)
138
+ @@config = conf
139
+ end
140
+
141
+ ##
142
+ # Return the configuration for this component.
143
+ def self.config
144
+ @@config
145
+ end
146
+
147
+ ##
148
+ # Decodes XML special characters.
149
+ def self.decode_xml(str)
150
+ CGI.unescapeHTML(str)
151
+ end
152
+
153
+ end
154
+
@@ -0,0 +1,116 @@
1
+ module Skates
2
+ module Base
3
+
4
+ ##
5
+ # Your application's controller should be descendant of this class.
6
+ class Controller
7
+
8
+ attr_accessor :stanza, :rendered, :action_name # Stanza received by the controller (Nokogiri::XML::Node)
9
+
10
+ ##
11
+ # Creates a new controller (you should not override this class) and assigns the stanza as well as any other value of the hash to instances named after the keys of the hash.
12
+ def initialize(stanza = nil)
13
+ @stanza = stanza
14
+ @view = nil
15
+ end
16
+
17
+ ##
18
+ # Performs the action and calls back the optional block argument : you should not override this function
19
+ def perform(action)
20
+ @action_name = action
21
+ begin
22
+ self.send(@action_name)
23
+ rescue
24
+ Skates.logger.error {
25
+ "#{$!}:\n#{$!.backtrace.join("\n")}"
26
+ }
27
+ end
28
+ self.render
29
+ end
30
+
31
+ ##
32
+ # Returns the list of variables assigned during the action.
33
+ def assigns
34
+ vars = Hash.new
35
+ instance_variables.each do |var|
36
+ if !["@view", "@action_name", "@block"].include? var
37
+ vars[var[1..-1]] = instance_variable_get(var)
38
+ end
39
+ end
40
+ vars
41
+ end
42
+
43
+ ##
44
+ # Called by default after each action to "build" a XMPP stanza. By default, it will use the /controller_name/action.xml.builder
45
+ # You can use the following options :
46
+ # - :file : render a specific file (can be in a different controller)
47
+ # - :action : render another action of the current controller
48
+ # - :nothing : doesn't render anything
49
+ def render(options = {})
50
+ return if @view and !options[:force] # Avoid double rendering, if we have already attached a view
51
+
52
+ if options == {} # default rendering
53
+ render(:file => default_template_name)
54
+
55
+ elsif action_name = options[:action]
56
+ result = render(:file => default_template_name(action_name.to_s))
57
+
58
+ elsif options[:file]
59
+ file = options[:file]
60
+ if file =~ /^\// # Render from view root
61
+ @view = render_for_file(File.join("app", "views", "#{file}.xml.builder"))
62
+ else
63
+ @view = render_for_file(view_path(file))
64
+ end
65
+
66
+ elsif options[:nothing]
67
+ @view = Skates::Base::View.new()
68
+ end
69
+ end
70
+
71
+ ##
72
+ # Actually evaluates the view
73
+ def evaluate
74
+ @view.evaluate if @view
75
+ end
76
+
77
+ ##
78
+ # Render and evaluate a given view
79
+ def render_and_evaluate(options = {})
80
+ render(options)
81
+ evaluate
82
+ end
83
+
84
+ ##
85
+ # Renders, evaluate and sends
86
+ def render_evaluate_and_send(options)
87
+ response = render_and_evaluate(options)
88
+ Skates.router.connection.send_xml response
89
+ end
90
+
91
+ protected
92
+
93
+ ##
94
+ # Builds the view path.
95
+ def view_path(file_name)
96
+ File.join("app", "views", "#{self.class.name.gsub("Controller","").downcase}", file_name)
97
+ end
98
+
99
+ ##
100
+ # Default template name used to build stanzas
101
+ def default_template_name(action_name = nil)
102
+ "#{action_name || @action_name}.xml.builder"
103
+ end
104
+
105
+ ##
106
+ # Creates the view and "evaluates" it to build the XML for the stanza
107
+ def render_for_file(file)
108
+ Skates.logger.info {
109
+ "RENDERING : #{file}"
110
+ }
111
+ Skates::Base::View.new(file, assigns)
112
+ end
113
+
114
+ end
115
+ end
116
+ end