firering 0.1.1 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.rdoc CHANGED
@@ -4,31 +4,29 @@ Campfire API interface powered by EventMachine and Yajl.
4
4
 
5
5
  require 'firering'
6
6
 
7
- Firering.subdomain = "subdomain"
8
- Firering.token = "token"
7
+ print "Enter subdomain: "; subdomain = gets.chomp
8
+ print "Enter user: " ; login = gets.chomp
9
+ print "Enter password: " ; password = gets.chomp
9
10
 
10
- EM.run do
11
- Firering.rooms do |rooms|
12
- rooms.each do |room|
13
- if room.name == "RoomName"
14
-
15
- puts "Joining #{room.name}"
11
+ conn = Firering::Connection.new("http://#{subdomain}.campfirenow.com") do |c|
12
+ c.login = login
13
+ c.password = password
14
+ end
16
15
 
17
- Firering.room_join(room.id) do
18
- Firering.stream(room.id) do |message|
16
+ EM.run do
17
+ conn.authenticate do |user|
18
+ conn.rooms do |rooms|
19
19
 
20
- if message.from_user?
21
- Firering.user(message.user_id) do |user|
22
- puts "( #{user.name} ) #{message.body}"
23
- end
24
- else
25
- puts message.body if message.body.to_s !~ /^\s*$/
26
- end
20
+ rooms.each do |room|
21
+ if room.name == "Room Name"
27
22
 
23
+ room.stream do |message|
24
+ message.user { |user| puts "#{user}: #{message}" }
28
25
  end
29
- end
30
26
 
27
+ end
31
28
  end
29
+
32
30
  end
33
31
  end
34
32
 
@@ -37,10 +35,10 @@ Campfire API interface powered by EventMachine and Yajl.
37
35
 
38
36
  = campf-notify
39
37
 
40
- The gem bundles an executable script for spawning libnotify powered notifications.
41
- To be able to use it, check your distro package repositories for the apropriate
42
- package containing the "notify-send" command line utility. In the case of archlinux,
43
- the package name is "libnotify".
38
+ The gem bundles an executable script for spawning libnotify powered
39
+ notifications. To be able to use it, check your distro package repositories
40
+ for the apropriate package containing the "notify-send" command line utility.
41
+ In the case of archlinux, the package name is "libnotify".
44
42
 
45
43
  The script needs the following environment variables in place:
46
44
 
@@ -55,10 +53,12 @@ And watch the lovely notifications each time something is posted to a room.
55
53
 
56
54
  == Running the specs
57
55
 
58
- For running the specs, I opted for running a sinatra application for serving
59
- the fixture data. The server runs automatically when "rake spec" is executed,
60
- it runs on the standard sinatra port (4567), so you should make sure that
61
- port is free when running the specs.
56
+ When the specs are run a process is forked where a Rack application is run.
57
+ This rack application serves all the fixtured responses that mimic the working
58
+ of the real campfire app. The only caveat here is you may get a conflict if you
59
+ are already running something on the port (8909). If this happens you'll need
60
+ to open the specs/spec_helper.rb file and change the port used to run the
61
+ fixtures server.
62
62
 
63
63
  For more details take a look at spec/fixtures/load_server.rb file.
64
64
 
data/Rakefile CHANGED
@@ -92,7 +92,7 @@ task :release => :build do
92
92
  sh "git commit --allow-empty -a -m 'Release #{version}'"
93
93
  sh "git tag v#{version}"
94
94
  sh "git push origin master"
95
- sh "git push v#{version}"
95
+ sh "git push --tags"
96
96
  sh "gem push pkg/#{name}-#{version}.gem"
97
97
  end
98
98
 
data/bin/campf-notify CHANGED
@@ -3,51 +3,45 @@
3
3
  require 'firering'
4
4
  require 'shellwords'
5
5
 
6
- Firering.subdomain = ENV["CAMPFIRE_SUBDOMAIN"].to_s
7
- Firering.token = ENV["CAMPFIRE_TOKEN"].to_s
6
+ subdomain, token = ENV["CAMPFIRE_SUBDOMAIN"], ENV["CAMPFIRE_TOKEN"]
7
+ room_name, icon_path = ARGV
8
8
 
