torrent_search 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: bd9610eafd270094d42cb2941cf33f3523c464de
4
- data.tar.gz: 46672a36018da5016f2236cb46d5b14ac8f69aac
3
+ metadata.gz: 12ddba74cad73e48b10325bc3d675c3847ace3dd
4
+ data.tar.gz: 2596e6a414aa0ab60d4a2648ea8d6949a499f1d1
5
5
  SHA512:
6
- metadata.gz: b4639cc8accc906700b3ff859bc9b0365d8a783225cb20a1f67aeb5d84d2ac30701caabb4fea8b9eebf4b58ebc46c14ff0baa4974a3d670023863a7ee0ad5b32
7
- data.tar.gz: 0f5635af0ebf544af3ad8b478a133dd8aa614be1f6752e7465f7226cc3fb7c6aa0f731d62bb41a065ea2b1485f97e8b47e922121eb93b35e8d126b36a724c57d
6
+ metadata.gz: a277fc48659c4c2d040081f594f095bb44ac16ab03cc63bf5bab8605f30e247971dfea548b6e7659480795541776c41db38e678f3eed18924a7439d2dd6c69a2
7
+ data.tar.gz: 2ed1dae9c1625eae4e7d6147404686940295344027646cc41ce2e83a256c9de5161ac16e30dcebb21c8f84294949bea0983900d72f2d2189efc7ada9edb96047
data/.gitignore CHANGED
@@ -7,7 +7,6 @@ Gemfile.lock
7
7
  InstalledFiles
8
8
  _yardoc
9
9
  coverage
10
- doc/
11
10
  lib/bundler/man
12
11
  pkg
13
12
  rdoc
