kat 0.3.0 → 1.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|