9
- room_name = ARGV[0].to_s
10
- icon_path = ARGV[1].to_s
11
-
12
- if Firering.subdomain =~ /^\s*$/ || Firering.token =~ /^\s*$/ || room_name =~ /^\s*$/ || !File.exists?(icon_path)
13
- puts "USAGE: campf-notify room-name notify-send-icon-path"
9
+ if subdomain.to_s =~ /^\s*$/ || token.to_s =~ /^\s*$/ || room_name.to_s =~ /^\s*$/ || icon_path.nil? || !File.file?(icon_path)
10
+ puts "USAGE: campf-notify room-name icon-path"
11
+ puts
12
+ puts "Icon path #{icon_path} is incorrect" unless icon_path && File.exists?(icon_path)
13
+ puts "Room name #{room_name} is missing" unless room_name =~ /\S/
14
14
  puts
15
- puts "Icon path #{icon_path} is incorrect\n" unless File.exists?(icon_path)
16
- puts "Room name #{room_name} is missing" unless room_name !~ /^\s*$/
17
-
18
15
  puts "The following environment variables are needed: "
19
16
  puts
20
- puts "CAMPFIRE_SUBDOMAIN: #{Firering.subdomain}"
21
- puts "CAMPFIRE_TOKEN: #{Firering.token}"
17
+ puts "CAMPFIRE_SUBDOMAIN: #{subdomain || 'missing!'}"
18
+ puts "CAMPFIRE_TOKEN: #{token || 'missing!'}"
22
19
  puts
23
- exit
20
+ exit 1
24
21
  else
25
- puts "connecting to #{Firering.subdomain}.#{Firering::HTTP.host}"
22
+ host = "http://#{subdomain}.campfirenow.com"
23
+ puts "connecting to #{host}"
26
24
  end
27
25
 
28
- EM.run do
29
- Firering.rooms do |rooms|
30
- rooms.each do |room|
31
- if room.name == room_name
26
+ conn = Firering::Connection.new(host) {|c| c.token = token }
32
27
 
33
- puts "Joining #{room.name}"
34
-
35
- Firering.room_join(room.id) do
36
- Firering.stream(room.id) do |message|
28
+ EM.run do
29
+ conn.rooms do |rooms|
30
+ room = rooms.detect { |r| r.name == room_name }
37
31
 
38
- if message.from_user?
39
- Firering.user(message.user_id) do |user|
40
- m = "( #{user.name} ) #{message.body}"
41
- system("notify-send -i #{icon_path.shellescape} -t 10000 Campfire #{m.shellescape}")
42
- end
43
- else
44
- m = message.body.to_s
45
- system("notify-send -i #{icon_path.shellescape} -t 10000 Campfire #{m.shellescape}") if m !~ /^\s*$/
46
- end
32
+ unless room
33
+ puts "Could not find that room. Found rooms:"
34
+ rooms.each { |room| puts " * #{room}" }
35
+ exit 1
36
+ end
47
37
 
48
- end
49
- end
38
+ puts "Streaming #{room}"
50
39
 
