leecher 0.2.3 → 1.0.0

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 (46) hide show
  1. data/.gitignore +5 -4
  2. data/.travis.yml +2 -0
  3. data/Gemfile +1 -2
  4. data/Gemfile.lock +48 -0
  5. data/README +58 -0
  6. data/Rakefile +5 -7
  7. data/bin/leecher +2 -265
  8. data/bin/leecher-aria2-event-hook +12 -0
  9. data/config/amqp.example.yml +28 -0
  10. data/config/arguments.rb +12 -0
  11. data/config/aria2.example.yml +6 -0
  12. data/config/boot.rb +64 -0
  13. data/config/environment.rb +22 -0
  14. data/config/environments/development.rb +2 -0
  15. data/config/environments/production.rb +5 -0
  16. data/config/environments/test.rb +2 -0
  17. data/config/gheed.example.yml +2 -0
  18. data/config/leecher.example.yml +1 -0
  19. data/config/post-daemonize/readme +5 -0
  20. data/config/pre-daemonize/amqp.rb +6 -0
  21. data/config/pre-daemonize/json.rb +3 -0
  22. data/config/pre-daemonize/readme +12 -0
  23. data/config/pre-daemonize/safely.rb +13 -0
  24. data/leecher.gemspec +13 -4
  25. data/lib/leecher.rb +24 -2
  26. data/lib/leecher/aria2/client.rb +65 -0
  27. data/lib/leecher/client.rb +31 -211
  28. data/lib/leecher/gheed/client.rb +35 -0
  29. data/lib/leecher/metalink_queue.rb +68 -0
  30. data/lib/leecher/version.rb +1 -1
  31. data/libexec/leecher-daemon.rb +34 -0
  32. data/script/console +4 -0
  33. data/script/destroy +4 -0
  34. data/script/generate +4 -0
  35. data/spec/aria2/client_spec.rb +26 -0
  36. data/spec/client_spec.rb +42 -83
  37. data/spec/metalink_queue_spec.rb +25 -0
  38. data/spec/spec.opts +2 -0
  39. data/spec/spec_helper.rb +0 -2
  40. data/tasks/rspec.rake +6 -0
  41. metadata +134 -90
  42. data/config/aria2.conf +0 -15
  43. data/config/client.yml +0 -32
  44. data/lib/leecher/log.rb +0 -91
  45. data/lib/leecher/shellout.rb +0 -27
  46. data/spec/shellout_spec.rb +0 -26
@@ -0,0 +1,35 @@
1
+ module Leecher
2
+ module Gheed
3
+
4
+ class Client
5
+
6
+ require "uri"
7
+ require "net/http"
8
+ require "net/https"
9
+
10
+ attr_reader :config
11
+
12
+ def initialize(config)
13
+ @config = config
14
+ end
15
+
16
+
17
+ def state_change(download, state)
18
+ uri = URI.parse(config.uri)
19
+ req = Net::HTTP::Put.new("/leecher/state_change")
20
+ req.set_form_data("uri" => download, "state" => state)
21
+ req.basic_auth(config.username, config.password)
22
+ res = Net::HTTP.start(uri.hostname, uri.port) do |http|
23
+ http.request(req)
24
+ end
25
+
26
+ case res
27
+ when Net::HTTPSuccess
28
+ MultiJson.decode(res.body)
29
+ else
30
+ res.error!
31
+ end
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,68 @@
1
+ module Leecher
2
+ class MetalinkQueue
3
+
4
+ require "yaml/store"
5
+ require "time"
6
+
7
+ def initialize(filename)
8
+ @store = YAML::Store.new(filename)
9
+ @transaction_depth = 0
10
+ end
11
+
12
+
13
+ def get_queue
14
+ transaction do
15
+ @store[:queue] ||= {}
16
+ end
17
+ end
18
+
19
+ def transaction
20
+ @transaction_depth += 1
21
+
22
+ if @transaction_depth == 1
23
+ @store.transaction do
24
+ begin
25
+ yield
26
+ ensure
27
+ @transaction_depth = 0
28
+ end
29
+ end
30
+ else
31
+ begin
32
+ yield
33
+ ensure
34
+ @transaction_depth -= 1
35
+ end
36
+ end
37
+ end
38
+
39
+ def set_store_entry(uri, properties = {})
40
+ transaction do
41
+ @store[:queue] ||= {}
42
+ @store[:queue][uri] ||= new_uri_entry
43
+ @store[:queue][uri].merge!(properties)
44
+ end
45
+ end
46
+
47
+ def add_uris(uris)
48
+ transaction do
49
+ uris.each do |uri|
50
+ set_store_entry(uri, :state => :brand_new)
51
+ end
52
+ end
53
+ end
54
+
55
+ def new_uri_entry
56
+ {
57
+ :created_at => Time.now.iso8601,
58
+ :state => :brand_new,
59
+ }
60
+ end
61
+
62
+ def update_uri_state(uri, state)
63
+ transaction do
64
+ set_store_entry(uri, :state => state)
65
+ end
66
+ end
67
+ end
68
+ end
@@ -1,3 +1,3 @@
1
1
  module Leecher
