blather 0.2.1 → 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (84) hide show
  1. data/LICENSE +2 -0
  2. data/README.rdoc +54 -29
  3. data/Rakefile +94 -13
  4. data/VERSION.yml +4 -0
  5. data/examples/drb_client.rb +2 -4
  6. data/examples/echo.rb +13 -8
  7. data/examples/pubsub/cli.rb +64 -0
  8. data/examples/pubsub/ping_pong.rb +18 -0
  9. data/examples/pubsub/pubsub_dsl.rb +52 -0
  10. data/examples/pubsub_client.rb +39 -0
  11. data/examples/rosterprint.rb +14 -0
  12. data/examples/xmpp4r/echo.rb +35 -0
  13. data/ext/extconf.rb +65 -0
  14. data/lib/blather.rb +18 -121
  15. data/lib/blather/client.rb +13 -0
  16. data/lib/blather/client/client.rb +165 -0
  17. data/lib/blather/client/dsl.rb +99 -0
  18. data/lib/blather/client/pubsub.rb +53 -0
  19. data/lib/blather/client/pubsub/node.rb +27 -0
  20. data/lib/blather/core_ext/active_support.rb +1 -0
  21. data/lib/blather/core_ext/libxml.rb +7 -1
  22. data/lib/blather/errors.rb +39 -18
  23. data/lib/blather/errors/sasl_error.rb +87 -0
  24. data/lib/blather/errors/stanza_error.rb +262 -0
  25. data/lib/blather/errors/stream_error.rb +253 -0
  26. data/lib/blather/jid.rb +9 -16
  27. data/lib/blather/roster.rb +9 -0
  28. data/lib/blather/roster_item.rb +7 -4
  29. data/lib/blather/stanza.rb +19 -25
  30. data/lib/blather/stanza/disco.rb +9 -0
  31. data/lib/blather/stanza/disco/disco_info.rb +84 -0
  32. data/lib/blather/stanza/disco/disco_items.rb +59 -0
  33. data/lib/blather/stanza/iq.rb +16 -4
  34. data/lib/blather/stanza/iq/query.rb +6 -4
  35. data/lib/blather/stanza/iq/roster.rb +38 -38
  36. data/lib/blather/stanza/pubsub.rb +33 -0
  37. data/lib/blather/stanza/pubsub/affiliations.rb +52 -0
  38. data/lib/blather/stanza/pubsub/errors.rb +9 -0
  39. data/lib/blather/stanza/pubsub/event.rb +21 -0
  40. data/lib/blather/stanza/pubsub/items.rb +59 -0
  41. data/lib/blather/stanza/pubsub/owner.rb +9 -0
  42. data/lib/blather/stanza/pubsub/subscriptions.rb +57 -0
  43. data/lib/blather/stream.rb +125 -57
  44. data/lib/blather/stream/client.rb +26 -0
  45. data/lib/blather/stream/component.rb +34 -0
  46. data/lib/blather/stream/parser.rb +17 -27
  47. data/lib/blather/stream/resource.rb +21 -24
  48. data/lib/blather/stream/sasl.rb +60 -37
  49. data/lib/blather/stream/session.rb +12 -19
  50. data/lib/blather/stream/stream_handler.rb +39 -0
  51. data/lib/blather/stream/tls.rb +22 -18
  52. data/lib/blather/xmpp_node.rb +91 -17
  53. data/spec/blather/core_ext/libxml_spec.rb +58 -0
  54. data/spec/blather/errors/sasl_error_spec.rb +56 -0
  55. data/spec/blather/errors/stanza_error_spec.rb +148 -0
  56. data/spec/blather/errors/stream_error_spec.rb +114 -0
  57. data/spec/blather/errors_spec.rb +40 -0
  58. data/spec/blather/jid_spec.rb +0 -7
  59. data/spec/blather/roster_item_spec.rb +5 -0
  60. data/spec/blather/roster_spec.rb +6 -6
  61. data/spec/blather/stanza/discos/disco_info_spec.rb +207 -0
  62. data/spec/blather/stanza/discos/disco_items_spec.rb +136 -0
  63. data/spec/blather/stanza/iq/query_spec.rb +9 -2
  64. data/spec/blather/stanza/iq/roster_spec.rb +117 -1
  65. data/spec/blather/stanza/iq_spec.rb +29 -0
  66. data/spec/blather/stanza/presence/subscription_spec.rb +12 -1
  67. data/spec/blather/stanza/presence_spec.rb +29 -0
  68. data/spec/blather/stanza/pubsub/affiliations_spec.rb +46 -0
  69. data/spec/blather/stanza/pubsub/items_spec.rb +59 -0
  70. data/spec/blather/stanza/pubsub/subscriptions_spec.rb +63 -0
  71. data/spec/blather/stanza/pubsub_spec.rb +26 -0
  72. data/spec/blather/stanza_spec.rb +13 -1
  73. data/spec/blather/stream/client_spec.rb +787 -0
  74. data/spec/blather/stream/component_spec.rb +86 -0
  75. data/spec/blather/xmpp_node_spec.rb +75 -22
  76. data/spec/fixtures/pubsub.rb +157 -0
  77. data/spec/spec_helper.rb +6 -14
  78. metadata +86 -74
  79. data/CHANGELOG +0 -5
  80. data/Manifest +0 -47
  81. data/blather.gemspec +0 -41
  82. data/lib/blather/stanza/error.rb +0 -31
  83. data/spec/blather/stream_spec.rb +0 -462
  84. data/spec/build_safe.rb +0 -20