40
+ room.stream do |message|
41
+ if message.from_user?
42
+ message.user { |user| system("notify-send -i #{icon_path.shellescape} -t 10000 Campfire #{ "( #{user} ) #{message}".shellescape }") }
43
+ else
44
+ system("notify-send -i #{icon_path.shellescape} -t 10000 Campfire #{message.to_s.shellescape}") if message.to_s =~ /\S/
51
45
  end
52
46
  end
53
47
  end
@@ -2,19 +2,21 @@ require 'firering'
2
2
 
3
3
  puts "If you prefer it, you can authenticate your user with u/p instead of the campfire token."
4
4
 
5
- print "Enter subdomain: "
6
- subdomain = gets.chomp
5
+ print "Enter subdomain: "; subdomain = gets.chomp
6
+ print "Enter user: " ; login = gets.chomp
7
+ print "Enter password: " ; password = gets.chomp
7
8
 
8
- print "Enter user: "
9
- user = gets.chomp
10
-
11
- print "Enter password: "
12
- password = gets.chomp
9
+ conn = Firering::Connection.new("http://#{subdomain}.campfirenow.com") do |c|
10
+ c.login = login
11
+ c.password = password
12
+ end
13
13
 
14
14
  EM.run do
15
- Firering.authenticate(subdomain, user, password) do |user|
15
+ conn.authenticate do |user|
16
16
 
17
17
  puts "Token for user #{user.name} is #{user.token}"
18
+ puts "You can set an environment variable for using it on scripts:"
19
+ puts "export CAMPFIRE_TOKEN=#{user.token}"
18
20
 
19
21
  EM.stop
20
22
 
data/examples/events.rb CHANGED
@@ -1,15 +1,21 @@
1
1
  require 'firering'
2
2
 
3
- Firering.subdomain = ENV["CAMPFIRE_SUBDOMAIN"]
4
- Firering.token = ENV["CAMPFIRE_TOKEN"]
3
+ print "Enter subdomain: "; subdomain = gets.chomp
4
+ print "Enter user: " ; login = gets.chomp
5
+ print "Enter password: " ; password = gets.chomp
6
+
7
+ conn = Firering::Connection.new("http://#{subdomain}.campfirenow.com") do |c|
8
+ c.login = login
9
+ c.password = password
10
+ end
5
11
 
6
12
  EM.run do
7
- Firering.rooms do |rooms|
8
- rooms.each do |room|
9
- if room.name == "test2"
10
- Firering.room_join(room.id) do
11
- Firering.stream(room.id) do |message|
13
+ conn.authenticate do |user|
14
+ conn.rooms do |rooms|
15
+ rooms.each do |room|
12
16
 
17
+ if room.name == "test2"
18
+ room.stream do |message|
13
19
  case
14
20
  when message.advertisement? then puts "Handle Ad"
15
21
  when message.allow_guests? then puts "Handle Allow Guests"
@@ -28,9 +34,12 @@ EM.run do
28
34
  when message.upload? then puts "Handle Upload"
29
35
  end
30
36
 
31
- puts message.to_s
37
+ message.user do |user|
38
+ puts "#{user}: #{message}"
39
+ end
32
40
  end
33
41
  end
42
+
34
43
  end
35
44
  end
36
45
  end
@@ -1,41 +1,53 @@
1
1
  require 'firering'
2
2
 
3
- Firering.subdomain = ENV["CAMPFIRE_SUBDOMAIN"]
4
- Firering.token = ENV["CAMPFIRE_TOKEN"]
3
+ print "Enter subdomain: "; subdomain = gets.chomp
4
+ print "Enter user: " ; login = gets.chomp
5
+ print "Enter password: " ; password = gets.chomp
5
6
 
6
- EM.run do
7
- Firering.rooms do |rooms|
8
-
9
- rooms.each do |room|
10
- puts "Users in room #{room.name} (#{room.topic})"
7
+ conn = Firering::Connection.new("http://#{subdomain}.campfirenow.com") do |c|
8
+ c.login = login
9
+ c.password = password
10
+ end
11
11
 
12
- if room.users.empty?
13
- puts " empty (locked: #{room.locked?})"
14
- else
15
- room.users.each do |u|
16
- puts " #{ u.name }. Admin: #{ u.admin? }"
12
+ EM.run do
13
+ conn.authenticate do
14
+ conn.rooms do |rooms|
15
+
16
+ rooms.each do |room|
17
+ puts "Users in room #{room.name} (#{room.topic})"
18
+
19
+ room.users do |users|
20
+ if users.empty?
21
+ puts " empty (locked: #{room.locked?})"
22
+ else
23
+ users.each do |u|
24
+ puts " #{ u.name }. Admin: #{ u.admin? }"
25
+ end
26
+ end
17
27
  end
18
- end
19
28
 
20
- if room.locked?
21
- puts " can't get recent messages in a locked room'"
22
- else
23
- Firering.room_recent_messages(room.id) do |messages|
29
+ if room.locked?
30
+ puts " can't get recent messages in a locked room'"
31
+ else
32
+ room.recent_messages do |messages|
24
33
 
25
- puts "-" * 80
26
- puts "recent message on #{room.name}"
27
- puts "-" * 80
34
+ puts "-" * 80
35
+ puts "recent message on #{room.name}"
36
+ puts "-" * 80
28
37
 
29
- messages.slice(0, 4).each do |m|
30
- puts "\n (#{m.user_id})"
38
+ messages.slice(0, 4).each do |m|
39
+ m.user do |u|
40
+ puts "\n (#{u})"
31
41
 
32
- m.body.to_s.split("\n").each do |chunk|
33
- puts " > #{chunk}"
42
+ m.body.to_s.split("\n").each do |chunk|
43
+ puts " > #{chunk}"
44
+ end
45
+ end
34
46
  end
35
47
  end
36
48
  end
37
- end
38
49
 
50
+ end
39
51
  end
40
52
  end
41
53
 
data/examples/rooms.rb CHANGED
@@ -1,20 +1,25 @@
1
1
  require 'firering'
2
2
 
3
- Firering.subdomain = ENV["CAMPFIRE_SUBDOMAIN"]
4
- Firering.token = ENV["CAMPFIRE_TOKEN"]
3
+ conn = Firering::Connection.new("http://#{ENV["CAMPFIRE_SUBDOMAIN"]}.campfirenow.com") do |c|
4
+ c.token = ENV["CAMPFIRE_TOKEN"]
5
+ end
5
6
 
6
7
  EM.run do
7
- Firering.rooms do |rooms|
8
-
8
+ conn.rooms do |rooms|
9
9
  rooms.each do |room|
10
- puts "Users in room #{room.name}"
11
10
 
12
- if room.users.empty?
13
- puts " empty (locked: #{room.locked?})"
14
- else
15
- room.users.each do |u|
16
- puts " #{ u.name }"
11
+ room.users do |users|
12
+ puts "Users in room #{room.name}"
13
+
14
+ if users.empty?
15
+ puts " empty (locked: #{room.locked?})"
16
+ else
17
+ users.each do |u|
18
+ puts " #{ u.name }"
19
+ end
17
20
  end
21
+
22
+ puts
18
23
  end
19
24
 
20
25
  end
@@ -1,31 +1,34 @@
1
1
  require 'firering'
2
2
 
3
- Firering.subdomain = ENV["CAMPFIRE_SUBDOMAIN"]
4
- Firering.token = ENV["CAMPFIRE_TOKEN"]
3
+ conn = Firering::Connection.new("http://#{ENV["CAMPFIRE_SUBDOMAIN"]}.campfirenow.com") do |c|
4
+ c.token = ENV["CAMPFIRE_TOKEN"]
5
+ end
5
6
 
6
7
  ROOM = "test2"
7
8
 
8
9
  EM.run do
9
- Firering.rooms do |rooms|
10
+ conn.rooms do |rooms|
10
11
  rooms.each do |room|
12
+
11
13
  if room.name == ROOM
12
- Firering.room_join(room.id) do
14
+ room.join do
13
15
 
14
- Firering.update_room(room.id, "topic" => "test test test") do |response|
15
- puts " * Updating topic: "
16
- puts response.response_header.status
16
+ room.update("topic" => "test test test") do |data, http|
17
+ print " * Updating topic. HTTP Status returned: "
18
+ puts http.response_header.status
17
19
  end
18
20
 
19
- Firering.text(room.id, "hola") do
20
- puts "texted"
21
+ room.text("this is a test from the refactored gem") do
22
+ puts "sent text"
21
23
  end
22
24
 
23
- Firering.paste(room.id, "hola\nmundo") do
24
- puts "pasted"
25
+ room.paste("this is a\npaste\nfrom the refactored gem") do
26
+ puts "sent paste"
25
27
  end
26
28
 
27
29
  end
28
30
  end
31
+
29
32
  end
30
33
  end
31
34
 
data/firering.gemspec CHANGED
@@ -4,8 +4,8 @@ Gem::Specification.new do |s|
4
4
  s.rubygems_version = '1.3.5'
5
5
 
6
6
  s.name = 'firering'
7
- s.version = '0.1.1'
8
- s.date = '2010-08-08'
7
+ s.version = '1.0.0'
8
+ s.date = '2010-08-16'
9
9
  s.rubyforge_project = 'firering'
10
10
 
11
11
  s.summary = "Campfire API interface powered by EventMachine and Yajl."
@@ -43,16 +43,19 @@ Gem::Specification.new do |s|
43
43
  examples/update_room.rb
44
44
  firering.gemspec
45
45
  lib/firering.rb
46
+ lib/firering/connection.rb
46
47
  lib/firering/data.rb
47
48
  lib/firering/data/message.rb
48
49
  lib/firering/data/room.rb
49
50
  lib/firering/data/upload.rb
50
51
  lib/firering/data/user.rb
51
- lib/firering/http.rb
52
52
  lib/firering/requests.rb
53
- lib/firering/streaming.rb
53
+ log/.gitignore
54
+ spec/firering/connection_spec.rb
55
+ spec/firering/data/message_spec.rb
56
+ spec/firering/data/room_spec.rb
57
+ spec/firering/data/user_spec.rb
54
58
  spec/firering/data_spec.rb
55
- spec/firering/requests_spec.rb
56
59
  spec/fixtures/headers/delete_messages_ID_star.json
57
60
  spec/fixtures/headers/get_room_ID.json
58
61
  spec/fixtures/headers/get_room_ID_live.json
@@ -90,8 +93,8 @@ Gem::Specification.new do |s|
90
93
  spec/fixtures/json/put_room_ID.json
91
94
  spec/fixtures/load_server.rb
92
95
  spec/fixtures/retrieve.rb
93
- spec/fixtures/server.rb
94
96
  spec/spec_helper.rb
97
+ spec/support/helpers.rb
95
98
  ]
96
99
  # = MANIFEST =
97
100
 
@@ -0,0 +1,125 @@
1
+ module Firering
2
+ class Connection
3
+ include Firering::Requests
4
+
5
+ attr_accessor :host
6
+ attr_accessor :streaming_host
7
+ attr_accessor :max_retries
8
+ attr_accessor :retry_delay
9
+ attr_accessor :redirects
10
+ attr_accessor :logger
11
+ attr_accessor :password
12
+ attr_accessor :token
13
+ attr_accessor :login
14
+
15
+ def initialize(host, streaming_host = "https://streaming.campfirenow.com")
16
+ @retry_delay, @redirects, @max_retries = 2, 1, 2
17
+ self.host, self.streaming_host = host, streaming_host
18
+ yield self if block_given?
19
+ end
20
+
21
+ def logger
22
+ @logger ||= Logger.new(STDOUT)
23
+ end
24
+
25
+ def host=(host)
26
+ @host = Addressable::URI.parse(host)
27
+ end
28
+
29
+ def streaming_host=(host)
30
+ @streaming_host = Addressable::URI.parse(host)
31
+ end
32
+
33
+ def auth_headers
34
+ token ? {'authorization' => [token, "X"] } : {'authorization' => [login, password] }
35
+ end
36
+
37
+ def parameters(data = nil)
38
+ parameters = {
39
+ :redirects => redirects,
40
+ :head => auth_headers.merge("Content-Type" => "application/json")
41
+ }
42
+ parameters.merge!(:body => data.is_a?(String) ? data : Yajl::Encoder.encode(data)) if data
43
+ parameters
44
+ end
45
+
46
+ def http(method, path, data = nil, &callback)
47
+ uri = host.join(path)
48
+ logger.info("performing request to #{uri}")
49
+
50
+ http = EventMachine::HttpRequest.new(uri).send(method, parameters(data))
51
+
52
+ http.errback do
53
+ perform_retry(http) do
54
+ http(method, path, data, &callback)
55
+ end
56
+ end
57
+
58
+ http.callback {
59
+ @performed_retries = 0
60
+ if callback
61
+ data = Yajl::Parser.parse(http.response, :symbolize_keys => true) rescue Hash.new
62
+ callback.call(data, http)
63
+ end
64
+ }
65
+
66
+ http
67
+ end
68
+
69
+ # Streaming
70
+ #
71
+ # The Streaming API allows you to monitor a room in real time. The
72
+ # authenticated user must already have joined the room in order to use this
73
+ # API.
74
+ def stream(room_id, &callback)
75
+ parser = Yajl::Parser.new(:symbolize_keys => true)
76
+
77
+ parser.on_parse_complete = proc do |data|
78
+ callback.call(Firering::Message.instantiate(self, data)) if callback
79
+ end
80
+
81
+ uri = streaming_host.join("/room/#{room_id}/live.json")
82
+ logger.info("performing streaming request to #{uri.to_s}")
83
+ http = EventMachine::HttpRequest.new(uri).get(parameters)
84
+
85
+ http.stream { |chunk| parser << chunk; @performed_retries= 0 }
86
+
87
+ # Campfire servers will try to hold the streaming connections open indefinitely.
88
+ # However, API clients must be able to handle occasional timeouts or
89
+ # disruptions. Upon unexpected disconnection, API clients should wait for a
90
+ # few seconds before trying to reconnect. Formats
91
+ http.errback do
92
+ perform_retry(http) do
93
+ stream(room_id, url, &callback)
94
+ end
95
+ end
96
+
97
+ http
98
+ end
99
+
100
+ def perform_retry(http, &callback)
101
+ if EventMachine.reactor_running? && (@performed_retries.nil? || @performed_retries < @max_retries)
102
+ logger.error("http error #{http.error}. Trying again in #{retry_delay} seconds...")
103
+ EventMachine::add_timer(retry_delay) do
104
+ @performed_retries = (@performed_retries || 0) + 1
105
+ logger.info("Reconnecting...")
106
+ callback.call
107
+ end
108
+ else
109
+ logger.error("The event machine loop is not running")
110
+ raise Firering::Connection::HTTPError.new(http)
111
+ end
112
+ end
113
+
114
+ class HTTPError < RuntimeError
115
+ attr_reader :http
116
+ def initialize(http)
117
+ @http = http
118
+ end
119
+ def to_s
120
+ "http error #{@http.error if @http.respond_to?(:error)}".strip
121
+ end
122
+ end
123
+
124
+ end
125
+ end
@@ -1,29 +1,49 @@
1
- module Firering
2
- class Message < Firering::Data
3
- key :id, :room_id, :user_id, :body, :created_at, :type
4
-
5
- MESSAGE_TYPES = %w[
6
- TextMessage PasteMessage SoundMessage AdvertisementMessage
7
- AllowGuestsMessage DisallowGuestsMessage IdleMessage KickMessage
8
- LeaveMessage SystemMessage TimestampMessage TopicChangeMessage
9
- UnidleMessage UnlockMessage UploadMessage
10
- ]
11
-
12
- # txs activesupport
13
- underscore = proc do |word|
14
- word.to_s.gsub(/::/, '/').gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').gsub(/([a-z\d])([A-Z])/,'\1_\2').tr("-", "_").downcase
15
- end
1
+ class Firering::Message < Firering::Data
16
2
 
17
- MESSAGE_TYPES.each do |message_type|
18
- current_type = (message_type =~ /(.+)Message/) && $1
19
- define_method("#{underscore[current_type]}?") do
20
- type == message_type
21
- end
22
- end
3
+ data_attributes :id, :room_id, :user_id, :body, :created_at, :type
23
4
 
24
- def from_user?
25
- !user_id.nil? && (!user_id.instance_of?(String) || user_id !~ /~\s*$/)
5
+ MESSAGE_TYPES = %w[
6
+ TextMessage PasteMessage SoundMessage AdvertisementMessage
7
+ AllowGuestsMessage DisallowGuestsMessage IdleMessage KickMessage
8
+ LeaveMessage SystemMessage TimestampMessage TopicChangeMessage
9
+ UnidleMessage UnlockMessage UploadMessage
10
+ ]
11
+
12
+ # txs activesupport
13
+ underscore = proc do |word|
14
+ w = word.to_s.gsub(/::/, '/')
15
+ w = w.gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
16
+ w = w.gsub(/([a-z\d])([A-Z])/,'\1_\2')
17
+ w.tr("-", "_").downcase
18
+ end
19
+
20
+ MESSAGE_TYPES.each do |message_type|
21
+ current_type = (message_type =~ /(.+)Message/) && $1
22
+ define_method("#{underscore[current_type]}?") do
23
+ type == message_type
26
24
  end
25
+ end
26
+
27
+ def from_user?
28
+ !user_id.nil? && (!user_id.instance_of?(String) || user_id !~ /^\s*$/)
29
+ end
30
+
31
+ # Highlights a message / Removes a message highlight.
32
+ def star(id, yes_or_no = true, &callback)
33
+ connection.star_message(id, yes_or_no, &callback)
34
+ end
27
35
 
36
+ def room(&callback)
37
+ connection.room(room_id, &callback)
28
38
  end
39
+
40
+ def user(&callback)
41
+ if from_user?
42
+ connection.user(user_id, &callback)
43
+ else
44
+ callback.call(nil, connection)
45
+ end
46
+ end
47
+
48
+ alias_method :to_s, :body
29
49
  end