blather 0.2.1 → 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +2 -0
- data/README.rdoc +54 -29
- data/Rakefile +94 -13
- data/VERSION.yml +4 -0
- data/examples/drb_client.rb +2 -4
- data/examples/echo.rb +13 -8
- data/examples/pubsub/cli.rb +64 -0
- data/examples/pubsub/ping_pong.rb +18 -0
- data/examples/pubsub/pubsub_dsl.rb +52 -0
- data/examples/pubsub_client.rb +39 -0
- data/examples/rosterprint.rb +14 -0
- data/examples/xmpp4r/echo.rb +35 -0
- data/ext/extconf.rb +65 -0
- data/lib/blather.rb +18 -121
- data/lib/blather/client.rb +13 -0
- data/lib/blather/client/client.rb +165 -0
- data/lib/blather/client/dsl.rb +99 -0
- data/lib/blather/client/pubsub.rb +53 -0
- data/lib/blather/client/pubsub/node.rb +27 -0
- data/lib/blather/core_ext/active_support.rb +1 -0
- data/lib/blather/core_ext/libxml.rb +7 -1
- data/lib/blather/errors.rb +39 -18
- data/lib/blather/errors/sasl_error.rb +87 -0
- data/lib/blather/errors/stanza_error.rb +262 -0
- data/lib/blather/errors/stream_error.rb +253 -0
- data/lib/blather/jid.rb +9 -16
- data/lib/blather/roster.rb +9 -0
- data/lib/blather/roster_item.rb +7 -4
- data/lib/blather/stanza.rb +19 -25
- data/lib/blather/stanza/disco.rb +9 -0
- data/lib/blather/stanza/disco/disco_info.rb +84 -0
- data/lib/blather/stanza/disco/disco_items.rb +59 -0
- data/lib/blather/stanza/iq.rb +16 -4
- data/lib/blather/stanza/iq/query.rb +6 -4
- data/lib/blather/stanza/iq/roster.rb +38 -38
- data/lib/blather/stanza/pubsub.rb +33 -0
- data/lib/blather/stanza/pubsub/affiliations.rb +52 -0
- data/lib/blather/stanza/pubsub/errors.rb +9 -0
- data/lib/blather/stanza/pubsub/event.rb +21 -0
- data/lib/blather/stanza/pubsub/items.rb +59 -0
- data/lib/blather/stanza/pubsub/owner.rb +9 -0
- data/lib/blather/stanza/pubsub/subscriptions.rb +57 -0
- data/lib/blather/stream.rb +125 -57
- data/lib/blather/stream/client.rb +26 -0
- data/lib/blather/stream/component.rb +34 -0
- data/lib/blather/stream/parser.rb +17 -27
- data/lib/blather/stream/resource.rb +21 -24
- data/lib/blather/stream/sasl.rb +60 -37
- data/lib/blather/stream/session.rb +12 -19
- data/lib/blather/stream/stream_handler.rb +39 -0
- data/lib/blather/stream/tls.rb +22 -18
- data/lib/blather/xmpp_node.rb +91 -17
- data/spec/blather/core_ext/libxml_spec.rb +58 -0
- data/spec/blather/errors/sasl_error_spec.rb +56 -0
- data/spec/blather/errors/stanza_error_spec.rb +148 -0
- data/spec/blather/errors/stream_error_spec.rb +114 -0
- data/spec/blather/errors_spec.rb +40 -0
- data/spec/blather/jid_spec.rb +0 -7
- data/spec/blather/roster_item_spec.rb +5 -0
- data/spec/blather/roster_spec.rb +6 -6
- data/spec/blather/stanza/discos/disco_info_spec.rb +207 -0
- data/spec/blather/stanza/discos/disco_items_spec.rb +136 -0
- data/spec/blather/stanza/iq/query_spec.rb +9 -2
- data/spec/blather/stanza/iq/roster_spec.rb +117 -1
- data/spec/blather/stanza/iq_spec.rb +29 -0
- data/spec/blather/stanza/presence/subscription_spec.rb +12 -1
- data/spec/blather/stanza/presence_spec.rb +29 -0
- data/spec/blather/stanza/pubsub/affiliations_spec.rb +46 -0
- data/spec/blather/stanza/pubsub/items_spec.rb +59 -0
- data/spec/blather/stanza/pubsub/subscriptions_spec.rb +63 -0
- data/spec/blather/stanza/pubsub_spec.rb +26 -0
- data/spec/blather/stanza_spec.rb +13 -1
- data/spec/blather/stream/client_spec.rb +787 -0
- data/spec/blather/stream/component_spec.rb +86 -0
- data/spec/blather/xmpp_node_spec.rb +75 -22
- data/spec/fixtures/pubsub.rb +157 -0
- data/spec/spec_helper.rb +6 -14
- metadata +86 -74
- data/CHANGELOG +0 -5
- data/Manifest +0 -47
- data/blather.gemspec +0 -41
- data/lib/blather/stanza/error.rb +0 -31
- data/spec/blather/stream_spec.rb +0 -462
- data/spec/build_safe.rb +0 -20
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -14,8 +14,6 @@ GitHub:: https://github.com/sprsquish/blather
|
|
14
14
|
|
15
15
|
RubyForge:: http://rubyforge.org/projects/squishtech/
|
16
16
|
|
17
|
-
Lighthouse:: http://squishtech.lighthouseapp.com/projects/20652-blather
|
18
|
-
|
19
17
|
== Author
|
20
18
|
|
21
19
|
Jeff Smick <sprsquish@gmail.com>
|
@@ -32,47 +30,74 @@ See the /examples directory for more advanced examples.
|
|
32
30
|
|
33
31
|
This will auto-accept any subscription requests and echo back any chat messages.
|
34
32
|
|
35
|
-
|
33
|
+
require 'rubygems'
|
34
|
+
require 'blather/client'
|
36
35
|
|
37
36
|
setup 'echo@jabber.local', 'echo'
|
38
37
|
|
39
38
|
# Auto approve subscription requests
|
40
|
-
|
41
|
-
write
|
39
|
+
subscription :request? do |s|
|
40
|
+
write s.approve!
|
42
41
|
end
|
43
42
|
|
44
43
|
# Echo back what was said
|
45
|
-
|
46
|
-
write
|
44
|
+
message :chat?, :body do |m|
|
45
|
+
write m.reply
|
47
46
|
end
|
48
|
-
|
47
|
+
|
48
|
+
== Handlers
|
49
|
+
|
50
|
+
Setup handlers by calling their names as methods.
|
51
|
+
|
52
|
+
# Will only be called for messages where #chat? responds positively
|
53
|
+
# and #body == 'exit'
|
54
|
+
message :chat?, :body => 'exit'
|
55
|
+
|
56
|
+
=== Handler Guards
|
57
|
+
|
58
|
+
Guards act like AND statements. Each condition must be met if the handler is to be used.
|
59
|
+
|
60
|
+
# Equivalent to saying (stanza.chat? && stanza.body)
|
61
|
+
message :chat?, :body
|
62
|
+
|
63
|
+
There are 5 different types of guards:
|
64
|
+
|
65
|
+
# Symbol
|
66
|
+
# Checks for a non-false reply to calling the symbol on the stanza
|
67
|
+
# Equivalent to stanza.chat?
|
68
|
+
message :chat?
|
69
|
+
|
70
|
+
# Hash with any value (:body => 'exit')
|
71
|
+
# Calls the key on the stanza and checks for equality
|
72
|
+
# Equivalent to stanza.body == 'exit'
|
73
|
+
message :body => 'exit'
|
74
|
+
|
75
|
+
# Hash with regular expression (:body => /exit/)
|
76
|
+
# Calls the key on the stanza and checks for a match
|
77
|
+
# Equivalent to stanza.body.match /exit/
|
78
|
+
message :body => /exit/
|
79
|
+
|
80
|
+
# Proc
|
81
|
+
# Calls the proc passing in the stanza
|
82
|
+
# Checks that the ID is modulo 3
|
83
|
+
message proc { |m| m.id % 3 == 0 }
|
84
|
+
|
85
|
+
# Array
|
86
|
+
# Use arrays with the previous types effectively turns the guard into
|
87
|
+
# an OR statement.
|
88
|
+
# Equivalent to stanza.body == 'foo' || stanza.body == 'baz'
|
89
|
+
message [{:body => 'foo'}, {:body => 'baz'}]
|
90
|
+
|
49
91
|
= TODO
|
50
92
|
|
51
93
|
* Cleanup API
|
94
|
+
* Add lambda callback ability to Iq stanzas
|
52
95
|
* Better Documentation
|
53
96
|
* Service Discovery (XEP-0030: http://xmpp.org/extensions/xep-0030.html)
|
54
97
|
* PubSub (XEP-0060: http://xmpp.org/extensions/xep-0060.html)
|
55
|
-
*
|
98
|
+
* More examples (Re-write XMPP4R examples into Blather)
|
56
99
|
|
57
100
|
= License
|
58
101
|
|
59
|
-
|
60
|
-
|
61
|
-
Permission is hereby granted, free of charge, to any person obtaining
|
62
|
-
a copy of this software and associated documentation files (the
|
63
|
-
"Software"), to deal in the Software without restriction, including
|
64
|
-
without limitation the rights to use, copy, modify, merge, publish,
|
65
|
-
distribute, sublicense, and/or sell copies of the Software, and to
|
66
|
-
permit persons to whom the Software is furnished to do so, subject to
|
67
|
-
the following conditions:
|
68
|
-
|
69
|
-
The above copyright notice and this permission notice shall be
|
70
|
-
included in all copies or substantial portions of the Software.
|
71
|
-
|
72
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
73
|
-
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
74
|
-
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
75
|
-
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
76
|
-
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
77
|
-
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
78
|
-
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
102
|
+
Please see LICENSE
|
103
|
+
The LibXML-Ruby license can be found in its directory
|
data/Rakefile
CHANGED
@@ -1,17 +1,98 @@
|
|
1
|
-
require '
|
2
|
-
require '
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
3
|
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
begin
|
5
|
+
require 'jeweler'
|
6
|
+
Jeweler::Tasks.new do |gem|
|
7
|
+
gem.name = 'blather'
|
8
|
+
gem.description = gem.summary = 'An evented XMPP library written on EventMachine and libxml-ruby'
|
8
9
|
|
9
|
-
|
10
|
-
|
10
|
+
gem.email = 'sprsquish@gmail.com'
|
11
|
+
gem.homepage = 'http://github.com/sprsquish/blather'
|
12
|
+
gem.authors = ['Jeff Smick']
|
11
13
|
|
12
|
-
|
13
|
-
p.rdoc_options += %w[-S -T hanna --main README.rdoc --exclude autotest]
|
14
|
+
gem.rubyforge_project = 'squishtech'
|
14
15
|
|
15
|
-
|
16
|
-
|
17
|
-
|
16
|
+
gem.extensions = %w[ext/extconf.rb]
|
17
|
+
|
18
|
+
gem.add_dependency 'eventmachine', '>= 0.12.6'
|
19
|
+
gem.add_dependency 'libxml-ruby', '>= 1.1.3'
|
20
|
+
|
21
|
+
# gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
|
22
|
+
end
|
23
|
+
rescue LoadError
|
24
|
+
puts "Jeweler (or a dependency) not available. Install it with: sudo gem install jeweler"
|
25
|
+
end
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
Rake::TestTask.new(:test) do |test|
|
29
|
+
test.libs << 'lib' << 'spec'
|
30
|
+
test.pattern = 'spec/**/*_spec.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
begin
|
35
|
+
require 'rcov/rcovtask'
|
36
|
+
Rcov::RcovTask.new do |test|
|
37
|
+
test.libs << 'spec'
|
38
|
+
test.pattern = 'spec/**/*_spec.rb'
|
39
|
+
test.rcov_opts += ['--exclude \/Library\/Ruby\/Gems,spec\/', '--xrefs']
|
40
|
+
test.verbose = true
|
41
|
+
end
|
42
|
+
rescue LoadError
|
43
|
+
task :rcov do
|
44
|
+
abort "RCov is not available. In order to run rcov, you must: sudo gem install spicycode-rcov"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
task :default => :test
|
50
|
+
|
51
|
+
begin
|
52
|
+
require 'hanna/rdoctask'
|
53
|
+
|
54
|
+
Rake::RDocTask.new do |rdoc|
|
55
|
+
if File.exist?('VERSION.yml')
|
56
|
+
config = YAML.load(File.read('VERSION.yml'))
|
57
|
+
version = "#{config[:major]}.#{config[:minor]}.#{config[:patch]}"
|
58
|
+
else
|
59
|
+
version = ""
|
60
|
+
end
|
61
|
+
|
62
|
+
rdoc.rdoc_dir = 'rdoc'
|
63
|
+
rdoc.title = "blather #{version}"
|
64
|
+
rdoc.rdoc_files.include('README*')
|
65
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
66
|
+
rdoc.options += %w[-S -T hanna --main README.rdoc --exclude autotest --exclude vendor]
|
67
|
+
end
|
68
|
+
rescue LoadError
|
69
|
+
task :rdoc do
|
70
|
+
abort "Hanna is not available. In order to use the Hanna, you must: sudo gem install mislav-hanna"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
desc 'Build extensions'
|
75
|
+
task :build => :extensions
|
76
|
+
task :extension => :build
|
77
|
+
|
78
|
+
ext = Config::CONFIG["DLEXT"]
|
79
|
+
task :extensions => ["ext/push_parser.#{ext}"]
|
80
|
+
file "ext/push_parser.#{ext}" =>
|
81
|
+
["Makefile"] + FileList["ext/*.{c,h}"].to_a do |t|
|
82
|
+
Dir.chdir("ext") { sh "make" }
|
83
|
+
end
|
84
|
+
|
85
|
+
namespace :extensions do
|
86
|
+
desc 'Clean extensions'
|
87
|
+
task :clean do
|
88
|
+
Dir.chdir("ext") do
|
89
|
+
sh "rm -f Makefile"
|
90
|
+
sh "rm -f *.{o,so,bundle,log}"
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
file "Makefile" => %w[ext/extconf.rb] do
|
96
|
+
command = ["ruby"] + $:.map{|dir| "-I#{File.expand_path dir}"} + ["extconf.rb"]
|
97
|
+
Dir.chdir("ext") { sh(*command) }
|
98
|
+
end
|
data/VERSION.yml
ADDED
data/examples/drb_client.rb
CHANGED
@@ -1,7 +1,5 @@
|
|
1
|
-
%w[rubygems lib/blather drb/drb].each { |r| require r }
|
1
|
+
%w[rubygems lib/blather/client drb/drb].each { |r| require r }
|
2
2
|
|
3
3
|
setup 'drb_client@jabber.local', 'drb_client'
|
4
4
|
|
5
|
-
|
6
|
-
DRb.start_service 'druby://localhost:99843', self
|
7
|
-
end
|
5
|
+
when_ready { DRb.start_service 'druby://localhost:99843', self }
|
data/examples/echo.rb
CHANGED
@@ -1,13 +1,18 @@
|
|
1
|
-
|
1
|
+
#!/usr/bin/env ruby
|
2
2
|
|
3
|
-
|
3
|
+
require 'blather/client'
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
when_ready { puts "Connected ! send messages to #{jid.stripped}." }
|
6
|
+
|
7
|
+
subscription :request? do |s|
|
8
|
+
write s.approve!
|
9
|
+
end
|
10
|
+
|
11
|
+
message :chat?, :body => 'exit' do |m|
|
12
|
+
say m.from, 'Exiting ...'
|
13
|
+
shutdown
|
8
14
|
end
|
9
15
|
|
10
|
-
|
11
|
-
|
12
|
-
write(m.reply) if m.chat? && m.body
|
16
|
+
message :chat?, :body do |m|
|
17
|
+
say m.from, "You sent: #{m.body}"
|
13
18
|
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
require 'blather/client'
|
4
|
+
|
5
|
+
#ls
|
6
|
+
#cd
|
7
|
+
#cat
|
8
|
+
#Blather::LOG.level = Logger::DEBUG
|
9
|
+
module CliHandler
|
10
|
+
include EM::Protocols::LineText2
|
11
|
+
|
12
|
+
def ls(node)
|
13
|
+
pubsub.node(parse_dir(node)) do |result|
|
14
|
+
cur = node.split('/')
|
15
|
+
puts
|
16
|
+
puts result.items.map { |i| (i.node.split('/') - cur).join('/') }.join("\n")
|
17
|
+
start_line
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def cd(node)
|
22
|
+
@node = parse_dir(node)
|
23
|
+
start_line
|
24
|
+
end
|
25
|
+
|
26
|
+
def cat(item)
|
27
|
+
end
|
28
|
+
|
29
|
+
def connect(who)
|
30
|
+
@who = who
|
31
|
+
puts "connected to '#{who}'"
|
32
|
+
end
|
33
|
+
|
34
|
+
def exit(_)
|
35
|
+
EM.stop
|
36
|
+
end
|
37
|
+
|
38
|
+
def initialize
|
39
|
+
$stdout.sync = true
|
40
|
+
@node = ''
|
41
|
+
@who = ''
|
42
|
+
start_line
|
43
|
+
end
|
44
|
+
|
45
|
+
def start_line
|
46
|
+
puts "\n/#{@node}> "
|
47
|
+
end
|
48
|
+
|
49
|
+
def receive_line(line)
|
50
|
+
return unless line =~ /(connect|exit|ls|cd|cat)\s?(.*)/i
|
51
|
+
__send__ $1, $2
|
52
|
+
end
|
53
|
+
|
54
|
+
def parse_dir(list)
|
55
|
+
return '' if list == '/'
|
56
|
+
cur = @node.split('/')
|
57
|
+
list.split('/').each { |dir| dir == '..' ? cur.pop : (cur << dir) }
|
58
|
+
cur * '/'
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
setup 'echo@jabber.local', 'echo'
|
63
|
+
pubsub_host 'pubsub.jabber.local'
|
64
|
+
when_ready { EM.open_keyboard CliHandler }
|
@@ -0,0 +1,18 @@
|
|
1
|
+
setup 'ping-pong@jabber.local', 'ping-pong'
|
2
|
+
|
3
|
+
pubsub.host = 'pubsub.jabber.local'
|
4
|
+
|
5
|
+
pubsub_event :node => 'ping' do |node|
|
6
|
+
pubsub.publish 'pong', node.payload
|
7
|
+
end
|
8
|
+
|
9
|
+
pubsub_event :node => 'pong' do |node|
|
10
|
+
x = node.payload.to_i
|
11
|
+
if x > 0
|
12
|
+
pubsub.publish 'ping', (x - 1)
|
13
|
+
else
|
14
|
+
shutdown
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
when_ready { pubsub.publish 'ping', 3 }
|
@@ -0,0 +1,52 @@
|
|
1
|
+
# Get affiliations
|
2
|
+
pubsub.affiliations do |aff|
|
3
|
+
aff == {
|
4
|
+
:member => [],
|
5
|
+
:none => [],
|
6
|
+
:outcast => [],
|
7
|
+
:owner => [],
|
8
|
+
:publisher => []
|
9
|
+
}
|
10
|
+
end
|
11
|
+
|
12
|
+
# Get subscriptions
|
13
|
+
pubsub.subscriptions do |sub|
|
14
|
+
sub == {
|
15
|
+
:none => [],
|
16
|
+
:pending => [],
|
17
|
+
:subscribed => [],
|
18
|
+
:unconfigured => []
|
19
|
+
}
|
20
|
+
end
|
21
|
+
|
22
|
+
# Get nodes
|
23
|
+
pubsub.nodes(path = nil) do |nodes|
|
24
|
+
nodes == [
|
25
|
+
DiscoItems::Item
|
26
|
+
.jid
|
27
|
+
.node
|
28
|
+
.name
|
29
|
+
]
|
30
|
+
end
|
31
|
+
|
32
|
+
# Get node
|
33
|
+
pubsub.node(path = nil) do |node|
|
34
|
+
node = Node
|
35
|
+
.attributes = {
|
36
|
+
[form data fields]
|
37
|
+
}
|
38
|
+
.type = '(leaf|collection)'
|
39
|
+
.feature = ''
|
40
|
+
.items(ids = [], :max => nil) { |list_of_items| }
|
41
|
+
end
|
42
|
+
|
43
|
+
# Get node items
|
44
|
+
pubsub.items(path = '' | ids = [], max = nil) do |node_items|
|
45
|
+
node_items = [
|
46
|
+
Item
|
47
|
+
.jid
|
48
|
+
.id
|
49
|
+
.name
|
50
|
+
.payload
|
51
|
+
]
|
52
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
# For subscribers
|
2
|
+
pubsub.nodes [path]
|
3
|
+
pubsub.node('path')
|
4
|
+
pubsub.node('path').subscribe
|
5
|
+
pubsub.node('path').affiliation
|
6
|
+
# For owners
|
7
|
+
pubsub.node('path').affiliations
|
8
|
+
pubsub.node('path').delete!
|
9
|
+
pubsub.node('path').purge!
|
10
|
+
pubsub.node('path').options
|
11
|
+
pubsub.node('path').defaults
|
12
|
+
pubsub.node('path').configure {}
|
13
|
+
|
14
|
+
pubsub.affiliations
|
15
|
+
|
16
|
+
pubsub.create 'node'
|
17
|
+
|
18
|
+
pubsub.publish 'node', 'content'
|
19
|
+
pubsub.delete 'node', 'item_id'
|
20
|
+
|
21
|
+
pubsub.subscriptions
|
22
|
+
pubsub.subscribe 'node'
|
23
|
+
pubsub.unsubscribe 'node'
|
24
|
+
|
25
|
+
# For subscribers
|
26
|
+
pubsub.subscriptions 'node'
|
27
|
+
pubsub.subscription 'node', [sub_id]
|
28
|
+
pubsub.subscription('node', [sub_id]).unsubscribe!
|
29
|
+
pubsub.subscription('node', [sub_id]).options
|
30
|
+
pubsub.subscription('node', [sub_id]).configure 'node', {}
|
31
|
+
# For owners
|
32
|
+
pubsub.subscriptions.pending
|
33
|
+
pubsub.subscription('sub_id').delete!
|
34
|
+
|
35
|
+
|
36
|
+
Node::Collection
|
37
|
+
nodes
|
38
|
+
Node::Leaf
|
39
|
+
items
|