kat 2.0.5 → 2.0.6

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: d11aee9f32fb16ec8fc1c4927eeeb2561bab56f1
4
- data.tar.gz: 9e2a9bc6ffa80b85424ee74f2617da798c1e6ba4
3
+ metadata.gz: ef9cbd9bc5edfd5bcbbab270423445df0e0ffec2
4
+ data.tar.gz: 57b4de26084d944ad5e369f38e8727442bad663a
5
5
  SHA512:
6
- metadata.gz: 1ccc89e6bcfd9e9a9e88a0cc97b210a79b5a9250e72071aa4a1e23a3203275d3d9785f870be21da77adea98cca34cb55c63efd8835ffec3259b6c87a57786454
7
- data.tar.gz: a7cf13cf4d2300a6d4616e49c17afe7688089109ee0d346de8f5ba8d60a48cccb5399da78e5d69b9e5491f828974a4cf5c788a1cd9ac957c8c6027c96631f7dd
6
+ metadata.gz: 509efe18911af40dfdaf17f3c86196d4990f833a500ebc2c68925d6fc0710fa86690beb266343dbb7d1134c6e171d2c578a31ded5830a2eb837378178663a135
7
+ data.tar.gz: 588d18d574b744c7f026d51fcfb2da40e8e0689dc64a94bf9f2ca9722ed3c8fac230281ee0c59007d30d2eadc9b2c65f74c97465ee6044ea3ac0ac3ed0e719b7
data/Rakefile CHANGED
@@ -1,7 +1,7 @@
1
1
  require 'bundler/gem_tasks'
2
2
  require 'rake/testtask'
3
3
 
4
- task :default => :test
4
+ task default: :test
5
5
 
6
6
  Rake::TestTask.new do |t|
7
7
  t.libs.push 'lib'
@@ -15,9 +15,9 @@ Gem::Specification.new do |s|
15
15
  s.summary = 'Kickass Torrents Interface'
16
16
  s.description = 'A Ruby interface to Kickass Torrents'
17
17
 
18
- s.files = `git ls-files`.split $/
19
- s.executables = s.files.grep(%r{^bin/}) { |f| File.basename f }
20
- s.test_files = s.files.grep(%r{^(test|spec|features)/})
18
+ s.files = `git ls-files`.split "\n"
19
+ s.executables = s.files.grep(/^bin\//) { |f| File.basename f }
20
+ s.test_files = s.files.grep(/^(test|spec|features)\//)
21
21
  s.require_paths = ['lib']
22
22
 
23
23
  s.add_runtime_dependency 'nokogiri', '~> 1.6'
@@ -6,14 +6,11 @@ require 'highline'
6
6
  require 'yaml'
7
7
 
8
8
  module Kat
9
-
10
- class << self
11
- #
12
- # Convenience method for the App class
13
- #
14
- def app(args = ARGV)
15
- App.new(args).main
16
- end
9
+ #
10
+ # Convenience method for the App class
11
+ #
12
+ def self.app(args = ARGV)
13
+ App.new(args).main
17
14
  end
18
15
 
19
16
  class App
@@ -48,16 +45,16 @@ module Kat
48
45
  #
49
46
  def init_options(args = nil)
50
47
  @args = case args
51
- when nil then []
52
- when String then args.split
53
- else args
54
- end
48
+ when nil then []
49
+ when String then args.split
50
+ else args
51
+ end
55
52
 
56
53
  @options = load_config || {}
57
54
 
58
- Kat.options(@args).tap { |o|
55
+ Kat.options(@args).tap do |o|
59
56
  @options.merge!(o) { |k, ov, nv| o["#{ k }_given".intern] ? nv : ov }
60
- }
57
+ end
61
58
 
62
59
  Kat::Colour.colour = @options[:colour]
63
60
  rescue NoMethodError => e
@@ -82,16 +79,16 @@ module Kat
82
79
  def main
83
80
  puts VERSION_STR
84
81
 
85
- Kat::Search.selects.select { |k, v| @options[v[:select]] }.tap { |lists|
82
+ Kat::Search.selects.select { |k, v| @options[v[:select]] }.tap do |lists|
86
83
  if lists.empty?
87
84
  while running; end
88
85
  else
89
86
  puts format_lists lists
90
87
  end
91
- }
88
+ end
92
89
  end
93
90
 
94
- private
91
+ private
95
92
 
96
93
  #
97
94
  # Get the width of the terminal window
@@ -139,7 +136,7 @@ module Kat
139
136
 
140
137
  -> {
141
138
  i = 0
142
- while searching do
139
+ while searching
143
140
  print "\rSearching...".yellow + '\\|/-'[i % 4]
144
141
  i += 1
145
142
  sleep 0.1
@@ -147,7 +144,7 @@ module Kat
147
144
  }
148
145
  ].map { |w| Thread.new { w.call } }.each(&:join)
149
146
 
150
- puts (res = format_results)
147
+ puts((res = format_results))
151
148
 
152
149
  if res.size > 1
153
150
  case (answer = prompt)
@@ -156,7 +153,7 @@ module Kat
156
153
  when 'p' then @page -= 1 if prev?
157
154
  when 'q' then return false
158
155
  else
159
- if (1..@kat.results[@page].size).include? (answer = answer.to_i)
156
+ if (1..@kat.results[@page].size).include?((answer = answer.to_i))
160
157
  print "\nDownloading".yellow <<
161
158
  ": #{ @kat.results[@page][answer - 1][:title] }... "
162
159
  puts download @kat.results[@page][answer - 1]
@@ -173,21 +170,21 @@ module Kat
173
170
  # Format a list of options
174
171
  #
175
172
  def format_lists(lists)
176
- lists.inject([nil]) { |buf, (k, v)|
177
- opts = Kat::Search.send(v[:select])
178
- buf << v[:select].to_s.capitalize
179
- buf << nil unless Array === opts.values.first
173
+ lists.inject([nil]) do |buf, (_, val)|
174
+ opts = Kat::Search.send(val[:select])
175
+ buf << val[:select].to_s.capitalize
176
+ buf << nil unless opts.values.first.is_a? Array
180
177
  width = opts.keys.sort { |a, b| b.size <=> a.size }.first.size
181
- opts.each { |k, v|
182
- buf += if Array === v
183
- [nil, "%#{ width }s => #{ v.shift }" % k] +
184
- v.map { |e| ' ' * (width + 4) + e }
185
- else
186
- ["%-#{ width }s => #{ v }" % k]
187
- end
188
- }
178
+ opts.each do |k, v|
179
+ buf += if v.is_a? Array
180
+ [nil, "%#{ width }s => #{ v.shift }" % k] +
181
+ v.map { |e| ' ' * (width + 4) + e }
182
+ else
183
+ ["%-#{ width }s => #{ v }" % k]
184
+ end
185
+ end
189
186
  buf << nil
190
- }
187
+ end
191
188
  end
192
189
 
193
190
  #
@@ -208,14 +205,14 @@ module Kat
208
205
  buf << ("\r%-#{ main_width + 5 }s#{ ' Size Age Seeds Leeches' if !hide_info? || @show_info }" %
209
206
  ["Page #{ page + 1 } of #{ @kat.pages }", nil]).yellow
210
207
 
211
- @kat.results[@page].each_with_index { |t, i|
208
+ @kat.results[@page].each_with_index do |t, i|
212
209
  age = t[:age].split "\xC2\xA0"
213
- age = "%3d %-6s" % age
210
+ age = '%3d %-6s' % age
214
211
  # Filter out the crap that invariably infests torrent names
215
212
  title = t[:title].codepoints.map { |c| c > 31 && c < 127 ? c.chr : '?' }.join[0...main_width]
216
213
  buf << ("%2d. %-#{ main_width }s#{ ' %10s %10s %7d %7d' if !hide_info? or @show_info }" %
217
214
  [i + 1, title, t[:size], age, t[:seeds], t[:leeches]]).tap { |s| s.red! if t[:seeds] == 0 }
218
- }
215
+ end
219
216
 
220
217
  buf << nil
221
218
  end
@@ -242,12 +239,12 @@ module Kat
242
239
  "#{ ', ' << '(n)'.cyan(true) << 'ext' if next? }" <<
243
240
  "#{ ', ' << '(p)'.cyan(true) << 'rev' if prev? }" <<
244
241
  "#{ ", #{ @show_info ? 'hide' : 'show' } " << '(i)'.cyan(true) << 'nfo' if hide_info? }" <<
245
- ', ' << '(q)'.cyan(true) << 'uit: ') { |q|
242
+ ', ' << '(q)'.cyan(true) << 'uit: ') do |q|
246
243
  q.responses[:not_valid] = 'Invalid option.'
