kat 0.3.0 → 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.
- checksums.yaml +4 -4
- data/bin/kat +127 -0
- data/kat.gemspec +4 -4
- data/lib/kat.rb +91 -35
- data/lib/kat/version.rb +3 -1
- data/test/kat_test.rb +76 -210
- metadata +38 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: a6ad7f5809e7cfbdc5379f566d93c61a361a51e6
|
4
|
+
data.tar.gz: 852186a99d1af3374c4de52f0bb91c45d3c3a5bc
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
data/kat.gemspec
CHANGED
@@ -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 = '
|
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 =
|
9
|
-
SEARCH_URL =
|
7
|
+
EMPTY_URL = 'new'
|
8
|
+
SEARCH_URL = 'usearch'
|
10
9
|
ADVANCED_URL = "#{KAT_URL}/torrents/search/advanced/"
|
11
10
|
|
12
|
-
STRING_FIELDS = [ :
|
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
|
-
#
|
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 =
|
38
|
-
|
39
|
+
@options = {}
|
40
|
+
|
41
|
+
self.query = search_term
|
42
|
+
self.options = opts
|
39
43
|
end
|
40
44
|
|
41
45
|
#
|
42
|
-
#
|
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
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
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
|
-
|
64
|
+
str.join '/'
|
61
65
|
end
|
62
66
|
|
63
67
|
#
|
64
|
-
# Change the search term
|
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
|
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
|
107
|
+
unless @results[page] or (@pages > -1 and page >= @pages)
|
105
108
|
begin
|
106
|
-
doc = Nokogiri::HTML(open("#{KAT_URL}/#{URI::encode(
|
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 =>
|
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
|
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, *
|
182
|
-
|
183
|
-
|
184
|
-
|
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
|
data/lib/kat/version.rb
CHANGED
data/test/kat_test.rb
CHANGED
@@ -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
|
-
|
9
|
+
let(:kat) { Kat.new 'test' }
|
10
|
+
let(:kat_opts) { Kat.new 'test', { :category => 'books' } }
|
18
11
|
|
19
|
-
describe '
|
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
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
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
|
-
|
46
|
-
|
47
|
-
|
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
|
-
|
52
|
-
|
53
|
-
|
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
|
-
|
61
|
-
|
62
|
-
|
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
|
-
|
83
|
-
|
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
|
-
|
90
|
-
|
91
|
-
|
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
|
-
|
177
|
-
|
178
|
-
|
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
|
-
|
183
|
-
|
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
|
-
|
194
|
-
|
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
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
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
|
-
|
210
|
-
|
211
|
-
|
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 '
|
216
|
-
it '
|
217
|
-
|
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
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
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
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
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
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
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
|
-
|
250
|
-
|
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.
|
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-
|
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: '
|
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: '
|
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:
|
92
|
+
rubyforge_project:
|
63
93
|
rubygems_version: 2.0.3
|
64
94
|
signing_key:
|
65
95
|
specification_version: 4
|