2
- VERSION = "0.2.3"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -0,0 +1,34 @@
1
+ # Generated amqp daemon
2
+
3
+ # Do your post daemonization configuration here
4
+ # At minimum you need just the first line (without the block), or a lot
5
+ # of strange things might start happening...
6
+ DaemonKit::Application.running! do |config|
7
+ # Trap signals with blocks or procs
8
+ # config.trap( 'INT' ) do
9
+ # # do something clever
10
+ # end
11
+ # config.trap( 'TERM', Proc.new { puts 'Going down' } )
12
+ end
13
+
14
+ leecher = Leecher.new
15
+
16
+ # Run an event-loop for processing
17
+ DaemonKit::AMQP.run do |connection|
18
+ # Inside this block we're running inside the reactor setup by the
19
+ # amqp gem. Any code in the examples (from the gem) would work just
20
+ # fine here.
21
+
22
+ connection.on_tcp_connection_loss do |client, settings|
23
+ DaemonKit.logger.debug("AMQP connection status changed: #{status}")
24
+ client.reconnect(false, 1)
25
+ end
26
+
27
+ amq = AMQP::Channel.new
28
+ amq.queue(leecher.config.metalink_queue_name).subscribe(:ack => true) do |metadata, payload|
29
+ DaemonKit.logger.debug "Received [#{leecher.config.metalink_queue_name}]: #{payload.inspect}"
30
+ DaemonKit.logger.debug leecher.process_message_on_metalink_queue(payload)
31
+ metadata.ack
32
+ DaemonKit.logger.debug "ack"
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ #!/usr/bin/env ruby
3
+ require File.expand_path('../../config/boot', __FILE__)
4
+ require 'daemon_kit/commands/console'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ #!/usr/bin/env ruby
3
+ require File.expand_path( '../../config/environment', __FILE__ )
4
+ require 'daemon_kit/commands/generate'
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ #!/usr/bin/env ruby
3
+ require File.expand_path( '../../config/environment', __FILE__ )
4
+ require 'daemon_kit/commands/generate'
@@ -0,0 +1,26 @@
1
+ require "leecher/aria2/client"
2
+ require "ostruct"
3
+
4
+ module Leecher::Aria2
5
+ describe Client do
6
+
7
+ let(:config) { OpenStruct.new(:port => 9000) }
8
+ let(:client) { Client.new(config) }
9
+
10
+ it "prefixes calls with 'aria2.'" do
11
+ client.should_receive(:call2).with("aria2.method").and_return([true, :param])
12
+ client.call("method")
13
+ end
14
+
15
+ it "responds to #add_uri by delegating to 'aria2.addUri'" do
16
+ client.should_receive(:call).with("addUri", ["uri"], { option: "value" })
17
+ client.add_uri(["uri"], { option: "value" })
18
+ end
19
+
20
+ it "knows where the callback command is" do
21
+ expected = File.expand_path("../../bin/leecher-aria2-event-hook", File.dirname(__FILE__))
22
+ client.callback_command.should eq(expected)
23
+ File.exist?(client.callback_command).should be_true
24
+ end
25
+ end
26
+ end
@@ -1,98 +1,57 @@
1
- require File.dirname(__FILE__) + "/spec_helper"
1
+ require "leecher/client"
2
+ require "ostruct"
2
3
 
