kat 0.3.0 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (7) hide show
  1. checksums.yaml +4 -4
  2. data/bin/kat +127 -0
  3. data/kat.gemspec +4 -4
  4. data/lib/kat.rb +91 -35
  5. data/lib/kat/version.rb +3 -1
  6. data/test/kat_test.rb +76 -210
  7. metadata +38 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6431728ef307439d3dff0b3f60e3619c0ae4d37c
4
- data.tar.gz: 914cef06e731347f85c4c8ba9c5363711d134e4a
3
+ metadata.gz: a6ad7f5809e7cfbdc5379f566d93c61a361a51e6
4
+ data.tar.gz: 852186a99d1af3374c4de52f0bb91c45d3c3a5bc
5
5
  SHA512:
6
- metadata.gz: d64ef6998d9d27411680c8aa08e42d6cf5d15dbe33a82122a9ede9ba04c75841e24a93b98c126207beadd3713e4d7102c67ccbd5fdd0411b716011050ed33030
7
- data.tar.gz: 7ec114ce7cf00ca98b9d94345c8cc1b6dbaf3aaf812334146fe58ae7e84f224cb92ec677c3e948d4593939e17d4058d3c4c11b967a75317fac8baa133668aa56
6
+ metadata.gz: cd2896742673ed05ccec2bd1ec63b2af9498911683a9b21e8ad0a7352d8311a2532f7ef8a26960266e4514d9a5aae979762acd710fde36b252159c74cb41d301
7
+ data.tar.gz: fc2812d838cc89f103fa9a52e6f2a3ae1345f466d9a732539eb1d26bc6adfa0971ff76961cd7296606aff6b83af9d8fa47639f34b66b2cb17a21905a9477b1a2
data/bin/kat ADDED
@@ -0,0 +1,127 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ e = []
4
+
5
+ require 'open-uri'
6
+ %w(kat trollop highline).sort.each do |lib|
7
+ begin
8
+ require lib
9
+ rescue LoadError
10
+ e << lib.sub(/_/, '')
11
+ end
12
+ end
13
+
14
+ unless e.empty?
15
+ puts <<-EOS
16
+ Kickass Torrents Search relies on #{e.join(', ').sub(/^(.*), /, '\1 and ')}. To download the gem#{'s' if e.size > 1} type:
17
+
18
+ gem install #{e.join ' '}
19
+
20
+ EOS
21
+ exit
22
+ end
23
+
24
+ VERSION_STR = "#{Kat::NAME} #{Kat::VERSION} (c) 2013 #{Kat::AUTHOR}"
25
+
26
+ list_args = { :categories => :category, :times => :added,
27
+ :languages => :language, :platforms => :platform }
28
+
29
+ options = Trollop::options do
30
+ version VERSION_STR
31
+ banner <<-EOS
32
+ #{VERSION_STR}
33
+
34
+ Usage: #{File.basename __FILE__} [options] <query>+
35
+
36
+ Options:
37
+ EOS
38
+
39
+ opt :exact, 'Exact phrase', :type => :string
40
+ opt :or, 'Optional words', :type => :string, :multi => true
41
+ opt :without, 'Without this word', :type => :string, :multi => true
42
+
43
+ opt :sort, 'Sort field (size, files, time_add, seeders, leechers)', :type => :string
44
+ opt :asc, 'Ascending sort order (descending is default)', :type => :boolean
45
+
46
+ opt :added, 'Age of the torrent', :type => :string, :short => :a
47
+ opt :category, 'Category', :type => :string, :short => :c
48
+ opt :files, 'Number of files', :type => :int
49
+ opt :imdb, 'IMDB ID', :type => :int
50
+ opt :seeds, 'Minimum number of seeders', :type => :int, :short => :s
51
+ opt :user, 'Uploader', :type => :string
52
+ opt :season, 'Television season', :type => :int
53
+ opt :episode, 'Television episode', :type => :int, :short => :e
54
+
55
+ opt :language, 'Language', :type => :int
56
+ opt :platform, 'Game platform', :type => :int
57
+
58
+ opt :safe, 'Family safety filter', :type => :boolean
59
+ opt :verified, 'Verified', :type => :boolean, :short => :m
60
+
61
+ opt :output, 'Directory to save torrents in', :type => :string, :short => :o
62
+
63
+ list_args.each {|k, v| opt k, "List the #{k} that may be used with --#{v}", :type => :boolean }
64
+ end
65
+
66
+ unless list_args.select! {|k, v| options[k] }.empty?
67
+ puts VERSION_STR
68
+ list_args.each do |opt, label|
69
+ args = Kat.send opt
70
+ puts "\n #{label.to_s.capitalize}"
71
+ puts unless args.values.first.is_a? Array
72
+ args.each {|k, v| puts v.is_a?(Array) ? "\n %12s => #{v.join "\n\t\t "}" % k : " %-23s => #{v}" % k }
73
+ puts
74
+ end
75
+ else
76
+ k = Kat.new ARGV.join(' '), options
77
+ h = HighLine.new
78
+ page = 0
79
+ puts VERSION_STR
80
+
81
+ loop do
82
+ r = k.search page
83
+ if r.nil?
84
+ puts "\nNo results"
85
+ break
86
+ end
87
+
88
+ n = page < k.pages - 1
89
+ p = page > 0
90
+
91
+ puts "\n%-72s S L\n\n" % "Page #{page + 1} of #{k.pages}"
92
+ r.each_with_index do |t, i|
93
+ puts "%2d. %-64s %5d %5d" % [ i + 1, t[:title][0..63], t[:seeds], t[:leeches] ]
94
+ end
95
+
96
+ commands = "[#{'n' if n}#{'p' if p}q]|"
97
+ _01to09 = "[1#{r.size > 9 ? '-9' : '-' + r.size.to_s}]"
98
+ _10to19 = "#{r.size > 9 ? '|1[0-' + (r.size > 19 ? '9' : (r.size - 10).to_s) + ']' : ''}"
99
+ _20to25 = "#{r.size > 19 ? '|2[0-' + (r.size - 20).to_s + ']' : ''}"
100
+ prompt = "\n1#{r.size > 1 ? '-' + r.size.to_s : ''} to download" +
101
+ "#{', (n)ext' if n}" +
102
+ "#{', (p)rev' if p}" +
103
+ ', (q)uit: '
104
+
105
+ case (answer = h.ask(prompt) {|q| q.validate = /^(#{commands}#{_01to09}#{_10to19}#{_20to25})$/ })
106
+ when 'q' then break
107
+ when 'n' then page += 1 if n
108
+ when 'p' then page -= 1 if p
109
+ else
110
+ if (1..r.size).include? answer.to_i
111
+ torrent = k.results[page][answer.to_i - 1]
112
+ puts "\nDownloading: #{torrent[:title]}"
113
+
114
+ begin
115
+ uri = URI torrent[:download]
116
+ uri.query = nil
117
+ response = uri.read
118
+ file = "#{File.expand_path(options[:output] || '.')}/#{torrent[:title].gsub(/ /, '.').gsub(/[^a-z0-9()_.-]/i, '')}.torrent"
119
+ File.open(file, 'w') {|f| f.write response }
120
+ rescue => e
121
+ puts e.message
122
+ end
123
+ end
124
+ end
125
+ end
126
+ puts
127
+ end
@@ -6,14 +6,12 @@ require 'kat/version'
6
6
  Gem::Specification.new do |s|
7
7
  s.name = 'kat'
8
8
  s.version = Kat::VERSION
9
- s.date = '2013-08-09'
9
+ s.date = Time.new.strftime '%Y-%m-%d'
10
10
  s.author = 'Fission Xuiptz'
11
11
  s.email = 'fissionxuiptz@softwaremojo.com'
12
12
  s.homepage = 'http://github.com/fissionxuiptz/kat'
13
13
  s.license = 'MIT'
14
14
 
15
- s.rubyforge_project = 'kat'
16
-
17
15
  s.summary = 'Kickass Torrents Interface'
18
16
  s.description = 'A Ruby interface to Kickass Torrents'
19
17
 
@@ -22,5 +20,7 @@ Gem::Specification.new do |s|
22
20
  s.test_files = s.files.grep(%r{^(test|spec|features)/})
23
21
  s.require_paths = [ 'lib' ]
24
22
 
25
- s.add_runtime_dependency 'nokogiri'
23
+ s.add_runtime_dependency 'nokogiri', '~> 1.6'
24
+ s.add_runtime_dependency 'highline', '~> 1.6'
25
+ s.add_runtime_dependency 'trollop', '~> 2.0'
26
26
  end
data/lib/kat.rb CHANGED
@@ -1,22 +1,24 @@
1
- # TODO: Lookup advanced search for language and platform values
2
-
3
1
  require 'nokogiri'
4
2
  require 'open-uri'
3
+ require 'kat/version'
5
4
 
6
5
  class Kat
7
6
  KAT_URL = 'http://kickass.to'
8
- EMPTY_URL = "new"
9
- SEARCH_URL = "usearch"
7
+ EMPTY_URL = 'new'
8
+ SEARCH_URL = 'usearch'
10
9
  ADVANCED_URL = "#{KAT_URL}/torrents/search/advanced/"
11
10
 
12
- STRING_FIELDS = [ :category, :seeds, :user, :age, :files, :imdb, :season, :episode ]
13
-
14
- # The names of these fields are transposed for ease of use
15
- SELECT_FIELDS = [ { :name => :language, :id => :lang_id }, { :name => :platform, :id => :platform_id } ]
11
+ STRING_FIELDS = [ :seeds, :user, :files, :imdb, :season, :episode ]
16
12
 
17
13
  # If these are set to anything but nil or false, they're turned on in the query
18
14
  SWITCH_FIELDS = [ :safe, :verified ]
19
15
 
16
+ # The names of these fields are transposed for ease of use
17
+ SELECT_FIELDS = [ { :name => :categories, :label => :category, :id => :category },
18
+ { :name => :times, :label => :added, :id => :age },
19
+ { :name => :languages, :label => :language, :id => :lang_id },
20
+ { :name => :platforms, :label => :platform, :id => :platform_id } ]
21
+
20
22
  SORT_FIELDS = %w(size files_count time_add seeders leechers)
21
23
 
22
24
  # The number of pages of results
@@ -25,21 +27,23 @@ class Kat
25
27
  # Any error in searching is stored here
26
28
  attr_reader :error
27
29
 
30
+ @@doc = nil
31
+
28
32
  #
29
33
  # Create a new +Kat+ object to search Kickass Torrents.
30
- #
31
- # The search term can be nil, a string/symbol, or an array of strings/symbols.
32
- #
33
- # Valid options are in STRING_FIELDS, SELECT_FIELDS or SWITCH_FIELDS
34
+ # The search_term can be nil, a string/symbol, or an array of strings/symbols.
35
+ # Valid options are in STRING_FIELDS, SELECT_FIELDS or SWITCH_FIELDS.
34
36
  #
35
37
  def initialize search_term = nil, opts = {}
36
38
  @search_term = []
37
- @options = opts.is_a?(Hash) ? opts : {}
38
- self.query = search_term.is_a?(Array) ? search_term.dup : search_term
39
+ @options = {}
40
+
41
+ self.query = search_term
42
+ self.options = opts
39
43
  end
40
44
 
41
45
  #
42
- # Use Kat.search(search_term) to do a quick search
46
+ # Kat.search will do a quick search and return the results
43
47
  #
44
48
  def self.search search_term
45
49
  self.new(search_term).search
@@ -48,21 +52,20 @@ class Kat
48
52
  #
49
53
  # Generate a query string from the stored options, supplying an optional page number
50
54
  #
51
- def query page = 0
52
- q = [ SEARCH_URL, @query.join(' ').gsub(/[^a-z0-9: _-]/i, '') ]
53
- q = [ EMPTY_URL ] if q[1].empty?
54
- q << page + 1 if page > 0
55
- q << if SORT_FIELDS.include? @options[:sort].to_s
55
+ def query_str page = 0
56
+ str = [ SEARCH_URL, @query.join(' ').gsub(/[^a-z0-9: _-]/i, '') ]
57
+ str = [ EMPTY_URL ] if str[1].empty?
58
+ str << page + 1 if page > 0
59
+ str << if SORT_FIELDS.include? @options[:sort].to_s
56
60
  "?field=#{options[:sort].to_s}&sorder=#{options[:asc] ? 'asc' : 'desc'}"
57
61
  else
58
62
  '' # ensure a trailing slash after the search terms or page number
59
63
  end
60
- q.join '/'
64
+ str.join '/'
61
65
  end
62
66
 
63
67
  #
64
- # Change the search term for the query, triggering a rebuild of the query string
65
- # and clearing past results.
68
+ # Change the search term, triggering a query rebuild and clearing past results.
66
69
  #
67
70
  # Raises ArgumentError if search_term is not a String, Symbol or Array
68
71
  #
@@ -84,7 +87,7 @@ class Kat
84
87
  end
85
88
 
86
89
  #
87
- # Change search options with a hash, triggering a rebuild of the query string and
90
+ # Change search options with a hash, triggering a query string rebuild and
88
91
  # clearing past results.
89
92
  #
90
93
  # Raises ArgumentError if opts is not a Hash
@@ -101,9 +104,9 @@ class Kat
101
104
  # cache results for subsequent calls of search with the same query string.
102
105
  #
103
106
  def search page = 0
104
- unless query.empty? or @results[page] or (@pages > -1 and page >= @pages)
107
+ unless @results[page] or (@pages > -1 and page >= @pages)
105
108
  begin
106
- doc = Nokogiri::HTML(open("#{KAT_URL}/#{URI::encode(query page)}"))
109
+ doc = Nokogiri::HTML(open("#{KAT_URL}/#{URI::encode(query_str page)}"))
107
110
  @results[page] = doc.css('td.torrentnameCell').map do |node|
108
111
  { :title => node.css('a.normalgrey').text,
109
112
  :magnet => node.css('a.imagnet').first.attributes['href'].value,
@@ -128,13 +131,21 @@ class Kat
128
131
  rescue => e
129
132
  # No result throws a 404 error.
130
133
  @pages = 0 if e.class == OpenURI::HTTPError and e.message['404 Not Found']
131
- @error = { :error => e, :query => query(page) }
134
+ @error = { :error => e, :query => query_str(page) }
132
135
  end
133
136
  end
134
137
 
135
138
  results[page]
136
139
  end
137
140
 
141
+ #
142
+ # For message chaining
143
+ #
144
+ def do_search page = 0
145
+ search page
146
+ self
147
+ end
148
+
138
149
  #
139
150
  # Get a copy of the results
140
151
  #
@@ -143,7 +154,14 @@ class Kat
143
154
  end
144
155
 
145
156
  #
146
- # If the method_sym or its plural is a field name in the results list, this will tell us
157
+ # If method_sym is a field name in SELECT_FIELDS, we can fetch the list of values.
158
+ #
159
+ def self.respond_to? method_sym, include_private = false
160
+ SELECT_FIELDS.find {|field| field[:name] == method_sym } ? true : super
161
+ end
162
+
163
+ #
164
+ # If method_sym or its plural is a field name in the results list, this will tell us
147
165
  # if we can fetch the list of values. It'll only happen after a successful search.
148
166
  #
149
167
  def respond_to? method_sym, include_private = false
@@ -170,20 +188,58 @@ private
170
188
  @query += @options[:without].map {|s| "-#{s}" } if @options[:without]
171
189
 
172
190
  STRING_FIELDS.each {|f| @query << "#{f}:#{@options[f]}" if @options[f] }
173
- SELECT_FIELDS.each {|f| @query << "#{f[:id]}:#{@options[f[:name]]}" if @options[f[:name]].to_i > 0 }
174
191
  SWITCH_FIELDS.each {|f| @query << "#{f}:1" if @options[f] }
192
+ SELECT_FIELDS.each do |f|
193
+ if (@options[f[:label]].to_s.to_i > 0 and f[:id].to_s['_id']) or
194
+ (@options[f[:label]] and not f[:id].to_s['_id'])
195
+ @query << "#{f[:id]}:#{@options[f[:label]]}"
196
+ end
197
+ end
198
+ end
199
+
200
+ #
201
+ # Get a list of options for a particular selection field from the advanced search form
202
+ #
203
+ # Raises an error unless the label is in SELECT_FIELDS
204
+ #
205
+ def self.field_options label
206
+ begin
207
+ raise 'Unknown search field' unless SELECT_FIELDS.find {|f| f[:label] == label.to_sym }
208
+
209
+ opts = (@@doc ||= Nokogiri::HTML(open(ADVANCED_URL))).css('table.formtable td').find do |e|
210
+ e.text[/#{label.to_s}/i]
211
+ end.next_element.first_element_child.children
212
+
213
+ unless (group = opts.css('optgroup')).empty?
214
+ # Categories
215
+ group.inject({}) {|c, og| c[og.attributes['label'].value] = og.children.map {|o| o.attributes['value'].value }; c }
216
+ else
217
+ # Times, languages, platforms
218
+ opts.reject {|o| o.attributes.empty? }.inject({}) {|p, o| p[o.text] = o.attributes['value'].value; p }
219
+ end
220
+ rescue => e
221
+ { :error => e }
222
+ end
223
+ end
224
+
225
+ #
226
+ # If method_sym is a field name in SELECT_FIELDS, fetch the list of values.
227
+ #
228
+ def self.method_missing method_sym, *args, &block
229
+ if respond_to? method_sym
230
+ return self.field_options SELECT_FIELDS.find {|field| field[:name] == method_sym }[:label]
231
+ end
232
+ super
175
233
  end
176
234
 
177
235
  #
178
236
  # If method_sym or its plural form is a field name in the results list, fetch the list of values.
179
237
  # Can only happen after a successful search.
180
238
  #
181
- def method_missing method_sym, *arguments, &block
182
- # Don't need no fancy schmancy pluralizing method. Just try chopping off the 's'.
183
- m = method_sym.to_s.chop.to_sym
184
- if not (@results.empty? or @results.last.empty?) and
185
- (@results.last.first[method_sym] or @results.last.first[m])
186
- return @results.compact.map {|rs| rs.map {|r| r[method_sym] || r[m] } }.flatten
239
+ def method_missing method_sym, *args, &block
240
+ if respond_to? method_sym
241
+ # Don't need no fancy schmancy singularizing method. Just try chopping off the 's'.
242
+ return @results.compact.map {|rs| rs.map {|r| r[method_sym] || r[method_sym.to_s.chop.to_sym] } }.flatten
187
243
  end
188
244
  super
189
245
  end
@@ -1,3 +1,5 @@
1
1
  class Kat
2
- VERSION = '0.3.0'
2
+ NAME = 'Kickass Torrents Search'
3
+ VERSION = '1.0.0'
4
+ AUTHOR = 'Fission Xuiptz'
3
5
  end
@@ -1,256 +1,122 @@
1
1
  require 'minitest/autorun'
2
-
3
- begin
4
- require 'minitest/pride'
5
- rescue LoadError
6
- end
7
-
8
2
  require 'kat'
9
3
 
4
+ blue_peter = Kat.new
5
+ blue_peter.do_search.do_search 1
6
+
10
7
  describe Kat do
11
- before do
12
- @kat = { :vanilla => Kat.new,
13
- :basic => Kat.new('test'),
14
- :advanced => Kat.new('test', { :category => 'books' }) }
15
- end
16
8
 
17
- # Quick search tests
9
+ let(:kat) { Kat.new 'test' }
10
+ let(:kat_opts) { Kat.new 'test', { :category => 'books' } }
18
11
 
19
- describe 'when quick searching' do
20
- it 'returns a result set' do
21
- Kat.search('test').must_be_instance_of Array
12
+ describe 'basic search' do
13
+ it 'returns a full result set' do
22
14
  Kat.search('test').size.must_equal 25
23
15
  end
24
16
  end
25
17
 
26
- # Vanilla query tests
27
-
28
- describe 'when checking if a vanilla query responds to result field before searching' do
29
- it 'returns false' do
30
- [ :titles, :magnets, :downloads, :sizes, :files, :ages, :seeds, :leeches ].each do |s|
31
- @kat[:vanilla].respond_to?(s).must_equal false
32
- end
33
- end
34
- end
35
-
36
- describe 'when checking if a vanilla query responds to result fields after searching' do
37
- it 'returns true' do
38
- @kat[:vanilla].search
39
- [ :titles, :magnets, :downloads, :sizes, :files, :ages, :seeds, :leeches ].each do |s|
40
- @kat[:vanilla].respond_to?(s).must_equal true
41
- end
18
+ describe 'advanced search' do
19
+ it 'returns a valid query string' do
20
+ blue_peter.query_str.must_equal 'new/'
21
+ blue_peter.query_str(1).must_equal 'new/2/'
22
+ kat.query_str.must_equal 'usearch/test/'
23
+ kat_opts.query_str(1).must_equal 'usearch/test category:books/2/'
24
+ kat_opts.query = :foobar
25
+ kat_opts.query_str(1).must_equal 'usearch/foobar category:books/2/'
26
+ kat_opts.query = [ 0, {}, [ :test, 0..1, [ 'user:foo' ] ] ]
27
+ kat_opts.query_str(1).must_equal 'usearch/test user:foo category:books/2/'
42
28
  end
43
- end
44
29
 
45
- describe 'when building a vanilla query' do
46
- it 'returns a query to new torrents' do
47
- @kat[:vanilla].query.must_equal 'new/'
30
+ it 'returns a valid query string based on many options' do
31
+ kat_opts.options = { :files => 2, :safe => true, :language => 2, :sort => :files_count, :asc => true, :seeds => 2 }
32
+ kat_opts.query_str(1).must_equal 'usearch/test seeds:2 files:2 safe:1 category:books lang_id:2/2/?field=files_count&sorder=asc'
48
33
  end
49
- end
50
34
 
51
- describe 'when searching with a vanilla query' do
52
- it 'returns a full result set' do
53
- @kat[:vanilla].search.must_be_instance_of Array
54
- [ :search, :titles, :magnets, :downloads, :sizes, :files, :ages, :seeds, :leeches ].each do |s|
55
- @kat[:vanilla].send(s).size.must_equal 25
35
+ it 'wont respond to result fields before a search' do
36
+ [ :titles, :files ].each do |s|
37
+ kat.respond_to?(s).must_equal false
56
38
  end
57
39
  end
58
- end
59
40
 
60
- describe 'when searching the 2nd page of a vanilla query' do
61
- it 'returns a full result set' do
62
- @kat[:vanilla].search(1).must_be_instance_of Array
63
- @kat[:vanilla].search(1).size.must_equal 25
64
- end
65
- end
66
-
67
- describe 'when searching 2 pages of a vanilla query' do
68
- it 'returns 50 results for each result field' do
69
- @kat[:vanilla].search
70
- @kat[:vanilla].search(1)
71
- if @kat[:vanilla].respond_to? :titles
72
- [ :titles, :magnets, :downloads, :sizes, :files, :ages, :seeds, :leeches ].each do |s|
73
- @kat[:vanilla].send(s).size.must_equal 50
74
- end
75
- else
76
- # Returning a failure, not an error
77
- proc { @kat[:vanilla].titles }.wont_raise NoMethodError
41
+ it 'responds to result fields after a search' do
42
+ [ :titles, :files ].each do |s|
43
+ blue_peter.respond_to?(s).must_equal true
78
44
  end
79
45
  end
80
- end
81
46
 
82
- describe 'when rebuilding a vanilla query' do
83
- it 'returns a query to new torrents' do
84
- @kat[:vanilla].query = nil
85
- @kat[:vanilla].query.must_equal 'new/'
47
+ it 'returns identical result sets' do
48
+ blue_peter.results[0].must_equal blue_peter.search
86
49
  end
87
- end
88
50
 
89
- describe 'when changing the search term on a vanilla query' do
90
- it 'returns a query to usearch' do
91
- @kat[:vanilla].query = 'test'
92
- @kat[:vanilla].query.must_equal 'usearch/test/'
93
- end
94
- end
95
-
96
- describe 'when adding an array of search terms to a vanilla query' do
97
- it 'returns a query to usearch' do
98
- @kat[:vanilla].query = [ 'test', 'category:books' ]
99
- @kat[:vanilla].query.must_equal 'usearch/test category:books/'
100
- end
101
- end
102
-
103
- describe 'when adding an array of crap to a vanilla query' do
104
- it 'returns a valid query' do
105
- @kat[:vanilla].query = [ 0, {}, [ 'test', 0..1 ] ]
106
- @kat[:vanilla].query.must_equal 'usearch/test/'
107
- end
108
- end
109
-
110
- # Basic query tests
111
-
112
- describe 'when checking if a basic query responds to result fields after searching' do
113
- it 'returns true' do
114
- @kat[:basic].search
115
- [ :titles, :magnets, :downloads, :sizes, :files, :ages, :seeds, :leeches ].each do |s|
116
- @kat[:basic].respond_to?(s).must_equal true
117
- end
118
- end
119
- end
120
-
121
- describe 'when building a basic query' do
122
- it 'returns a query to usearch' do
123
- @kat[:basic].query.must_equal 'usearch/test/'
124
- end
125
- end
126
-
127
- describe 'when building a basic query with pages' do
128
- it 'returns a query to usearch with page numbers' do
129
- @kat[:basic].query(1).must_equal 'usearch/test/2/'
130
- end
131
- end
132
-
133
- describe 'when changing the search term of a basic query to nil' do
134
- it 'returns a query to new' do
135
- @kat[:basic].query = nil
136
- @kat[:basic].query.must_equal 'new/'
137
- end
138
- end
139
-
140
- describe 'when adding options to a basic query' do
141
- it 'returns a query to usearch with options' do
142
- @kat[:basic].options = { :category => 'movies' }
143
- @kat[:basic].options.must_equal({ :category => 'movies' })
144
- @kat[:basic].query.must_equal 'usearch/test category:movies/'
145
- @kat[:basic].results.must_be_empty
146
- @kat[:basic].pages.must_be :==, -1
147
- end
148
- end
149
-
150
- describe 'when adding options to a basic query with pages' do
151
- it 'returns a query to usearch with options and page numbers' do
152
- @kat[:basic].options = { :category => 'movies' }
153
- @kat[:basic].options.must_equal({ :category => 'movies' })
154
- @kat[:basic].query(1).must_equal 'usearch/test category:movies/2/'
155
- @kat[:basic].results.must_be_empty
156
- @kat[:basic].pages.must_be :==, -1
157
- end
158
- end
159
-
160
- describe 'when searching with a basic query' do
161
- it 'returns a result set' do
162
- @kat[:basic].search.must_be_instance_of Array
163
- @kat[:basic].search.size.must_equal 25
164
- @kat[:basic].pages.must_be :>, 0
165
- end
166
- end
167
-
168
- describe 'when searching the 2nd page of a basic query' do
169
- it 'returns a result set' do
170
- @kat[:basic].search(1).must_be_instance_of Array
171
- @kat[:basic].search(1).size.must_equal 25
172
- @kat[:basic].pages.must_be :>, 0
51
+ it 'returns a full result set' do
52
+ blue_peter.search(1).size.must_equal 25
53
+ blue_peter.titles.size.must_equal 50
173
54
  end
174
- end
175
55
 
176
- describe 'when searching with the same basic query twice' do
177
- it 'returns the same result set' do
178
- @kat[:basic].search.must_equal @kat[:basic].search
56
+ it 'returns a valid query string with options' do
57
+ bp = blue_peter.dup
58
+ bp.options = { :user => :foobar }
59
+ bp.options.must_equal({ :user => :foobar })
60
+ bp.query_str(1).must_equal 'usearch/user:foobar/2/'
61
+ bp.results.must_be_empty
62
+ bp.pages.must_equal(-1)
179
63
  end
180
- end
181
64
 
182
- describe 'when searching for something that does not exist in a basic query' do
183
- it 'returns an empty set, sets an error and sets pages to 0' do
184
- @kat[:basic].query = 'owijefbvoweivf'
185
- @kat[:basic].search.must_be_nil
186
- @kat[:basic].error.must_be_instance_of Hash
187
- @kat[:basic].error[:error].must_be_instance_of OpenURI::HTTPError
188
- @kat[:basic].error[:error].message.must_equal '404 Not Found'
189
- @kat[:basic].pages.must_equal 0
65
+ it 'can raise an ArgumentError for query=' do
66
+ proc { kat.query = 0 }.must_raise ArgumentError
190
67
  end
191
- end
192
68
 
193
- describe 'when searching for something with 1 page of results in a basic query' do
194
- it 'returns a result set and sets the pages to 1' do
195
- @kat[:basic].options = { :category => 'wallpapers' }
196
- @kat[:basic].options.must_equal({ :category => 'wallpapers' })
197
- @kat[:basic].search.wont_be_nil
198
- @kat[:basic].search.size.wont_equal 0
69
+ it 'can raise an ArgumentError for options=' do
70
+ proc { kat.options = 'foobar' }.must_raise ArgumentError
199
71
  end
200
- end
201
72
 
202
- describe 'when passing query= a Symbol' do
203
- it 'works just like a String' do
204
- @kat[:basic].query = :test
205
- @kat[:basic].query.must_equal 'usearch/test/'
73
+ it 'works when there are fewer than 25 results' do
74
+ kat.options = { :category => :wallpapers }
75
+ kat.search.wont_be_nil
76
+ kat.search.size.wont_equal 0
77
+ kat.pages.must_equal 1
206
78
  end
207
- end
208
79
 
209
- describe 'when passing a non-String-or-Array object to query=' do
210
- it 'raises an ArgumentError' do
211
- proc { @kat[:basic].query = { :foo => 'bar' } }.must_raise ArgumentError
80
+ it 'can return 0 results, set an error and set pages to 0' do
81
+ kat.query = 'owijefbvoweivf'
82
+ kat.search.must_be_nil
83
+ kat.error[:error].must_be_instance_of OpenURI::HTTPError
84
+ kat.error[:error].message.must_equal '404 Not Found'
85
+ kat.pages.must_equal 0
212
86
  end
213
87
  end
214
88
 
215
- describe 'when passing a non-Hash object to options=' do
216
- it 'raises an ArgumentError' do
217
- proc { @kat[:basic].options = 'foobar' }.must_raise ArgumentError
89
+ describe 'field options' do
90
+ it 'returns a list of time added options' do
91
+ times = Kat.times
92
+ times.must_be_instance_of Hash
93
+ times.wont_be_empty
94
+ times[:error].must_be_nil
218
95
  end
219
- end
220
96
 
221
- # Advanced query tests
222
-
223
- describe 'when building an advanced query' do
224
- it 'returns a query with options to usearch' do
225
- @kat[:advanced].query.must_equal 'usearch/test category:books/'
226
- end
227
- end
228
-
229
- describe 'when building an advanced query with pages' do
230
- it 'returns a query with options and page numbers' do
231
- @kat[:advanced].query(1).must_equal 'usearch/test category:books/2/'
97
+ it 'returns a list of categories' do
98
+ categories = Kat.categories
99
+ categories.must_be_instance_of Hash
100
+ categories.wont_be_empty
101
+ categories[:error].must_be_nil
232
102
  end
233
- end
234
103
 
235
- describe 'when building an advanced query with select fields' do
236
- it 'returns a query with options to usearch' do
237
- @kat[:advanced].options = { :language => 2 }
238
- @kat[:advanced].query.must_equal 'usearch/test category:books lang_id:2/'
104
+ it 'returns a list of languages' do
105
+ languages = Kat.languages
106
+ languages.must_be_instance_of Hash
107
+ languages.wont_be_empty
108
+ languages[:error].must_be_nil
239
109
  end
240
- end
241
110
 
242
- describe 'when using symbols instead of strings in an advanced query' do
243
- it 'returns the same query string' do
244
- @kat[:advanced].options = { :category => :books }
245
- @kat[:advanced].query.must_equal 'usearch/test category:books/'
111
+ it 'returns a list of platforms' do
112
+ platforms = Kat.platforms
113
+ platforms.must_be_instance_of Hash
114
+ platforms.wont_be_empty
115
+ platforms[:error].must_be_nil
246
116
  end
247
- end
248
117
 
249
- describe 'when sorting fields' do
250
- it 'adds a sort part to the query string' do
251
- @kat[:advanced].options = { :sort => :size }
252
- @kat[:advanced].query.must_equal 'usearch/test category:books/?field=size&sorder=desc'
118
+ it 'returns an error' do
119
+ Kat.field_options(:foobar)[:error].must_be_instance_of RuntimeError
253
120
  end
254
121
  end
255
-
256
122
  end
metadata CHANGED
@@ -1,32 +1,61 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kat
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.3.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fission Xuiptz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2013-08-09 00:00:00.000000000 Z
11
+ date: 2013-08-14 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - '>='
17
+ - - ~>
18
18
  - !ruby/object:Gem::Version
19
- version: '0'
19
+ version: '1.6'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - '>='
24
+ - - ~>
25
25
  - !ruby/object:Gem::Version
26
- version: '0'
26
+ version: '1.6'
27
+ - !ruby/object:Gem::Dependency
28
+ name: highline
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: '1.6'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: '1.6'
41
+ - !ruby/object:Gem::Dependency
42
+ name: trollop
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: '2.0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: '2.0'
27
55
  description: A Ruby interface to Kickass Torrents
28
56
  email: fissionxuiptz@softwaremojo.com
29
- executables: []
57
+ executables:
58
+ - kat
30
59
  extensions: []
31
60
  extra_rdoc_files: []
32
61
  files:
@@ -36,6 +65,7 @@ files:
36
65
  - LICENSE
37
66
  - README.md
38
67
  - Rakefile
68
+ - bin/kat
39
69
  - kat.gemspec
40
70
  - lib/kat.rb
41
71
  - lib/kat/version.rb
@@ -59,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
59
89
  - !ruby/object:Gem::Version
60
90
  version: '0'
61
91
  requirements: []
62
- rubyforge_project: kat
92
+ rubyforge_project:
63
93
  rubygems_version: 2.0.3
64
94
  signing_key:
65
95
  specification_version: 4