data/README.md CHANGED
@@ -1,24 +1,31 @@
1
- # TorrentSearch
2
-
3
- TODO: Write a gem description
4
-
5
- ## Installation
6
-
7
- Add this line to your application's Gemfile:
1
+ [![Gem Version](https://badge.fury.io/rb/torrent_search.png)](http://badge.fury.io/rb/torrent_search)
2
+ [![Dependency Status](https://gemnasium.com/joenas/torrent_search.png)](https://gemnasium.com/joenas/torrent_search)
8
3
 
9
- gem 'torrent_search'
10
4
 
11
- And then execute:
5
+ # TorrentSearch
12
6
 
13
- $ bundle
7
+ Search for torrents and download from command line. Trackers will be added, PRs are welcome!
14
8
 
15
- Or install it yourself as:
9
+ ## Installation
16
10
 
17
11
  $ gem install torrent_search
18
12
 
19
13
  ## Usage
20
14
 
21
- TODO: Write usage instructions here
15
+ ![cli usage](https://raw.github.com/joenas/torrent_search/master/doc/screenshot.png)
16
+
17
+ ```shell
18
+ Usage:
19
+ tsearch [TERMS]
20
+
21
+ Options:
22
+ -l, [--limit=N] # Limit search results, default 10
23
+
24
+ Commands:
25
+ tsearch [TERMS] # tsearch help search for options
26
+ tsearch help [COMMAND] # Describe available commands or one specific command
27
+ tsearch version # Shows version
28
+ ```
22
29
 
23
30
  ## Contributing
24
31
 
data/bin/tsearch CHANGED
@@ -1,5 +1,4 @@
1
1
  #!/usr/bin/env ruby
2
2
  require "torrent_search"
3
- require "torrent_search/cli"
4
3
 
5
4
  TorrentSearch::CLI.start
Binary file
@@ -1,6 +1,23 @@
1
- require "torrent_search/version"
1
+ require 'thor'
2
+ require 'torrent_search/default_command'
3
+ require 'torrent_search/cli'
4
+
2
5
  require "torrent_search/trackers/kick_ass/kick_ass"
3
6
 
7
+ require 'torrent_search/views/base'
8
+ require 'torrent_search/views/download'
9
+ require 'torrent_search/views/menu'
10
+ require 'torrent_search/views/search'
11
+
12
+ require 'torrent_search/services/download'
13
+
14
+ require 'torrent_search/controllers/download'
15
+ require 'torrent_search/controllers/search'
16
+
17
+ require 'torrent_search/result_table'
18
+
19
+ require "torrent_search/version"
20
+
4
21
  module TorrentSearch
5
22
  # Your code goes here...
6
23
  end
@@ -1,10 +1,3 @@
1
- require 'thor'
2
- require 'torrent_search/default_command'
3
- require 'torrent_search/download'
4
- require 'torrent_search/result_table'
5
- require 'torrent_search/menu'
6
- require 'torrent_search/version'
7
-
8
1
  module TorrentSearch
9
2
  class CLI < Thor
10
3
  include Thor::Actions
@@ -28,10 +21,7 @@ module TorrentSearch
28
21
  desc: 'Limit search results, default 10'
29
22
 
30
23
  def search(*search_terms)
31
- search_result = Trackers::KickAss::Scraper.new(search_terms, options).search
32
- Menu.new(self, search_result).display
33
- rescue SocketError
34
- error 'No network connection?'
24
+ Controllers::Search.new.search search_terms, options
35
25
  end
36
26
 
37
27
  end
@@ -0,0 +1,47 @@
1
+ module TorrentSearch
2
+ module Controllers
3
+ class Download
4
+ DEFAULT_DIR = '.'
5
+
6
+ def initialize(search_result, view = Views::Download.new)
7
+ @search_result = search_result
8
+ @view = view
9
+ end
10
+
11
+ def download
12
+ perform_download choose_torrent!, choose_path!
13
+ end
14
+
15
+ private
16
+ def perform_download(torrent, path)
17
+ @view.downloading! torrent
18
+ Services::Download.new(path, torrent).perform @view
19
+ end
20
+
21
+ def choose_torrent!
22
+ while
23
+ index = @view.torrent?(search_result_range.max)
24
+ return @search_result[index.to_i] if valid_choice?(index)
25
+ @view.invalid_option!
26
+ end
27
+ end
28
+
29
+ def choose_path!
30
+ path = @view.directory? DEFAULT_DIR
31
+ path.empty? ? DEFAULT_DIR : path
32
+ end
33
+
34
+ def valid_choice?(index)
35
+ index.match(/\A\d+\z/) && is_in_search_result?(index)
36
+ end
37
+
38
+ def is_in_search_result?(index)
39
+ search_result_range.member?(index.to_i)
40
+ end
41
+
42
+ def search_result_range
43
+ @range ||= Range.new(0, @search_result.length, true)
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,56 @@
1
+ module TorrentSearch
2
+ module Controllers
3
+ class Search
4
+ ACTIONS = {
5
+ d: :download,
6
+ s: :search,
7
+ q: :quit
8
+ }
9
+
10
+ def initialize(view = Views::Search.new)
11
+ @view = view
12
+ end
13
+
14
+ def search(search_terms = nil, options = {})
15
+ search_terms ||= @view.search_terms?
16
+ perform_search search_terms, options
17
+ display_menu
18
+ choose_action!
19
+ rescue SocketError
20
+ error 'No network connection?'
21
+ end
22
+
23
+
24
+ private
25
+ def perform_search(search_terms, options)
26
+ @search_result = Trackers::KickAss::Scraper.new(search_terms, options).search
27
+ end
28
+
29
+ def display_menu
30
+ Views::Menu.new(available_actions, @search_result).display
31
+ end
32
+
33
+ def download
34
+ Download.new(@search_result).download
35
+ end
36
+
37
+ def quit
38
+ CLI::quit
39
+ end
40
+
41
+ def choose_action!
42
+ chosen = @view.action?
43
+ send available_actions.fetch(chosen, :invalid_command)
44
+ end
45
+
46
+ def invalid_command
47
+ @view.invalid_command!
48
+ choose_action!
49
+ end
50
+
51
+ def available_actions
52
+ ACTIONS.dup.tap {|hash| hash.delete(:d) unless @search_result.any? }
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,40 @@
1
+ require "httparty"
2
+
3
+ module TorrentSearch
4
+ module Services
5
+ class Download
6
+ def initialize(path, torrent)
7
+ @path = path
8
+ @torrent = torrent
9
+ end
10
+
11
+ def perform(view)
12
+ if success?
13
+ save!
14
+ view.success
15
+ else
16
+ view.failure response, @torrent.href
17
+ end
18
+ end
19
+
20
+ private
21
+ def success?
22
+ response.code == 200
23
+ end
24
+
25
+ def filename
26
+ File.join(@path, "#{@torrent.filename}.torrent")
27
+ end
28
+
29
+ def save!
30
+ File.open(filename, "wb") do |file|
31
+ file.write response.parsed_response
32
+ end
33
+ end
34
+
35
+ def response
36
+ @response ||= HTTParty.get(@torrent.href)
37
+ end
38
+ end
39
+ end
40
+ end
@@ -1,3 +1,3 @@
1
1
  module TorrentSearch
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -0,0 +1,19 @@
1
+ module TorrentSearch
2
+ module Views
3
+ class Base
4
+ include Thor::Shell
5
+ include Thor::Actions
6
+
7
+ def initialize
8
+ end
9
+
10
+ def invalid_option!
11
+ say 'Invalid option', :red
12
+ end
13
+
14
+ def invalid_command!
15
+ say 'Invalid command', :red
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,26 @@
1
+ module TorrentSearch
2
+ module Views
3
+ class Download < Base
4
+
5
+ def torrent?(max)
6
+ ask("Torrent (0-#{max}):")
7
+ end
8
+
9
+ def directory?(default)
10
+ ask "Directory to save file (default '#{default}'):"
11
+ end
12
+
13
+ def downloading!(torrent)
14
+ say "Downloading #{torrent.name}...", :blue
15
+ end
16
+
17
+ def success
18
+ say 'Complete', :green
19
+ end
20
+
21
+ def failure(error, href)
22
+ say "Error: #{error.message} - #{href}", :red
23
+ end
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,34 @@
1
+ module TorrentSearch
2
+ module Views
3
+
4
+ class Menu < Base
5
+ def initialize(actions, search_result)
6
+ @actions = actions
7
+ @search_result = search_result
8
+ end
9
+
10
+ def display
11
+ say_status "\n[Results]", '', :green
12
+ if results?
13
+ print_search_result_table
14
+ else
15
+ say 'Nothing found'
16
+ end
17
+ say_status "\n[Actions]", action_menu, :blue
18
+ end
19
+
20
+ private
21
+ def results?
22
+ @search_result.any?
23
+ end
24
+
25
+ def print_search_result_table
26
+ print_table ResultTable.new(@search_result)
27
+ end
28
+
29
+ def action_menu
30
+ @actions.map {|hash| hash.join(": ")}.join("\t")
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,14 @@
1
+ module TorrentSearch
2
+ module Views
3
+ class Search < Base
4
+
5
+ def action?
6
+ ask("Choose:").to_sym
7
+ end
8
+
9
+ def search_terms?
10
+ ask("Search for:").split
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe TorrentSearch::Controllers::Download do
4
+ Given(:search_result){['asdf']}
5
+ Given(:view){double 'view'}
6
+ Given{view.stub(:downloading!)}
7
+
8
+ subject{described_class.new(search_result, view)}
9
+
10
+ describe '#download' do
11
+ Given(:torrent){search_result.first}
12
+ Given(:path){'.'}
13
+ Given(:download_double){double 'download'}
14
+
15
+ Given{view.stub(:directory?).and_return(path)}
16
+
17
+ Given{TorrentSearch::Services::Download.should_receive(:new).with(path, torrent).and_return(download_double)}
18
+ Given{download_double.should_receive(:perform).with(view)}
19
+
20
+ When{subject.download}
21
+
22
+ describe 'choosing download dir' do
23
+ Given{view.stub(:torrent?).and_return('0')}
24
+
25
+ context 'with default dir' do
26
+ Given(:path){described_class::DEFAULT_DIR}
27
+ Given{view.stub(:directory?).and_return('')}
28
+ Then{}
29
+ end
30
+
31
+ context 'given a dir' do
32
+ Given(:path){'/tmp'}
33
+ Then{}
34
+ end
35
+ end
36
+
37
+ describe 'choosing torrent' do
38
+ context 'with invalid choice then valid choice' do
39
+ Given{view.stub(:torrent?).and_return('1', '0')}
40
+ Given{view.should_receive(:invalid_option!)}
41
+ Then{}
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,58 @@
1
+ require 'spec_helper'
2
+
3
+ describe TorrentSearch::Controllers::Search do
4
+
5
+ Given(:view){double 'view'}
6
+
7
+ subject{described_class.new(view)}
8
+
9
+ describe '#search' do
10
+ Given(:search_terms){'asdf'}
11
+ Given(:options){{}}
12
+ Given(:scraper_double){double 'scraper'}
13
+
14
+ Given{TorrentSearch::Trackers::KickAss::Scraper.should_receive(:new).with(search_terms, options).and_return(scraper_double)}
15
+ Given{scraper_double.should_receive(:search).and_return(search_result)}
16
+
17
+ When{subject.search(search_terms, options)}
18
+
19
+ context 'with no search result' do
20
+ Given(:search_result){[]}
21
+
22
+ context 'with invalid command then quit' do
23
+ Given{view.stub(:action?).and_return(:d, :q)}
24
+ Given{view.stub(:invalid_command!)}
25
+
26
+ Given{TorrentSearch::CLI.should_receive(:quit)}
27
+ Then{}
28
+ end
29
+
30
+ context 'with search again then quit' do
31
+ Given(:new_search_terms){'fdsa'}
32
+
33
+ Given{view.stub(:action?).and_return(:s, :q)}
34
+ Given{view.stub(:search_terms?).and_return(new_search_terms)}
35
+
36
+ Given{TorrentSearch::Trackers::KickAss::Scraper.should_receive(:new).with(new_search_terms, options).and_return(scraper_double)}
37
+ Given{scraper_double.should_receive(:search).and_return(search_result)}
38
+ Given{TorrentSearch::CLI.should_receive(:quit)}
39
+ Then{}
40
+ end
41
+ end
42
+
43
+ context 'with search result' do
44
+ Given(:search_result){[OpenStruct.new(name: '', size:'', seeders: '', leechers:'')]}
45
+
46
+ describe 'download' do
47
+ Given{view.stub(:action?).and_return(:d)}
48
+
49
+ Given(:download_double){double 'download'}
50
+ Given{TorrentSearch::Controllers::Download.should_receive(:new).with(search_result).and_return(download_double)}
51
+ Given{download_double.should_receive :download}
52
+ Then{}
53
+ end
54
+ end
55
+
56
+ end
57
+
58
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe TorrentSearch::Services::Download do
4
+ Given(:path){File.join(Dir.pwd, 'spec/tmp')}
5
+ Given(:torrent){OpenStruct.new(href: 'http://asdf.com', filename: 'asdf')}
6
+ Given(:created_file){File.join(path, "#{torrent.filename}.torrent")}
7
+ Given(:view){double 'view'}
8
+
9
+ subject{described_class.new(path, torrent)}
10
+
11
+ before :each do
12
+ FileUtils.rm created_file, force: true
13
+ end
14
+
15
+ describe '#perform' do
16
+ When{subject.perform view}
17
+
18
+ context 'with 200 success' do
19
+ Given{stub_request(:any, "asdf.com").to_return(status: 200, body: 'abc')}
20
+ Given{view.should receive(:success)}
21
+ Then{a_request(:get, "asdf.com").should have_been_made.once}
22
+ And{File.read(created_file).should eq 'abc'}
23
+ end
24
+
25
+ context 'with 404 not found' do
26
+ Given{stub_request(:any, "asdf.com").to_return(status: 404)}
27
+ Given{view.should receive(:failure)}
28
+ Then{a_request(:get, "asdf.com").should have_been_made.once}
29
+ And{File.exists?(created_file).should be_false}
30
+ end
31
+
32
+ context 'with 500 error' do
33
+ Given{stub_request(:any, "asdf.com").to_return(status: 500)}
34
+ Given{view.should receive(:failure)}
35
+ Then{a_request(:get, "asdf.com").should have_been_made.once}
36
+ And{File.exists?(created_file).should be_false}
37
+ end
38
+ end
39
+ end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'torrent_search'
2
2
  require 'rspec-given'
3
3
  require 'vcr'
4
- require 'fakeweb'
4
+ require 'webmock/rspec'
5
5
 
6
6
  Dir[File.dirname(__FILE__) + "/support/*.rb"].each{|file| require file}
7
7
 
@@ -10,6 +10,13 @@ RSpec.configure do |config|
10
10
  config.run_all_when_everything_filtered = true
11
11
  config.filter_run :focus
12
12
 
13
+ config.before :all do
14
+ path = File.join(Dir.pwd, 'spec/tmp')
15
+ FileUtils.mkdir path unless File.exists? path
16
+ end
17
+
18
+
19
+
13
20
  # Run specs in random order to surface order dependencies. If you find an
14
21
  # order dependency and want to debug it, you can fix the order by providing
15
22
  # the seed, which is printed after each run.
@@ -1,7 +1,7 @@
1
1
  # spec/support/vcr_setup.rb
2
2
  VCR.configure do |c|
3
3
  c.cassette_library_dir = 'spec/vcr'
4
- c.hook_into :fakeweb
4
+ c.hook_into :webmock
5
5
  c.configure_rspec_metadata!
6
6
  c.allow_http_connections_when_no_cassette = true
7
7
  c.default_cassette_options = { :match_requests_on => [:query, :method, :uri, :body], record: :new_episodes }
@@ -29,6 +29,6 @@ Gem::Specification.new do |spec|
29
29
  spec.add_development_dependency "guard", ">=1.6"
30
30
  spec.add_development_dependency "guard-rspec", ">=2.4"
31
31
  spec.add_development_dependency "vcr"
32
- spec.add_development_dependency "fakeweb"
32
+ spec.add_development_dependency "webmock"
33
33
 
34
34
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: torrent_search
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Jon Neverland
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-12-23 00:00:00.000000000 Z
11
+ date: 2013-12-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -151,7 +151,7 @@ dependencies:
151
151
  - !ruby/object:Gem::Version
152
152
  version: '0'
153
153
  - !ruby/object:Gem::Dependency
154
- name: fakeweb
154
+ name: webmock
155
155
  requirement: !ruby/object:Gem::Requirement
156
156
  requirements:
157
157
  - - '>='
@@ -180,16 +180,25 @@ files:
180
180
  - README.md
181
181
  - Rakefile
182
182
  - bin/tsearch
183
+ - doc/screenshot.png
183
184
  - lib/torrent_search.rb
184
185
  - lib/torrent_search/cli.rb
186
+ - lib/torrent_search/controllers/download.rb
187
+ - lib/torrent_search/controllers/search.rb
185
188
  - lib/torrent_search/default_command.rb
186
- - lib/torrent_search/download.rb
187
- - lib/torrent_search/menu.rb
188
189
  - lib/torrent_search/result_table.rb
190
+ - lib/torrent_search/services/download.rb
189
191
  - lib/torrent_search/trackers/kick_ass/kick_ass.rb
190
192
  - lib/torrent_search/trackers/kick_ass/scraper.rb
191
193
  - lib/torrent_search/trackers/kick_ass/torrent.rb
192
194
  - lib/torrent_search/version.rb
195
+ - lib/torrent_search/views/base.rb
196
+ - lib/torrent_search/views/download.rb
197
+ - lib/torrent_search/views/menu.rb
198
+ - lib/torrent_search/views/search.rb
199
+ - spec/lib/torrent_search/controllers/download_spec.rb
200
+ - spec/lib/torrent_search/controllers/search_spec.rb
201
+ - spec/lib/torrent_search/services/download_spec.rb
193
202
  - spec/lib/torrent_search/trackers/kick_ass/scraper_spec.rb
194
203
  - spec/spec_helper.rb
195
204
  - spec/support/vcr_setup.rb
@@ -219,6 +228,9 @@ signing_key:
219
228
  specification_version: 4
220
229
  summary: Search various torrent sites
221
230
  test_files:
231
+ - spec/lib/torrent_search/controllers/download_spec.rb
232
+ - spec/lib/torrent_search/controllers/search_spec.rb
233
+ - spec/lib/torrent_search/services/download_spec.rb
222
234
  - spec/lib/torrent_search/trackers/kick_ass/scraper_spec.rb
223
235
  - spec/spec_helper.rb
224
236
  - spec/support/vcr_setup.rb
@@ -1,38 +0,0 @@
1
- require "httparty"
2
-
3
- module TorrentSearch
4
- DEFAULT_DIR = '.'
5
-
6
- class Download
7
- def initialize(view, torrent)
8
- @view = view
9
- @torrent = torrent
10
- end
11
-
12
- def perform
13
- save! filename
14
- say 'Complete', :green
15
- end
16
-
17
- private
18
- def filename
19
- File.join(path, "#{@torrent.filename}.torrent")
20
- end
21
-
22
- def path
23
- path = @view.ask "Directory to save file (default '#{DEFAULT_DIR}'):"
24
- path.empty? ? DEFAULT_DIR : path
25
- end
26
-
27
- def save!(filename)
28
- say "Downloading #{@torrent.name}...", :green
29
- File.open(filename, "wb") do |file|
30
- file.write HTTParty.get(@torrent.href).parsed_response
31
- end
32
- end
33
-
34
- def say(*args)
35
- @view.say *args
36
- end
37
- end
38
- end
@@ -1,80 +0,0 @@
1
- module TorrentSearch
2
-
3
- ACTIONS = {
4
- d: :download,
5
- s: :search,
6
- q: :quit
7
- }
8
-
9
- class Menu < SimpleDelegator
10
- def initialize(view, search_result)
11
- @view = view
12
- @search_result = search_result
13
- super @view
14
- end
15
-
16
- def display
17
- say_status "\n[Results]", '', :green
18
- if results?
19
- print_table result_table
20
- else
21
- say 'Nothing found'
22
- end
23
- say_status "\n[Actions]", actions, :blue
24
- choose_action!
25
- end
26
-
27
- private
28
- def results?
29
- @search_result.any?
30
- end
31
-
32
- def result_table
33
- ResultTable.new(@search_result)
34
- end
35
-
36
- def available_actions
37
- ACTIONS.dup.tap {|hash| hash.delete(:d) unless results? }
38
- end
39
-
40
- def actions
41
- available_actions.map {|hash| hash.join(": ")}.join("\t")
42
- end
43
-
44
- def choose_action!
45
- chosen = ask("Choose:").to_sym
46
- send available_actions.fetch(chosen, :invalid_command)
47
- end
48
-
49
- def invalid_command
50
- say 'Invalid command', :red
51
- choose_action!
52
- end
53
-
54
- def quit
55
- CLI::quit
56
- end
57
-
58
- def search
59
- super ask("Search for:").split
60
- end
61
-
62
- def download
63
- index = ask("Torrent (0-#{search_result_range.max}):")
64
- unless index.match(/\A\d+\z/) && is_in_search_result?(index)
65
- say 'Invalid option', :red
66
- download
67
- end
68
- torrent = @search_result[index.to_i]
69
- Download.new(@view, torrent).perform
70
- end
71
-
72
- def is_in_search_result?(index)
73
- search_result_range.member?(index.to_i)
74
- end
75
-
76
- def search_result_range
77
- @range ||= Range.new(0, @search_result.length, true)
78
- end
79
- end
80
- end