3
- require "lib/leecher/client"
4
+ module Leecher
5
+ describe Client do
4
6
 
5
- describe Leecher::Client do
7
+ let(:work_dir) { "/tmp/client-spec-#{Process.pid}" }
8
+ let(:config) { OpenStruct.new(:work_dir => work_dir) }
9
+ let(:aria2_client) { double("aria2-client") }
10
+ let(:gheed_client) { double("gheed-client") }
11
+ let(:leecher) { Client.new(config, aria2_client, gheed_client) }
6
12
 
7
- before do
8
- mock_log = double("log")
9
- %w(debug warn info error fatal).each do |log_level|
10
- mock_log.should_receive(log_level).any_number_of_times
13
+ before do
14
+ FileUtils.mkdir_p(work_dir)
11
15
  end
12
- @c = Leecher::Client.new("username",
13
- "password",
14
- "metalink",
15
- double("bunny_opts"),
16
- {
17
- :bin => "/usr/bin/aria2c",
18
- :args => ["-V"],
19
- },
20
- mock_log)
21
- end
22
-
23
-
24
- it "should initialize" do
25
- @c.username.should == "username"
26
- @c.password.should == "password"
27
- @c.metalink_queue_name.should == "metalink"
28
- end
29
-
30
- context "next_queue_message" do
31
16
 
32
- it "should only return when something is on the queue" do
33
- bunny = double("bunny")
34
- @c.stub(:make_bunny) { bunny }
35
- q = double("q")
36
- q.should_receive(:subscribe).
37
- and_yield({:payload => :queue_empty}).
38
- and_yield({:payload => "message"})
39
- @c.next_queue_message(q).should == "message"
17
+ after do
18
+ FileUtils.rm_r(work_dir) if File.exist?(work_dir)
40
19
  end
41
- end
20
+
21
+ context "#process_message_on_metalink_queue" do
42
22
 
43
- context "drain_metalink_queue" do
44
23
 
45
- before do
46
- @q = double("q")
47
- @q.should_receive(:name).
48
- any_number_of_times.
49
- and_return("qq")
50
- end
24
+ let(:uri) { "http://gheed/some.metalink" }
25
+ let(:options) { { "dir" => "/download/here" } }
26
+ let(:json_instruction) { MultiJson.encode([[uri], options]) }
51
27
 
52
-
53
- it "should rpc to aria2" do
54
- uris = ["http://some/url"]
55
- options = {}
56
- @c.stub(:next_queue_message).
57
- and_yield(Yajl::Encoder.encode([uris, options]))
58
- rpc_server = double("rpc_server")
59
- rpc_server.should_receive(:call).
60
- with("aria2.addUri", uris, options).
61
- and_return(true)
62
- @c.stub(:rpc_server).
63
- and_return(rpc_server)
64
- @c.drain_metalink_queue(@q).should be_true
65
- end
28
+ before do
29
+ leecher.stub(:aria2_client).and_return(aria2_client)
30
+ end
31
+
32
+ it "consumes aria instructions encoded in JSON and forwards them onto aria" do
33
+ aria2_client.should_receive(:add_uri).with([uri], options).and_return(1)
34
+ leecher.process_message_on_metalink_queue(json_instruction)
35
+ end
66
36
 