247
244
  q.validate = validation_regex
248
- }
249
- rescue RegexpError => e
250
- puts (@kat.pages > 0 ? "Error reading the page\n" : "Could not connect to the site\n").red
245
+ end
246
+ rescue RegexpError
247
+ puts((@kat.pages > 0 ? "Error reading the page\n" : "Could not connect to the site\n").red)
251
248
 
252
249
  return 'q'
253
250
  end
@@ -260,12 +257,12 @@ module Kat
260
257
 
261
258
  uri = URI(URI.encode torrent[:download])
262
259
  uri.query = nil
263
- file = "#{ @options[:output] || '.' }/" <<
260
+ file = "#{ @options[:output] || '.' }/" \
264
261
  "#{ torrent[:title].tr(' ', ?.).gsub(/[^a-z0-9()_.-]/i, '') }.torrent"
265
262
 
266
- fail '404 File Not Found' if (res = Net::HTTP.start(uri.host) { |http|
263
+ fail '404 File Not Found' if (res = Net::HTTP.start(uri.host) do |http|
267
264
  http.get uri
268
- }).code == '404'
265
+ end).code == '404'
269
266
 
270
267
  File.open(File.expand_path(file), 'w') { |f| f.write res.body }
271
268
 
@@ -278,13 +275,11 @@ module Kat
278
275
  # Load options from CONFIG if it exists
279
276
  #
280
277
  def load_config
281
- (symbolise = -> h {
282
- Hash === h ? Hash[h.map { |k, v| [k.intern, symbolise[v]] }] : h
283
- })[YAML.load_file CONFIG] if File.readable? CONFIG
278
+ (symbolise = lambda do |h|
279
+ h.is_a?(Hash) ? Hash[h.map { |k, v| [k.intern, symbolise[v]] }] : h
280
+ end)[YAML.load_file CONFIG] if File.readable? CONFIG
284
281
  rescue => e
285
282
  warn "Failed to load #{ CONFIG }: #{ e }"
286
283
  end
287
-
288
284
  end
289
-
290
285
  end
@@ -1,23 +1,19 @@
1
1
  module Kat