data/LICENSE CHANGED
@@ -1,3 +1,5 @@
1
+ Blather
2
+
1
3
  Copyright (c) 2008 Jeff Smick
2
4
 
3
5
  Permission is hereby granted, free of charge, to any person obtaining
@@ -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
- %w[rubygems blather].each { |r| require r }
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
- handle :subscription do |s|
41
- write(s.approve!) if s.request?
39
+ subscription :request? do |s|
40
+ write s.approve!
42
41
  end
43
42
 
44
43
  # Echo back what was said
45
- handle :message do |m|
46
- write(m.reply) if m.chat? && m.body
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
- * ?? (suggestions)
98
+ * More examples (Re-write XMPP4R examples into Blather)
56
99
 
57
100
  = License
58
101
 
59
- Copyright (c) 2008 Jeff Smick
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 'echoe'
2
- require 'hanna/rdoctask'
1
+ require 'rubygems'
2
+ require 'rake'
3
3
 
4
- Echoe.new('blather') do |p|
5
- p.author = 'Jeff Smick'
6
- p.email = 'sprsquish@gmail.com'
7
- p.url = 'http://github.com/sprsquish/blather/tree/master'
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
- p.project = 'squishtech'
10
- p.summary = 'An evented XMPP library written on EventMachine and libxml-ruby'
10
+ gem.email = 'sprsquish@gmail.com'
11
+ gem.homepage = 'http://github.com/sprsquish/blather'
12
+ gem.authors = ['Jeff Smick']
11
13
 
12
- p.runtime_dependencies = ['eventmachine', 'libxml-ruby >=0.9.7']
13
- p.rdoc_options += %w[-S -T hanna --main README.rdoc --exclude autotest]
14
+ gem.rubyforge_project = 'squishtech'
14
15
 
15
- p.test_pattern = 'spec/**/*_spec.rb'
16
- p.rcov_options = ['--exclude \/Library\/Ruby\/Gems,spec\/', '--xrefs']
17
- end
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
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 2
4
+ :patch: 2
@@ -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
- handle :ready do
6
- DRb.start_service 'druby://localhost:99843', self
7
- end
5
+ when_ready { DRb.start_service 'druby://localhost:99843', self }
@@ -1,13 +1,18 @@
1
- %w[rubygems lib/blather].each { |r| require r }
1
+ #!/usr/bin/env ruby
2
2
 
3
- setup 'echo@jabber.local', 'echo'
3
+ require 'blather/client'
4
4
 
5
- # Auto approve subscription requests
6
- handle :subscription do |s|
7
- write(s.approve!) if s.request?
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
- # Echo back what was said
11
- handle :message do |m|
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