67
- it "should return according to the success of the rpc" do
68
- @c.stub(:next_queue_message).
69
- and_yield(Yajl::Encoder.encode([[], {}]))
70
- rpc_server = double("rpc_server")
71
- @c.stub(:rpc_server).
72
- and_return(rpc_server)
73
- rpc_server.stub(:call).
74
- and_return(true)
75
- @c.drain_metalink_queue(@q).should be_true
76
- rpc_server.stub(:call).
77
- and_raise(RuntimeError)
78
- @c.drain_metalink_queue(@q).should be_false
37
+ it "keeps a list of what is in-flight" do
38
+ aria2_client.stub(:add_uri)
39
+ leecher.process_message_on_metalink_queue(json_instruction)
40
+ leecher.queue.get_queue.should have_key(uri)
41
+ end
79
42
  end
80
43
 
81
- it "should rewrite the dir option" do
82
- uris = ["http://some/url"]
83
- options = { "dir" => "foo" }
84
- @c.stub(:next_queue_message).
85
- and_yield(Yajl::Encoder.encode([uris, options]))
86
- rpc_server = double("rpc_server")
87
- rpc_server.should_receive(:call).
88
- with("aria2.getGlobalOption").
89
- and_return({"dir" => "/tmp"})
90
- rpc_server.should_receive(:call).
91
- with("aria2.addUri", uris, {"dir" => "/tmp/foo"}).
92
- and_return(true)
93
- @c.stub(:rpc_server).
94
- and_return(rpc_server)
95
- @c.drain_metalink_queue(@q).should be_true
44
+ context "#status_change" do
45
+
46
+ it "updates the store status" do
47
+ response = {"bitfield"=>"80", "completedLength"=>"726", "connections"=>"0", "dir"=>"movies/720p/example", "downloadSpeed"=>"0", "errorCode"=>"0", "files"=>[{"completedLength"=>"726", "index"=>"1", "length"=>"726", "path"=>"movies/720p/example/nfo.metalink", "selected"=>"true", "uris"=>[{"status"=>"used", "uri"=>"http://example/download/metalinks/nfo.metalink"}, {"status"=>"waiting", "uri"=>"http://example/download/metalinks/nfo.metalink"}, {"status"=>"waiting", "uri"=>"http://example/download/metalinks/nfo.metalink"}, {"status"=>"waiting", "uri"=>"http://example/download/metalinks/nfo.metalink"}, {"status"=>"waiting", "uri"=>"http://example/download/metalinks/nfo.metalink"}]}], "followedBy"=>["2"], "gid"=>"1", "numPieces"=>"1", "pieceLength"=>"1048576", "status"=>"complete", "totalLength"=>"726", "uploadLength"=>"0", "uploadSpeed"=>"0"}
48
+ aria2_client.should_receive(:tell_status).with(1).and_return(response)
49
+ leecher.status_change(1)
50
+ entry = leecher.queue.get_queue["http://example/download/metalinks/nfo.metalink"]
51
+ entry.should_not be_nil
52
+ entry[:gid].should eq(1)
53
+ entry[:state].should eq(:complete)
54
+ end
96
55
  end
97
56
  end
98
57
  end
@@ -0,0 +1,25 @@
1
+ require "leecher/metalink_queue"
2
+
3
+ module Leecher
4
+ describe MetalinkQueue do
5
+
6
+ require "fileutils"
7
+
8
+ let(:filename) { "/tmp/metalink-queue-spec-#{Process.pid}" }
9
+ let(:queue) { MetalinkQueue.new(filename) }
10
+
11
+ after do
12
+ FileUtils.rm(filename) if File.exist?(filename)
13
+ end
14
+
15
+ it "stores the queue to disk" do
16
+ File.size?(filename).should be_nil
17
+ queue.get_queue.should be_empty
18
+ queue.add_uris(["a", "b"])
19
+ File.size(filename).should_not be_zero
20
+ queue.get_queue.should have_key("a")
21
+ queue.get_queue.should have_key("b")
22
+ MetalinkQueue.new(filename).get_queue.keys.should eq(["a", "b"])
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,2 @@
1
+ --color
2
+ --format documentation
@@ -1,2 +0,0 @@
1
- $LOAD_PATH.unshift(File.dirname(__FILE__))
2
- $LOAD_PATH.unshift(File.dirname(__FILE__) + "../lib")
@@ -0,0 +1,6 @@
1
+ require 'rspec/core/rake_task'
2
+
3
+ desc "Run the specs under spec/"
4
+ RSpec::Core::RakeTask.new do |t|
5
+ t.rspec_opts = ['--options', "spec/spec.opts"]
6
+ end
metadata CHANGED
@@ -1,137 +1,181 @@
1
- --- !ruby/object:Gem::Specification
1
+ --- !ruby/object:Gem::Specification
2
2
  name: leecher