2
-
3
2
  module Colour
4
-
5
3
  COLOURS = %w(black red green yellow blue magenta cyan white)
6
4
 
7
- class << self
8
- # From AwesomePrint.colorize? by Michael Dvorkin
9
- # https://github.com/michaeldv/awesome_print/blob/master/lib/awesome_print/inspector.rb
10
- def capable?
11
- STDOUT.tty? && (ENV['TERM'] && ENV['TERM'] != 'dumb' || ENV['ANSICON'])
12
- end
5
+ # From AwesomePrint.colorize? by Michael Dvorkin
6
+ # https://github.com/michaeldv/awesome_print/blob/master/lib/awesome_print/inspector.rb
7
+ def self.capable?
8
+ STDOUT.tty? && (ENV['TERM'] && ENV['TERM'] != 'dumb' || ENV['ANSICON'])
9
+ end
13
10
 
14
- def colour=(f)
15
- @@colour = f && capable?
16
- end
11
+ def self.colour=(f)
12
+ @@colour = f && capable?
13
+ end
17
14
 
18
- def colour?
19
- @@colour
20
- end
15
+ def self.colour?
16
+ @@colour
21
17
  end
22
18
 
23
19
  @@colour = capable?
@@ -26,14 +22,14 @@ module Kat
26
22
  @@colour
27
23
  end
28
24
 
29
- COLOURS.each { |c|
25
+ COLOURS.each do |c|
30
26
  define_method(c) { |*args| colour c, args[0] }
31
27
  define_method("#{ c }!") { |*args| colour! c, args[0] }
32
- }
28
+ end
33
29
 
34
30
  def uncolour
35
31
  case self
