watchmob 0.1.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.
- data/bin/watchmob +30 -0
- data/lib/watchmob.rb +10 -0
- data/lib/watchmob/aukro_page.rb +57 -0
- data/lib/watchmob/error.rb +6 -0
- data/lib/watchmob/gsmarena_page.rb +86 -0
- data/lib/watchmob/gsmarena_search_page.rb +33 -0
- data/lib/watchmob/heureka_page.rb +69 -0
- data/lib/watchmob/heureka_search_page.rb +31 -0
- data/lib/watchmob/log.rb +36 -0
- data/lib/watchmob/page.rb +17 -0
- data/lib/watchmob/smartphone.rb +112 -0
- data/lib/watchmob/view_helpers.rb +15 -0
- data/lib/watchmob/watchlist.haml +63 -0
- data/lib/watchmob/watchlist.rb +47 -0
- metadata +118 -0
data/bin/watchmob
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
$LOAD_PATH.unshift(File.expand_path("../../lib", __FILE__))
|
3
|
+
require 'json'
|
4
|
+
require 'watchmob'
|
5
|
+
|
6
|
+
if ARGV.size >= 1
|
7
|
+
json = JSON.parse(File.read(ARGV[0]))
|
8
|
+
else
|
9
|
+
STDERR.puts "Use: #$0 input.json <output.html>"
|
10
|
+
exit 1
|
11
|
+
end
|
12
|
+
|
13
|
+
begin
|
14
|
+
Watchmob::Log.output = STDERR
|
15
|
+
watchlist = Watchmob::Watchlist.new(json)
|
16
|
+
watchlist.sort_by! :battery_capacity
|
17
|
+
|
18
|
+
if ARGV.size >= 2
|
19
|
+
output = File.open(ARGV[1], "w")
|
20
|
+
else
|
21
|
+
output = STDOUT
|
22
|
+
end
|
23
|
+
|
24
|
+
output.write(watchlist.to_html)
|
25
|
+
rescue Watchmob::WatchmobError => e
|
26
|
+
Watchmob::Log.error(e)
|
27
|
+
exit 2
|
28
|
+
ensure
|
29
|
+
output.close if output
|
30
|
+
end
|
data/lib/watchmob.rb
ADDED
@@ -0,0 +1,10 @@
|
|
1
|
+
require 'watchmob/log'
|
2
|
+
require 'watchmob/error'
|
3
|
+
require 'watchmob/watchlist'
|
4
|
+
require 'watchmob/page'
|
5
|
+
require 'watchmob/smartphone'
|
6
|
+
require 'watchmob/aukro_page'
|
7
|
+
require 'watchmob/heureka_page'
|
8
|
+
require 'watchmob/heureka_search_page'
|
9
|
+
require 'watchmob/gsmarena_page'
|
10
|
+
require 'watchmob/gsmarena_search_page'
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
module Watchmob
|
3
|
+
class AukroPage < Page
|
4
|
+
def finished?
|
5
|
+
not document.at_css("#itemFinishBox2").nil?
|
6
|
+
end
|
7
|
+
|
8
|
+
def price
|
9
|
+
if finished?
|
10
|
+
e = document.at_css("#itemFinishBox2 .left:first-child strong")
|
11
|
+
else
|
12
|
+
e = document.at_css(".price")
|
13
|
+
end
|
14
|
+
e.inner_text.gsub(/\s+/, "").gsub(",", ".").to_f
|
15
|
+
end
|
16
|
+
|
17
|
+
def deadline
|
18
|
+
document.at_css(".timeInfo").inner_text.match(
|
19
|
+
/\(([[:alpha:]]+),\s+(\d+)\s+([[:alpha:]]+),\s+(\d+):(\d+):(\d+)/
|
20
|
+
) do |md|
|
21
|
+
text, weekday_name, month_day, month_name, hours, minutes, seconds = *md.to_a
|
22
|
+
DateTime.new(
|
23
|
+
Time.now.year,
|
24
|
+
month_name_to_number(month_name),
|
25
|
+
month_day.to_i,
|
26
|
+
hours.to_i,
|
27
|
+
minutes.to_i,
|
28
|
+
seconds.to_i,
|
29
|
+
'+1'
|
30
|
+
)
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def month_name_to_number(month_name)
|
35
|
+
case month_name
|
36
|
+
when "ledna" then 1
|
37
|
+
when "února" then 2
|
38
|
+
when "března" then 3
|
39
|
+
when "dubna" then 4
|
40
|
+
when "května" then 5
|
41
|
+
when "června" then 6
|
42
|
+
when "července" then 7
|
43
|
+
when "srpna" then 8
|
44
|
+
when "září" then 9
|
45
|
+
when "října" then 10
|
46
|
+
when "listopadu" then 11
|
47
|
+
when "prosince" then 12
|
48
|
+
else
|
49
|
+
require 'pry'
|
50
|
+
pry binding
|
51
|
+
raise AukroDateError, "Unknown month name #{month_name.inspect}"
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
@@ -0,0 +1,86 @@
|
|
1
|
+
require 'andand'
|
2
|
+
require 'watchmob/page'
|
3
|
+
|
4
|
+
module Watchmob
|
5
|
+
class GSMArenaPage < Page
|
6
|
+
def self.find(phrase)
|
7
|
+
search = GSMArenaSearchPage.search(phrase)
|
8
|
+
uri = search.first_result_uri
|
9
|
+
new(uri)
|
10
|
+
end
|
11
|
+
|
12
|
+
def title
|
13
|
+
"GSMArena"
|
14
|
+
end
|
15
|
+
|
16
|
+
def name
|
17
|
+
document.at_css("h1").andand.inner_text
|
18
|
+
end
|
19
|
+
|
20
|
+
def mass
|
21
|
+
property "Weight" do |text|
|
22
|
+
text.match /(\d+)\s*g/ do |md|
|
23
|
+
return md[1].to_f
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def battery_capacity
|
29
|
+
property "Battery" do |text|
|
30
|
+
text.match /(\d+)\s*mAh/i do |md|
|
31
|
+
return md[1].to_f
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def stand_by
|
37
|
+
property "Stand-by" do |text|
|
38
|
+
average_time text
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def talk_time
|
43
|
+
property "Talk time" do |text|
|
44
|
+
average_time text
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def average_time(text)
|
49
|
+
minutes = text.scan(/(\d+)\s*h\s*(\d+\s*min)?/).map do |hours, mins=0|
|
50
|
+
hours.to_f * 60 + mins.to_f
|
51
|
+
end
|
52
|
+
|
53
|
+
unless minutes.empty?
|
54
|
+
sum = minutes.inject :+
|
55
|
+
return sum.to_f / minutes.size
|
56
|
+
end
|
57
|
+
|
58
|
+
nil
|
59
|
+
end
|
60
|
+
|
61
|
+
def autofocus
|
62
|
+
property "Primary" do |text|
|
63
|
+
!!(text =~ /autofocus/)
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def property(name)
|
68
|
+
found = nil
|
69
|
+
document.css("#specs-list tr").each do |tr|
|
70
|
+
cells = [tr.at_css("th"), tr.at_css(".ttl")]
|
71
|
+
if cells.any? { |c| c.andand.inner_text.to_s.strip == name }
|
72
|
+
value = tr.at_css(".nfo").inner_text
|
73
|
+
|
74
|
+
unless value.nil?
|
75
|
+
raise PropertyError, "Property named #{name} has been matched " +
|
76
|
+
"more than once on GSM arena page #{uri}" if found
|
77
|
+
found = yield value
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
found
|
83
|
+
end
|
84
|
+
|
85
|
+
end
|
86
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'watchmob/page'
|
2
|
+
|
3
|
+
module Watchmob
|
4
|
+
class GSMArenaSearchPage < Page
|
5
|
+
def self.search(phrase)
|
6
|
+
encoded_phrase = URI.encode_www_form_component(phrase)
|
7
|
+
new("http://www.gsmarena.com/results.php3?sQuickSearch=no&sName=#{encoded_phrase}", phrase)
|
8
|
+
end
|
9
|
+
|
10
|
+
def initialize(uri, phrase = nil)
|
11
|
+
super(uri)
|
12
|
+
@phrase = phrase
|
13
|
+
end
|
14
|
+
|
15
|
+
attr_reader :phrase
|
16
|
+
|
17
|
+
def first_result_uri
|
18
|
+
if document.at_css("#specs-list")
|
19
|
+
uri
|
20
|
+
else
|
21
|
+
path = document.css("#mid-col .makers li a").
|
22
|
+
map { |a| a.attr("href") }.
|
23
|
+
first
|
24
|
+
|
25
|
+
if path
|
26
|
+
URI.parse("http://www.gsmarena.com/#{path}")
|
27
|
+
else
|
28
|
+
raise NotFoundError, "There were no phones for #{phrase.inspect} on GSM arena"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
require 'andand'
|
2
|
+
require 'watchmob/page'
|
3
|
+
|
4
|
+
module Watchmob
|
5
|
+
class HeurekaPage < Page
|
6
|
+
def self.find(name)
|
7
|
+
search = HeurekaSearchPage.search(name)
|
8
|
+
uri = search.first_result_uri
|
9
|
+
uri.path += "/specifikace/"
|
10
|
+
new(uri)
|
11
|
+
end
|
12
|
+
|
13
|
+
def title
|
14
|
+
"Heureka"
|
15
|
+
end
|
16
|
+
|
17
|
+
def name
|
18
|
+
document.at_css(".main-info h2").andand.inner_text
|
19
|
+
end
|
20
|
+
|
21
|
+
def mass
|
22
|
+
param_value(1607).andand.to_f
|
23
|
+
end
|
24
|
+
|
25
|
+
def battery_capacity
|
26
|
+
capacity = param_value(1615).andand.to_f
|
27
|
+
if !capacity.nil? and capacity <= 10
|
28
|
+
# this is a common mistake in Heureka specs (1.3 mAh instead of 1300 mAh)
|
29
|
+
capacity * 1000
|
30
|
+
else
|
31
|
+
capacity
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def stand_by
|
36
|
+
param_value_to_min(1616).andand.to_f
|
37
|
+
end
|
38
|
+
|
39
|
+
def talk_time
|
40
|
+
param_value_to_min(1617).andand.to_f
|
41
|
+
end
|
42
|
+
|
43
|
+
def autofocus
|
44
|
+
param_value(3688).andand.eql? "ano"
|
45
|
+
end
|
46
|
+
|
47
|
+
def param_value_to_min(param)
|
48
|
+
value = param_value(param).andand.to_f
|
49
|
+
case unit = param_unit(param)
|
50
|
+
when /hodin/
|
51
|
+
value * 60
|
52
|
+
when /min/
|
53
|
+
value
|
54
|
+
when nil
|
55
|
+
nil
|
56
|
+
else
|
57
|
+
raise RuntimeError, "Unknown unit #{unit}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def param_value(param)
|
62
|
+
document.at_css("#param-value-#{param}").andand.inner_text
|
63
|
+
end
|
64
|
+
|
65
|
+
def param_unit(param)
|
66
|
+
document.at_css("#param-unit-#{param}").andand.inner_text
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'watchmob/page'
|
3
|
+
|
4
|
+
module Watchmob
|
5
|
+
class HeurekaSearchPage < Page
|
6
|
+
def self.search(phrase)
|
7
|
+
encoded_phrase = URI.encode_www_form_component(phrase)
|
8
|
+
new("http://mobilni-telefony.heureka.cz/f:q:#{encoded_phrase}", phrase)
|
9
|
+
end
|
10
|
+
|
11
|
+
def initialize(uri, phrase = nil)
|
12
|
+
super(uri)
|
13
|
+
@phrase = phrase
|
14
|
+
end
|
15
|
+
|
16
|
+
attr_reader :phrase
|
17
|
+
|
18
|
+
def first_result_uri
|
19
|
+
if document.at_css("#product-container")
|
20
|
+
found_uri = document.css("#product-container .p").
|
21
|
+
map { |search_result| URI.parse(search_result.at_css("h2 a").attr("href")) }.
|
22
|
+
first
|
23
|
+
|
24
|
+
return found_uri unless found_uri.nil?
|
25
|
+
end
|
26
|
+
|
27
|
+
raise NotFoundError, "There were no smartphones for #{phrase.inspect} on Heureka"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
data/lib/watchmob/log.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
module Watchmob
|
2
|
+
module Log
|
3
|
+
extend self
|
4
|
+
|
5
|
+
def error(msg)
|
6
|
+
show msg
|
7
|
+
end
|
8
|
+
|
9
|
+
def info(msg)
|
10
|
+
show msg
|
11
|
+
end
|
12
|
+
|
13
|
+
def waiting(msg)
|
14
|
+
show "#{msg}..."
|
15
|
+
begin
|
16
|
+
yield
|
17
|
+
rescue Exception => e
|
18
|
+
show " #{msg} -> #{e.class.to_s}"
|
19
|
+
raise
|
20
|
+
else
|
21
|
+
show " #{msg} -> OK"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
attr_accessor :output
|
26
|
+
|
27
|
+
@output = STDERR
|
28
|
+
@print_mutex = Mutex.new
|
29
|
+
|
30
|
+
def show(msg)
|
31
|
+
@print_mutex.synchronize do
|
32
|
+
output.puts(msg)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'uri'
|
3
|
+
require 'open-uri'
|
4
|
+
|
5
|
+
module Watchmob
|
6
|
+
class Page
|
7
|
+
def initialize(uri)
|
8
|
+
@uri = uri
|
9
|
+
Log.waiting("get #@uri") do
|
10
|
+
@document = Nokogiri::HTML.parse(open(uri))
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
attr_reader :uri
|
15
|
+
attr_reader :document
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'uri'
|
2
|
+
require 'watchmob/gsmarena_page'
|
3
|
+
require 'watchmob/heureka_page'
|
4
|
+
|
5
|
+
module Watchmob
|
6
|
+
class Smartphone
|
7
|
+
|
8
|
+
SPEC_PAGES = [HeurekaPage, GSMArenaPage]
|
9
|
+
|
10
|
+
def self.find(params)
|
11
|
+
unless params["aukro"] and params["phone"]
|
12
|
+
raise ArgumentError, "I want parameters 'aukro' and 'phone', got #{params.inspect}"
|
13
|
+
end
|
14
|
+
|
15
|
+
aukro_page = AukroPage.new(URI.parse(params["aukro"]))
|
16
|
+
spec_pages = SPEC_PAGES.map { |spec_page|
|
17
|
+
begin
|
18
|
+
spec_page.find(params["phone"])
|
19
|
+
rescue NotFoundError
|
20
|
+
nil
|
21
|
+
end
|
22
|
+
}.select { |s| !s.nil? }
|
23
|
+
|
24
|
+
unless spec_pages.empty?
|
25
|
+
new(aukro_page, spec_pages)
|
26
|
+
else
|
27
|
+
raise NotFoundError, "Smartphone #{params["phone"]} <#{params["aukro"]}> was not found anywhere"
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
def initialize(aukro_page, spec_pages)
|
32
|
+
@aukro_page = aukro_page
|
33
|
+
@spec_pages = spec_pages
|
34
|
+
end
|
35
|
+
|
36
|
+
attr_reader :aukro_page
|
37
|
+
attr_reader :spec_pages
|
38
|
+
|
39
|
+
def name
|
40
|
+
@spec_pages.first.name
|
41
|
+
end
|
42
|
+
|
43
|
+
def aukro_finished?
|
44
|
+
@aukro_page.finished?
|
45
|
+
end
|
46
|
+
|
47
|
+
def aukro_price
|
48
|
+
@aukro_page.price
|
49
|
+
end
|
50
|
+
|
51
|
+
def aukro_deadline
|
52
|
+
@aukro_page.deadline
|
53
|
+
end
|
54
|
+
|
55
|
+
def aukro_uri
|
56
|
+
@aukro_page.uri
|
57
|
+
end
|
58
|
+
|
59
|
+
def mass
|
60
|
+
average :mass
|
61
|
+
end
|
62
|
+
|
63
|
+
def battery_capacity
|
64
|
+
average :battery_capacity
|
65
|
+
end
|
66
|
+
|
67
|
+
def stand_by
|
68
|
+
average :stand_by
|
69
|
+
end
|
70
|
+
|
71
|
+
def talk_time
|
72
|
+
average :talk_time
|
73
|
+
end
|
74
|
+
|
75
|
+
def autofocus
|
76
|
+
boolean :autofocus
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
|
81
|
+
def average(property)
|
82
|
+
values = property_values(property)
|
83
|
+
unless values.empty?
|
84
|
+
sum = values.inject(0) { |a,b| a + b }
|
85
|
+
sum / values.length.to_f
|
86
|
+
else
|
87
|
+
nil
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def boolean(property)
|
92
|
+
values = property_values(property)
|
93
|
+
unless values.empty?
|
94
|
+
if values.all? { |v| !!v }
|
95
|
+
true
|
96
|
+
elsif values.all? { |v| !v }
|
97
|
+
false
|
98
|
+
else
|
99
|
+
raise PropertyError, "Property #{property} of #{name} has values #{values.inspect}"
|
100
|
+
+ ", expected all true or all false"
|
101
|
+
end
|
102
|
+
else
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def property_values(property)
|
108
|
+
@spec_pages.map(&property).select { |v| !v.nil? }
|
109
|
+
end
|
110
|
+
|
111
|
+
end
|
112
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Watchmob
|
2
|
+
module ViewHelpers
|
3
|
+
def format_datetime(time)
|
4
|
+
time.strftime("%Y-%m-%d %H:%M:%S")
|
5
|
+
end
|
6
|
+
|
7
|
+
def format_time_interval(seconds)
|
8
|
+
if seconds > 2*24*60*60
|
9
|
+
"#{(seconds / (24*60*60)).round} days"
|
10
|
+
else
|
11
|
+
"#{(seconds / (60*60)).round} hours"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
!!! 5
|
2
|
+
%html
|
3
|
+
%head
|
4
|
+
%meta{:charset => "utf-8"}
|
5
|
+
%meta{:"http-equiv" => "Content-Type", :content => "text/html; charset=utf-8"}
|
6
|
+
%title Aukro smartphones comparsion
|
7
|
+
%style
|
8
|
+
:sass
|
9
|
+
table#phones
|
10
|
+
border-collapse: collapse
|
11
|
+
tbody tr:hover
|
12
|
+
background-color: #ddd
|
13
|
+
tbody tr.finished
|
14
|
+
font-style: italic
|
15
|
+
th
|
16
|
+
font-weight: bold
|
17
|
+
text-align: left
|
18
|
+
padding-left: 1em
|
19
|
+
td
|
20
|
+
text-align: right
|
21
|
+
padding-right: 1em
|
22
|
+
&.name
|
23
|
+
text-align: left
|
24
|
+
font-weight: bold
|
25
|
+
|
26
|
+
|
27
|
+
%body
|
28
|
+
%h1 Aukro smartphones comparsion
|
29
|
+
|
30
|
+
%table#phones
|
31
|
+
%thead
|
32
|
+
%tr
|
33
|
+
%th.name Name
|
34
|
+
%th.price Price
|
35
|
+
%th.mass Mass
|
36
|
+
%th.battery Battery
|
37
|
+
%th.talk-time Talk time
|
38
|
+
%th.stand-by Stand by
|
39
|
+
%th.autofocus Autofocus
|
40
|
+
%th.remaining-time Time
|
41
|
+
%th.deadline Deadline
|
42
|
+
%th.spec Spec
|
43
|
+
|
44
|
+
%tbody
|
45
|
+
- phones.each do |phone|
|
46
|
+
%tr{:class => phone.aukro_finished? ? "finished" : nil}
|
47
|
+
%td.name
|
48
|
+
%a{:href => phone.aukro_uri.to_s} #{phone.name}
|
49
|
+
%td #{phone.aukro_price} CZK
|
50
|
+
%td #{phone.mass} g
|
51
|
+
%td #{phone.battery_capacity} mAh
|
52
|
+
%td #{phone.talk_time} min
|
53
|
+
%td #{phone.stand_by / 60} h
|
54
|
+
%td #{phone.autofocus.nil? ? "" : (phone.autofocus ? "yes" : "no")}
|
55
|
+
%td #{format_time_interval(phone.aukro_deadline.to_time - Time.now)}
|
56
|
+
%td #{format_datetime(phone.aukro_deadline.to_time)}
|
57
|
+
%td
|
58
|
+
- phone.spec_pages.each do |spec|
|
59
|
+
%a{:href => spec.uri} #{spec.title}
|
60
|
+
|
61
|
+
#generated-at
|
62
|
+
Generated at #{format_datetime(Time.now)}
|
63
|
+
|
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'timeout'
|
2
|
+
require 'sass'
|
3
|
+
require 'haml'
|
4
|
+
|
5
|
+
require 'watchmob/view_helpers'
|
6
|
+
|
7
|
+
module Watchmob
|
8
|
+
class Watchlist
|
9
|
+
include ViewHelpers
|
10
|
+
|
11
|
+
HAML_TEMPLATE = File.expand_path("../watchlist.haml", __FILE__)
|
12
|
+
PHONE_TIMEOUT = 15.0
|
13
|
+
|
14
|
+
def initialize(json)
|
15
|
+
@phones = []
|
16
|
+
|
17
|
+
phone_threads = json.map do |phone_json|
|
18
|
+
params = if phone_json.is_a? Array
|
19
|
+
{"aukro" => phone_json[0], "phone" => phone_json[1]}
|
20
|
+
else
|
21
|
+
phone_json
|
22
|
+
end
|
23
|
+
|
24
|
+
Thread.new do
|
25
|
+
Thread.current.abort_on_exception = true
|
26
|
+
@phones << Smartphone.find(params)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
Timeout.timeout(PHONE_TIMEOUT) do
|
31
|
+
phone_threads.each(&:join)
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
attr_reader :phones
|
36
|
+
|
37
|
+
def sort_by!(property)
|
38
|
+
@phones.sort_by! { |phone| phone.aukro_page.uri.to_s }
|
39
|
+
@phones.sort_by! { |phone| phone.__send__(property) or 0 }
|
40
|
+
end
|
41
|
+
|
42
|
+
def to_html
|
43
|
+
engine = Haml::Engine.new(File.read(HAML_TEMPLATE), :filename => HAML_TEMPLATE)
|
44
|
+
engine.render(self)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,118 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: watchmob
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Jan Špaček
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2012-07-01 00:00:00.000000000 Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: andand
|
16
|
+
requirement: &68961290 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ~>
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '1.3'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *68961290
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: haml
|
27
|
+
requirement: &68960590 !ruby/object:Gem::Requirement
|
28
|
+
none: false
|
29
|
+
requirements:
|
30
|
+
- - ~>
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '3.1'
|
33
|
+
type: :runtime
|
34
|
+
prerelease: false
|
35
|
+
version_requirements: *68960590
|
36
|
+
- !ruby/object:Gem::Dependency
|
37
|
+
name: json
|
38
|
+
requirement: &68960200 !ruby/object:Gem::Requirement
|
39
|
+
none: false
|
40
|
+
requirements:
|
41
|
+
- - ~>
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: '1.6'
|
44
|
+
type: :runtime
|
45
|
+
prerelease: false
|
46
|
+
version_requirements: *68960200
|
47
|
+
- !ruby/object:Gem::Dependency
|
48
|
+
name: nokogiri
|
49
|
+
requirement: &68959790 !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ~>
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.5'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: *68959790
|
58
|
+
- !ruby/object:Gem::Dependency
|
59
|
+
name: sass
|
60
|
+
requirement: &68958990 !ruby/object:Gem::Requirement
|
61
|
+
none: false
|
62
|
+
requirements:
|
63
|
+
- - ~>
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: '3.1'
|
66
|
+
type: :runtime
|
67
|
+
prerelease: false
|
68
|
+
version_requirements: *68958990
|
69
|
+
description: ! " Reads a simple JSON watchlist, loads auction information and\n phone
|
70
|
+
specifications from multiple web resources and displays\n the smartphones in clear
|
71
|
+
HTML file.\n"
|
72
|
+
email: patek.mail@gmail.com
|
73
|
+
executables:
|
74
|
+
- watchmob
|
75
|
+
extensions: []
|
76
|
+
extra_rdoc_files: []
|
77
|
+
files:
|
78
|
+
- ./lib/watchmob/aukro_page.rb
|
79
|
+
- ./lib/watchmob/heureka_page.rb
|
80
|
+
- ./lib/watchmob/smartphone.rb
|
81
|
+
- ./lib/watchmob/page.rb
|
82
|
+
- ./lib/watchmob/gsmarena_search_page.rb
|
83
|
+
- ./lib/watchmob/watchlist.haml
|
84
|
+
- ./lib/watchmob/log.rb
|
85
|
+
- ./lib/watchmob/view_helpers.rb
|
86
|
+
- ./lib/watchmob/error.rb
|
87
|
+
- ./lib/watchmob/gsmarena_page.rb
|
88
|
+
- ./lib/watchmob/heureka_search_page.rb
|
89
|
+
- ./lib/watchmob/watchlist.rb
|
90
|
+
- ./lib/watchmob.rb
|
91
|
+
- !binary |-
|
92
|
+
YmluL3dhdGNobW9i
|
93
|
+
homepage: http://github.com/honzasp/watchmob
|
94
|
+
licenses:
|
95
|
+
- MIT
|
96
|
+
post_install_message:
|
97
|
+
rdoc_options: []
|
98
|
+
require_paths:
|
99
|
+
- lib
|
100
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
101
|
+
none: false
|
102
|
+
requirements:
|
103
|
+
- - ! '>='
|
104
|
+
- !ruby/object:Gem::Version
|
105
|
+
version: '0'
|
106
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
107
|
+
none: false
|
108
|
+
requirements:
|
109
|
+
- - ! '>='
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '0'
|
112
|
+
requirements: []
|
113
|
+
rubyforge_project:
|
114
|
+
rubygems_version: 1.8.17
|
115
|
+
signing_key:
|
116
|
+
specification_version: 3
|
117
|
+
summary: Watch smartphones on Aukro.cz and compare them
|
118
|
+
test_files: []
|