3
- version: !ruby/object:Gem::Version
4
- hash: 17
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.0
5
5
  prerelease:
6
- segments:
7
- - 0
8
- - 2
9
- - 3
10
- version: 0.2.3
11
6
  platform: ruby
12
- authors:
7
+ authors:
13
8
  - Marc Bowes
14
9
  autorequire:
15
10
  bindir: bin
16
11
  cert_chain: []
17
-
18
- date: 2011-08-22 00:00:00 +02:00
19
- default_executable:
20
- dependencies:
21
- - !ruby/object:Gem::Dependency
22
- name: rspec
23
- prerelease: false
24
- requirement: &id001 !ruby/object:Gem::Requirement
12
+ date: 2012-07-15 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: daemon-kit
16
+ requirement: &7773340 !ruby/object:Gem::Requirement
25
17
  none: false
26
- requirements:
27
- - - ">="
28
- - !ruby/object:Gem::Version
29
- hash: 7
30
- segments:
31
- - 2
32
- version: "2"
33
- type: :development
34
- version_requirements: *id001
35
- - !ruby/object:Gem::Dependency
36
- name: bunny
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
37
23
  prerelease: false
38
- requirement: &id002 !ruby/object:Gem::Requirement
24
+ version_requirements: *7773340
25
+ - !ruby/object:Gem::Dependency
26
+ name: safely
27
+ requirement: &7788340 !ruby/object:Gem::Requirement
39
28
  none: false
40
- requirements:
41
- - - ">="
42
- - !ruby/object:Gem::Version
43
- hash: 3
44
- segments:
45
- - 0
46
- version: "0"
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
47
33
  type: :runtime
48
- version_requirements: *id002
49
- - !ruby/object:Gem::Dependency
50
- name: erubis
51
34
  prerelease: false
52
- requirement: &id003 !ruby/object:Gem::Requirement
35
+ version_requirements: *7788340
36
+ - !ruby/object:Gem::Dependency
37
+ name: multi_json
38
+ requirement: &7786200 !ruby/object:Gem::Requirement
53
39
  none: false
54
- requirements:
55
- - - ">="
56
- - !ruby/object:Gem::Version
57
- hash: 3
58
- segments:
59
- - 0
60
- version: "0"
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
61
44
  type: :runtime
62
- version_requirements: *id003
63
- - !ruby/object:Gem::Dependency
45
+ prerelease: false
46
+ version_requirements: *7786200
47
+ - !ruby/object:Gem::Dependency
64
48
  name: yajl-ruby
49
+ requirement: &7784880 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: *7784880
58
+ - !ruby/object:Gem::Dependency
59
+ name: amqp
60
+ requirement: &7784000 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :runtime
65
67
  prerelease: false
66
- requirement: &id004 !ruby/object:Gem::Requirement
68
+ version_requirements: *7784000
69
+ - !ruby/object:Gem::Dependency
70
+ name: erubis
71
+ requirement: &7782780 !ruby/object:Gem::Requirement
67
72
  none: false
68
- requirements:
69
- - - ">="
70
- - !ruby/object:Gem::Version
71
- hash: 3
72
- segments:
73
- - 0
74
- version: "0"
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
75
77
  type: :runtime
76
- version_requirements: *id004
78
+ prerelease: false
79
+ version_requirements: *7782780
80
+ - !ruby/object:Gem::Dependency
81
+ name: rake
82
+ requirement: &7781680 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *7781680
91
+ - !ruby/object:Gem::Dependency
92
+ name: rspec
93
+ requirement: &7780840 !ruby/object:Gem::Requirement
94
+ none: false
95
+ requirements:
96
+ - - ! '>='
97
+ - !ruby/object:Gem::Version
98
+ version: '0'
99
+ type: :development
100
+ prerelease: false
101
+ version_requirements: *7780840
77
102
  description: A server populates a queue, this client downloads those files