36
- when String then gsub /\e\[[0-9;]+?m(.*?)\e\[0m/, '\\1'
32
+ when String then gsub(/\e\[[0-9;]+?m(.*?)\e\[0m/, '\\1')
37
33
  when Array then map { |e| e.uncolour if e }
38
34
  else self
39
35
  end
@@ -48,12 +44,15 @@ module Kat
48
44
  self
49
45
  end
50
46
 
51
- private
47
+ private
52
48
 
53
49
  def colour(name, intense = false)
54
50
  return case self
55
- when String, Symbol then "\e[#{ intense ? 1 : 0 };#{ 30 + COLOURS.index(name) }m#{ self }\e[0m"
56
- when Array then map { |e| e.send name.to_s, intense if e }
51
+ when String, Symbol
52
+ "\e[#{ intense ? 1 : 0 };" \
53
+ "#{ 30 + COLOURS.index(name) }m#{ self }\e[0m"
54
+ when Array
55
+ map { |e| e.send name.to_s, intense if e }
57
56
  end if colour?
58
57
 
59
58
  self
@@ -62,14 +61,12 @@ module Kat
62
61
  def colour!(name, intense = false)
63
62
  case self
64
63
  when String then replace send(name.to_s, intense)
65
- when Array then each { |e| e.send "#{ name.to_s }!", intense if e }
64
+ when Array then each { |e| e.send "#{ name }!", intense if e }
66
65
  end if colour?
67
66
 
68
67
  self
69
68
  end
70
-
71
69
  end
72
-
73
70
  end
74
71
 
75
72
  class String; include Kat::Colour end
@@ -1,5 +1,4 @@
1
1
  module Kat
2
-
3
2
  FIELD_MAP = {
4
3
  exact: { type: :string, desc: 'Exact phrase' },
5
4
  or: { type: :string, desc: 'Optional words', multi: true },
@@ -29,5 +28,4 @@ module Kat
29
28
  output: { type: :string, desc: 'Directory to save torrents in', short: :o },
30
29
  colour: { desc: 'Output with colour', short: :none }
31
30
  }.freeze
32
-
33
31
  end
@@ -3,57 +3,50 @@ require File.dirname(__FILE__) + '/version'
3
3
  require 'trollop'
4
4
 
5
5
  module Kat
6
-
7
- class << self
8
- #
9
- # Convenience method for the Options class
10
- #
11
- def options(args)
12
- Options.parse args
13
- end
6
+ #
7
+ # Convenience method for the Options class
8
+ #
9
+ def self.options(args)
10
+ Options.parse args
14
11
  end
15
12
 
16
13
  class Options
14
+ #
15
+ # Pick out the invocation options from the field map
16
+ #
17
+ def self.options_map
18
+ fields = %i(desc type multi select short)
17
19
 
18
- class << self
19
- #
20
- # Pick out the invocation options from the field map
21
- #
22
- def options_map
23
- fields = %i(desc type multi select short)
24
-
25
- FIELD_MAP.inject({}) { |hash, (k, v)|
26
- hash.tap { |h| h[k] = v.select { |f| fields.include? f } if v[:desc] }
27
- }
20
+ FIELD_MAP.reduce({}) do |hash, (k, v)|
21
+ hash.tap { |h| h[k] = v.select { |f| fields.include? f } if v[:desc] }
28
22
  end
23
+ end
29
24
 
30
- def parse(args)
31
- Trollop::options(args) {
32
- version VERSION_STR
33
- banner <<-USAGE.gsub /^\s+\|/, ''
34
- |#{ VERSION_STR }
35
- |
36
- |Usage: #{ File.basename __FILE__ } [options] <query>+
37
- |
38
- | Options:
39
- USAGE
40
-
41
- Options.options_map.each { |k, v|
42
- opt k,
43
- v[:desc],
44
- { type: v[:type] || :boolean,
45
- multi: v[:multi],
46
- short: v[:short] }
47
- }
48
-
49
- Options.options_map.each { |k, v|
50
- opt v[:select],
51
- "List the #{ v[:select] } that may be used with --#{ k }",
52
- short: :none if v[:select]
53
- }
54
- }
25
+ def self.parse(args)
26
+ Trollop.options(args) do
27
+ version VERSION_STR
28
+ banner <<-USAGE.gsub(/^\s+\|/, '')
29
+ |#{ VERSION_STR }
30
+ |
31
+ |Usage: #{ File.basename __FILE__ } [options] <query>+
32
+ |
33
+ | Options:
34
+ USAGE
35
+
36
+ Options.options_map.each do |k, v|
37
+ opt k,
38
+ v[:desc],
39
+ type: v[:type] || :boolean,
40
+ multi: v[:multi],
41
+ short: v[:short]
42
+ end
43
+
44
+ Options.options_map.each do |k, v|
45
+ opt v[:select],
46
+ "List the #{ v[:select] } that may be used with --#{ k }",
47
+ short: :none if v[:select]
48
+ end
55
49
  end
56
50
  end
57
-
58
51
  end
59
52
  end
@@ -5,23 +5,20 @@ require 'net/http'
5
5
  require 'andand'
6
6
 
7
7
  module Kat
8
-
9
8
  BASE_URL = 'http://kickass.to'
10
9
  RECENT_PATH = 'new'
11
10
  SEARCH_PATH = 'usearch'
12
11
  ADVANCED_URL = "#{ BASE_URL }/torrents/search/advanced/"
13
12
 
14
- class << self
15
- #
16
- # Convenience methods for the Search class
17
- #
18
- def search(search_term = nil, opts = {})
19
- Search.new search_term, opts
20
- end
13
+ #
14
+ # Convenience methods for the Search class
15
+ #
16
+ def self.search(search_term = nil, opts = {})
17
+ Search.new search_term, opts
18
+ end
21
19
 
22
- def quick_search(search_term = nil)
23
- Search.quick_search search_term
24
- end
20
+ def self.quick_search(search_term = nil)
21
+ Search.quick_search search_term
25
22
  end
26
23
 
27
24
  class Search
@@ -35,86 +32,35 @@ module Kat
35
32
 
36
33
  @@doc = nil
37
34
 
38
- class << self
39
-
40
- #
41
- # Kat.quick_search will do a quick search and return the results
42
- #
43
- def quick_search(search_term = nil)
44
- new(search_term).search
45
- end
46
-
47
- def field_map(type = nil)
48
- return FIELD_MAP.dup unless type
49
-
50
- FIELD_MAP.inject({}) { |hash, (k, v)|
51
- hash.tap { |h|
52
- case type
53
- when :select then h[k] = { select: v[:select], id: v[:id] || k }
54
- when :sort then h[k] = v[:sort] and h[v[:sort]] = v[:sort]
55
- h[v[:id]] = v[:sort] if v[:id]
56
- else h[k] = v[type]
57
- end if v[type]
58
- }
59
- }
60
- end
61
-
62
- def checks; field_map :check end
63
- def inputs; field_map :input end
64
- def selects; field_map :select end
65
- def sorts; field_map :sort end
66
-
67
- private
35
+ #
36
+ # Kat.quick_search will do a quick search and return the results
37
+ #
38
+ def self.quick_search(search_term = nil)
39
+ new(search_term).search
40
+ end
68
41
 
69
- #
70
- # Get a list of options for a particular selection field from the advanced search form
71
- #
72
- def field_options(label)
73
- fail 'Unknown search field' unless selects.find { |k, v| k == label.intern }
74
-
75
- url = URI(ADVANCED_URL)
76
-
77
- @@doc ||= Nokogiri::HTML(Net::HTTP.start(url.host) { |http| http.get url }.body)
78
-
79
- opts = @@doc.css('table.formtable td').find { |e|
80
- e.text[/#{ label }/i]
81
- }.next_element.first_element_child.children
82
-
83
- unless (group = opts.css('optgroup')).empty?
84
- # Categories
85
- group.inject({}) { |cat, og|
86
- cat.tap { |c|
87
- c[og.attributes['label'].value] = og.children.map { |o|
88
- o.attributes['value'].value
89
- }
90
- }
91
- }
92
- else
93
- # Times, languages, platforms
94
- opts.reject { |o| o.attributes.empty? }.inject({}) { |p, o|
95
- p.tap { |p| p[o.text] = o.attributes['value'].value }
96
- }
42
+ def self.field_map(type = nil)
43
+ return FIELD_MAP.dup unless type
44
+
45
+ FIELD_MAP.reduce({}) do |hash, (k, v)|
46
+ hash.tap do |h|
47
+ case type
48
+ when :select
49
+ h[k] = { select: v[:select], id: v[:id] || k }
50
+ when :sort
51
+ h[k] = v[:sort] && h[v[:sort]] = v[:sort]
52
+ h[v[:id]] = v[:sort] if v[:id]
53
+ else
54
+ h[k] = v[type]
55
+ end if v[type]
97
56
  end
98
- rescue => e
99
- { error: e }
100
- end
101
-
102
- #
103
- # If method is a field name in SELECT_FIELDS, fetch the list of values.
104
- #
105
- def method_missing(method, *args, &block)
106
- return super unless respond_to? method
107
- field_options selects.find { |k, v| v[:select] == method }.first
108
- end
109
-
110
- #
111
- # If method is a field name in SELECT_FIELDS, we can fetch the list of values
112
- #
113
- def respond_to_missing?(method, include_private = false)
114
- !!selects.find { |k, v| v[:select] == method } || super
115
57
  end
58
+ end
116
59
 
117
- end # class methods
60
+ def self.checks; field_map :check end
61
+ def self.inputs; field_map :input end
62
+ def self.selects; field_map :select end
63
+ def self.sorts; field_map :sort end
118
64
 
119
65
  #
120
66
  # Create a new +Kat::Search+ object to search Kickass Torrents.
@@ -139,9 +85,9 @@ module Kat
139
85
  str = [RECENT_PATH] if str[1].empty?
140
86
  str << page + 1 if page > 0
141
87
 
142
- sorts.find { |k, v| @options[:sort] && k == @options[:sort].intern }.tap { |k, v|
88
+ sorts.detect { |k, v| @options[:sort] && k == @options[:sort].intern }.tap do |k, v|
143
89
  str << (k ? "?field=#{ v }&sorder=#{ options[:asc] ? 'asc' : 'desc' }" : '')
144
- }
90
+ end
145
91
 
146
92
  str.join '/'
147
93
  end
@@ -152,13 +98,14 @@ module Kat
152
98
  # Raises ArgumentError if search_term is not a String, Symbol or Array
153
99
  #
154
100
  def query=(search_term)
155
- @search_term = case search_term
101
+ @search_term =
102
+ case search_term
156
103
  when nil, '' then []
157
104
  when String, Symbol then [search_term]
158
105
  when Array then search_term.flatten.select { |e| [String, Symbol].include? e.class }
159
- else fail ArgumentError, "search_term must be a String, Symbol or Array. " <<
106
+ else fail ArgumentError, 'search_term must be a String, Symbol or Array. ' \
160
107
  "#{ search_term.inspect } given."
161
- end
108
+ end
162
109
 
163
110
  build_query
164
111
  end
@@ -177,8 +124,8 @@ module Kat
177
124
  # Raises ArgumentError if options is not a Hash
178
125
  #
179
126
  def options=(options)
180
- fail ArgumentError, "options must be a Hash. " <<
181
- "#{ options.inspect } given." unless Hash === options
127
+ fail ArgumentError, 'options must be a Hash. ' \
128
+ "#{ options.inspect } given." unless options.is_a?(Hash)
182
129
 
183
130
  @options.merge! options
184
131
 
@@ -194,13 +141,13 @@ module Kat
194
141
  # a result set limited to the 25 results Kickass Torrents returns itself. Will
195
142
  # cache results for subsequent calls of search with the same query string.
196
143
  #
197
- def search(page = 0)
144
+ def search(page_num = 0)
198
145
  @error = nil
199
146
  @message = nil
200
147
 
201
- search_proc = -> page {
148
+ search_proc = lambda do |page|
202
149
  begin
203
- uri = URI(URI::encode(to_s page))
150
+ uri = URI(URI.encode(to_s page))
204
151
  res = Net::HTTP.get_response(uri)
205
152
  if res.code == '301'
206
153
  path = Net::HTTP::Get.new(res.header['location'])
@@ -212,7 +159,7 @@ module Kat
212
159
 
213
160
  doc = Nokogiri::HTML(res.body)
214
161
 
215
- @results[page] = doc.xpath('//table[@class="data"]/tr[position()>1]/td[1]').map { |node|
162
+ @results[page] = doc.xpath('//table[@class="data"]/tr[position()>1]/td[1]').map do |node|
216
163
  { path: node.css('a.torType').first.andand.attr('href'),
217
164
  title: node.css('a.cellMainLink').text,
218
165
  magnet: node.css('a.imagnet').first.andand.attr('href'),
@@ -221,8 +168,8 @@ module Kat
221
168
  files: (node = node.next_element).text.to_i,
222
169
  age: (node = node.next_element).text,
223
170
  seeds: (node = node.next_element).text.to_i,
224
- leeches: (node = node.next_element).text.to_i }
225
- }
171
+ leeches: node.next_element.text.to_i }
172
+ end
226
173
 
227
174
  # If we haven't previously performed a search with this query string, get the
228
175
  # number of pages from the pagination bar at the bottom of the results page.
@@ -234,16 +181,16 @@ module Kat
234
181
  rescue => e
235
182
  @error = { error: e }
236
183
  end unless @results[page] || (@pages > -1 && page >= @pages)
237
- }
184
+ end
238
185
 
239
186
  # Make sure we do a query for the first page of results before getting
240
187
  # subsequent pages in order to correctly figure out the total number of
241
188
  # pages of results.
242
- pages = (Range === page ? page.to_a : [page])
189
+ pages = (page_num.is_a?(Range) ? page_num.to_a : [page_num])
243
190
  pages.unshift(0) if @pages == -1 && !pages.include?(0)
244
191
  pages.each { |i| search_proc.call i }
245
192
 
246
- results[Range === page ? page.max : page]
193
+ results[page_num.is_a?(Range) ? page_num.max : page_num]
247
194
  end
248
195
 
249
196
  #
@@ -276,7 +223,61 @@ module Kat
276
223
  "#{ BASE_URL }/#{ query_str page }"
277
224
  end
278
225
 
279
- private
226
+ private
227
+
228
+ #
229
+ # Get a list of options for a particular selection field from the advanced search form
230
+ #
231
+ def self.field_options(label)
232
+ fail 'Unknown search field' unless selects.detect do |k, v|
233
+ k == label.intern
234
+ end
235
+
236
+ url = URI(ADVANCED_URL)
237
+
238
+ req = Net::HTTP.start(url.host) { |http| http.get url }
239
+ @@doc ||= Nokogiri::HTML(req.body)
240
+
241
+ opts = @@doc.css('table.formtable td').detect do |e|
242
+ e.text[/#{ label }/i]
243
+ end
244
+
245
+ opts = opts.next_element.first_element_child.children
246
+
247
+ if (group = opts.css('optgroup')).empty?
248
+ # Times, languages, platforms
249
+ opts.reject { |o| o.attributes.empty? }.reduce({}) do |p, o|
250
+ p.tap { |p1| p1[o.text] = o.attributes['value'].value }
251
+ end
252
+ else
253
+ # Categories
254
+ group.reduce({}) do |cat, og|
255
+ cat.tap do |c|
256
+ c[og.attributes['label'].value] = og.children.map do |o|
257
+ o.attributes['value'].value
258
+ end
259
+ end
260
+ end
261
+ end
262
+ rescue => e
263
+ { error: e }
264
+ end
265
+
266
+ #
267
+ # If method is a field name in SELECT_FIELDS, fetch the list of values.
268
+ #
269
+ def self.method_missing(method, *args, &block)
270
+ return super unless respond_to? method
271
+ field_options selects.detect { |k, v| v[:select] == method }.first
272
+ end
273
+
274
+ #
275
+ # If method is a field name in SELECT_FIELDS,
276
+ # we can fetch the list of values
277
+ #
278
+ def self.respond_to_missing?(method, include_private = false)
279
+ !!selects.detect { |k, v| v[:select] == method } || super
280
+ end
280
281
 
281
282
  #
282
283
  # Clear out the query and rebuild it from the various stored options. Also clears out the
@@ -293,17 +294,22 @@ module Kat
293
294
 
294
295
  @query += inputs.select { |k, v| @options[k] }.map { |k, v| "#{ k }:#{ @options[k] }" }
295
296
  @query += checks.select { |k, v| @options[k] }.map { |k, v| "#{ k }:1" }
296
- @query += selects.select { |k, v|
297
+
298
+ byzantine = selects.select do |k, v|
297
299
  (v[:id].to_s[/^.*_id$/] && @options[k].to_s.to_i > 0) ||
298
300
  (v[:id].to_s[/^[^_]+$/] && @options[k])
299
- }.map { |k, v| "#{ v[:id] }:#{ @options[k] }" }
301
+ end
302
+
303
+ @query += byzantine.map { |k, v| "#{ v[:id] }:#{ @options[k] }" }
300
304
  end
301
305
 
302
306
  #
303
307
  # Fetch a list of values from the results set given by name
304
308
  #
305
309
  def results_column(name)
306
- @results.compact.map { |rs| rs.map { |r| r[name] || r[name[0...-1].intern] } }.flatten
310
+ @results.compact.map do |rs|
311
+ rs.map { |r| r[name] || r[name[0...-1].intern] }
312
+ end.flatten
307
313
  end
308
314
 
309
315
  #
@@ -318,7 +324,5 @@ module Kat
318
324
  !(@results.empty? || @results.first.empty?) &&
319
325
  (@results.first.first[method] || @results.first.first[method[0..-2].intern]) || super
320
326
  end
321
-
322
327
  end
323
-
324
328
  end
@@ -1,6 +1,6 @@
1
1
  module Kat
2
2
  NAME = 'Kickass Torrents Search'
3
- VERSION = '2.0.5'
3
+ VERSION = '2.0.6'
4
4
  MALEVOLENT_DICTATOR_FOR_LIFE = 'Fission Xuiptz'
5
5
  AUTHOR = MALEVOLENT_DICTATOR_FOR_LIFE
6
6
  VERSION_STR = "#{NAME} #{VERSION} (c) 2013 #{MALEVOLENT_DICTATOR_FOR_LIFE}"
@@ -23,7 +23,7 @@ describe Kat::App do
23
23
 
24
24
  it 'creates a validation regex' do
25
25
  app.page.must_equal 0
26
- app.instance_exec {
26
+ app.instance_exec do
27
27
  @window_width = 80
28
28
 
29
29
  prev?.wont_equal true
@@ -42,8 +42,9 @@ describe Kat::App do
42
42
 
43
43
  prev?.must_equal true
44
44
  next?.wont_equal true
45
- # Skip the test if there's no results. We really only want to test in ideal
46
- # network conditions and no results here are an indication that's not the case
45
+ # Skip the test if there's no results. We really only want to test in
46
+ # ideal network conditions and no results here are an indication that's
47
+ # not the case
47
48
  validation_regex.must_equal(
48
49
  /^([pq]|[1-#{ [9, n].min }]#{
49
50
  "|1[0-#{ [9, n - 10].min }]" if n > 9
@@ -51,19 +52,19 @@ describe Kat::App do
51
52
  ) if n > 0
52
53
 
53
54
  @page = 0
54
- }
55
+ end
55
56
  end
56
57
 
57
58
  it 'deals with terminal width' do
58
- app.instance_exec {
59
+ app.instance_exec do
59
60
  set_window_width
60
- hide_info?.must_equal (@window_width < 81)
61
- }
61
+ hide_info?.must_equal(@window_width < 81)
62
+ end
62
63
  end
63
64
 
64
65
  it 'formats a list of options' do
65
- app.instance_exec {
66
- %i(category added platform language).each { |s|
66
+ app.instance_exec do
67
+ %i(category added platform language).each do |s|
67
68
  list = format_lists(s => Kat::Search.selects[s])
68
69
 
69
70
  list.must_be_instance_of Array
@@ -71,22 +72,28 @@ describe Kat::App do
71
72
 
72
73
  [0, 2, list.size - 1].each { |i| list[i].must_be_nil }
73
74
 
74
- list[1].must_equal case s
75
- when :added then 'Times'
76
- when :category then 'Categories'
77
- else s.to_s.capitalize << 's'
78
- end
79
-
80
- 3.upto(list.size - 2) { |i| list[i].must_be_instance_of String } unless s == :category
81
- 3.upto(list.size - 2) { |i| list[i].must_match(/^\s*([A-Z]+ => )?[a-z0-9-]+/) if list[i] } if s == :category
82
- }
83
- }
75
+ str = case s
76
+ when :added then 'Times'
77
+ when :category then 'Categories'
78
+ else s.to_s.capitalize << 's'
79
+ end
80
+ list[1].must_equal str
81
+
82
+ 3.upto(list.size - 2) do |i|
83
+ list[i].must_be_instance_of String
84
+ end unless s == :category
85
+
86
+ 3.upto(list.size - 2) do |i|
87
+ list[i].must_match(/^\s*([A-Z]+ => )?[a-z0-9-]+/) if list[i]
88
+ end if s == :category
89
+ end
90
+ end
84
91
  end
85
92
 
86
93
  it 'formats a list of torrents' do
87
94
  Kat::Colour.colour = false
88
95
 
89
- app.instance_exec {
96
+ app.instance_exec do
90
97
  set_window_width
91
98
  list = format_results
92
99
 
@@ -95,33 +102,33 @@ describe Kat::App do
95
102
 
96
103
  list.last.must_be_nil
97
104
 
98
- (2..list.size - 2).each { |i|
99
- list[i].must_match /^(\s[1-9]|[12][0-9])\. .*/
100
- }
101
- }
105
+ (2..list.size - 2).each do |i|
106
+ list[i].must_match(/^(\s[1-9]|[12][0-9])\. .*/)
107
+ end
108
+ end
102
109
  end
103
110
 
104
111
  it 'downloads data from a URL' do
105
112
  Kat::Colour.colour = false
106
113
 
107
- app.instance_exec {
114
+ app.instance_exec do
108
115
  s = 'foobar'
109
- result = download({ download: 'http://google.com', title: s })
116
+ result = download(download: 'http://google.com', title: s)
110
117
  result.must_equal :done
111
- File.exists?(File.expand_path "./#{ s }.torrent").must_equal true
118
+ File.exist?(File.expand_path "./#{ s }.torrent").must_equal true
112
119
  File.delete(File.expand_path "./#{ s }.torrent")
113
- }
120
+ end
114
121
  end
115
122
 
116
123
  it 'returns an error message when a download fails' do
117
124
  Kat::Colour.colour = false
118
125
 
119
- app.instance_exec {
120
- result = download({ download: 'http://foo.bar', title: 'foobar' })
126
+ app.instance_exec do
127
+ result = download(download: 'http://foo.bar', title: 'foobar')
121
128
  result.must_be_instance_of Array
122
129
  result.first.must_equal :failed
123
- result.last.must_match /^getaddrinfo/
124
- }
130
+ result.last.must_match(/^getaddrinfo/)
131
+ end
125
132
  end
126
133
  end
127
134
  end
@@ -14,18 +14,18 @@ describe Kat::Colour do
14
14
  end
15
15
 
16
16
  it 'has colour methods' do
17
- colours.each { |c|
17
+ colours.each do |c|
18
18
  ''.respond_to?(c).must_equal true
19
19
  :s.respond_to?(c).must_equal true
20
20
  [].respond_to?(c).must_equal true
21
21
  {}.respond_to?(c).wont_equal true
22
- }
22
+ end
23
23
  end
24
24
 
25
25
  it 'colours strings' do
26
26
  Kat::Colour.colour = true
27
27
 
28
- colours.each_with_index { |c, i|
28
+ colours.each_with_index do |c, i|
29
29
  str = 'foobar'
30
30
  result = "\e[0;#{ 30 + i }mfoobar\e[0m"
31
31
  intense_result = "\e[1;#{ 30 + i }mfoobar\e[0m"
@@ -42,7 +42,7 @@ describe Kat::Colour do
42
42
  str = 'foobar'
43
43
  str.send("#{ c }!", true).must_equal intense_result
44
44
  str.must_equal intense_result
45
- }
45
+ end
46
46
  end
47
47
 
48
48
  it 'uncolour strings' do
@@ -59,7 +59,7 @@ describe Kat::Colour do
59
59
  it 'colours symbols' do
60
60
  Kat::Colour.colour = true
61
61
 
62
- colours.each_with_index { |c, i|
62
+ colours.each_with_index do |c, i|
63
63
  sym = :foobar
64
64
  result = "\e[0;#{ 30 + i }mfoobar\e[0m"
65
65
  intense_result = "\e[1;#{ 30 + i }mfoobar\e[0m"
@@ -75,7 +75,7 @@ describe Kat::Colour do
75
75
 
76
76
  sym.send("#{ c }!", true).must_equal :foobar
77
77
  sym.must_equal :foobar
78
- }
78
+ end
79
79
  end
80
80
 
81
81
  it 'does not uncolour symbols' do
@@ -91,7 +91,7 @@ describe Kat::Colour do
91
91
  it 'colours arrays of strings and symbols' do
92
92
  Kat::Colour.colour = true
93
93
 
94
- colours.each_with_index { |c, i|
94
+ colours.each_with_index do |c, i|
95
95
  s = ['foobar', :foobar, nil, ['foobar', :foobar, nil]]
96
96
  t = ['foobar', :foobar, nil, ['foobar', :foobar, nil]]
97
97
  result = [
@@ -110,7 +110,7 @@ describe Kat::Colour do
110
110
  result[3][1] = :foobar
111
111
  s.send("#{ c }!").must_equal result
112
112
  s.must_equal result
113
- }
113
+ end
114
114
  end
115
115
 
116
116
  it 'uncolours arrays of strings' do
@@ -13,22 +13,22 @@ describe Kat::Options do
13
13
  end
14
14
 
15
15
  it 'has symbolised keys' do
16
- opts.keys.each { |key|
16
+ opts.keys.each do |key|
17
17
  key.must_be_instance_of Symbol
18
18
  key.wont_equal :size
19
- }
19
+ end
20
20
  end
21
21
 
22
22
  it 'has a hash for values, each with a description' do
23
- opts.values.each { |value|
23
+ opts.values.each do |value|
24
24
  value.must_be_instance_of Hash
25
25
  value.keys.must_include :desc
26
- }
26
+ end
27
27
  end
28
28
 
29
29
  it 'has symbolised keys in each value' do
30
- opts.each { |key, value|
31
- value.each { |k, v|
30
+ opts.each do |key, value|
31
+ value.each do |k, v|
32
32
  k.must_be_instance_of Symbol
33
33
  case k
34
34
  when :desc
@@ -37,12 +37,12 @@ describe Kat::Options do
37
37
  v.must_equal true
38
38
  when :short
39
39
  v.must_be_instance_of Symbol
40
- v.must_match /\A([a-z]|none)\Z/
40
+ v.must_match(/\A([a-z]|none)\Z/)
41
41
  else
42
42
  v.must_be_instance_of Symbol
43
43
  end
44
- }
45
- }
44
+ end
45
+ end
46
46
  end
47
47
  end
48
48
 
@@ -55,9 +55,9 @@ describe Kat::Options do
55
55
 
56
56
  opts.must_be_instance_of Hash
57
57
  opts.wont_be_empty
58
- opts.values.each { |value|
58
+ opts.values.each do |value|
59
59
  [[], nil, false].must_include value
60
- }
60
+ end
61
61
  end
62
62
 
63
63
  it 'returns an options hash with some options switched on' do
@@ -68,9 +68,9 @@ describe Kat::Options do
68
68
 
69
69
  opts[:or].must_equal %w(baz)
70
70
  opts[:sort].must_equal 'age'
71
- %i(colour colour_given or_given sort_given).each { |key|
71
+ %i(colour colour_given or_given sort_given).each do |key|
72
72
  opts[key].must_equal true
73
- }
73
+ end
74
74
  end
75
75
  end
76
76
 
@@ -7,7 +7,7 @@ blue_peter.go.go 1
7
7
  describe Kat::Search do
8
8
 
9
9
  let(:kat) { Kat.search 'test' }
10
- let(:kat_opts) { Kat.search 'test', { category: 'books' } }
10
+ let(:kat_opts) { Kat.search 'test', category: 'books' }
11
11
 
12
12
  describe 'basic search' do
13
13
  it 'returns a full result set' do
@@ -33,15 +33,15 @@ describe Kat::Search do
33
33
  end
34
34
 
35
35
  it 'wont respond to result fields before a search' do
36
- %i(titles files).each { |s|
36
+ %i(titles files).each do |s|
37
37
  kat.respond_to?(s).must_equal false
38
- }
38
+ end
39
39
  end
40
40
 
41
41
  it 'responds to result fields after a search' do
42
- %i(titles files).each { |s|
42
+ %i(titles files).each do |s|
43
43
  blue_peter.respond_to?(s).must_equal true
44
- }
44
+ end
45
45
  end
46
46
 
47
47
  it 'returns identical result sets' do
@@ -56,7 +56,7 @@ describe Kat::Search do
56
56
  it 'returns a valid query string with options' do
57
57
  bp = blue_peter.dup
58
58
  bp.options = { user: :foobar }
59
- bp.options.must_equal({ user: :foobar })
59
+ bp.options.must_equal(user: :foobar)
60
60
  bp.query_str(1).must_equal 'usearch/user:foobar/2/'
61
61
  bp.results.must_be_empty
62
62
  bp.pages.must_equal(-1)
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kat
3
3
  version: !ruby/object:Gem::Version
4
- version: 2.0.5
4
+ version: 2.0.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Fission Xuiptz
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2014-03-31 00:00:00.000000000 Z
11
+ date: 2014-07-05 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: nokogiri
@@ -112,7 +112,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
112
112
  version: '0'
113
113
  requirements: []
114
114
  rubyforge_project:
115
- rubygems_version: 2.2.0
115
+ rubygems_version: 2.2.2
116
116
  signing_key:
117
117
  specification_version: 4
118
118
  summary: Kickass Torrents Interface