78
- email:
103
+ email:
79
104
  - marcbowes+leecher@gmail.com
80
- executables:
105
+ executables:
81
106
  - leecher
107
+ - leecher-aria2-event-hook
82
108
  extensions: []
83
-
84
109
  extra_rdoc_files: []
85
-
86
- files:
110
+ files:
87
111
  - .gitignore
112
+ - .travis.yml
88
113
  - Gemfile
114
+ - Gemfile.lock
115
+ - README
89
116
  - Rakefile
90
117
  - bin/leecher
91
- - config/aria2.conf
92
- - config/client.yml
118
+ - bin/leecher-aria2-event-hook
119
+ - config/amqp.example.yml
120
+ - config/arguments.rb
121
+ - config/aria2.example.yml
122
+ - config/boot.rb
123
+ - config/environment.rb
124
+ - config/environments/development.rb
125
+ - config/environments/production.rb
126
+ - config/environments/test.rb
127
+ - config/gheed.example.yml
128
+ - config/leecher.example.yml
129
+ - config/post-daemonize/readme
130
+ - config/pre-daemonize/amqp.rb
131
+ - config/pre-daemonize/json.rb
132
+ - config/pre-daemonize/readme
133
+ - config/pre-daemonize/safely.rb
93
134
  - leecher.gemspec
94
135
  - lib/leecher.rb
136
+ - lib/leecher/aria2/client.rb
95
137
  - lib/leecher/client.rb
96
- - lib/leecher/log.rb
97
- - lib/leecher/shellout.rb
138
+ - lib/leecher/gheed/client.rb
139
+ - lib/leecher/metalink_queue.rb
98
140
  - lib/leecher/version.rb
141
+ - libexec/leecher-daemon.rb
142
+ - script/console
143
+ - script/destroy
144
+ - script/generate
145
+ - spec/aria2/client_spec.rb
99
146
  - spec/client_spec.rb
100
- - spec/shellout_spec.rb
147
+ - spec/metalink_queue_spec.rb
148
+ - spec/spec.opts
101
149
  - spec/spec_helper.rb
102
- has_rdoc: true
103
- homepage: ""
150
+ - tasks/rspec.rake
151
+ homepage: ''
104
152
  licenses: []
105
-
106
153
  post_install_message:
107
154
  rdoc_options: []
108
-
109
- require_paths:
155
+ require_paths:
110
156
  - lib
111
- required_ruby_version: !ruby/object:Gem::Requirement
157
+ required_ruby_version: !ruby/object:Gem::Requirement
112
158
  none: false
113
- requirements:
114
- - - ">="
115
- - !ruby/object:Gem::Version
116
- hash: 3
117
- segments:
159
+ requirements:
160
+ - - ! '>='
161
+ - !ruby/object:Gem::Version
162
+ version: '0'
163
+ segments:
118
164
  - 0
119
- version: "0"
120
- required_rubygems_version: !ruby/object:Gem::Requirement
165
+ hash: -3150552957577293375
166
+ required_rubygems_version: !ruby/object:Gem::Requirement
121
167
  none: false
122
- requirements:
123
- - - ">="
124
- - !ruby/object:Gem::Version
125
- hash: 3
126
- segments:
168
+ requirements:
169
+ - - ! '>='
170
+ - !ruby/object:Gem::Version
171
+ version: '0'
172
+ segments:
127
173
  - 0
128
- version: "0"
174
+ hash: -3150552957577293375
129
175
  requirements: []
130
-
131
176
  rubyforge_project: leecher
132
- rubygems_version: 1.6.2
177
+ rubygems_version: 1.8.11
133
178
  signing_key:
134
179
  specification_version: 3
135
180
  summary: Download files off an AMQP queue
136
181
  test_files: []
137
-