unicafe 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +17 -0
- data/.rspec +2 -0
- data/.ruby-gemset +1 -0
- data/.ruby-version +1 -0
- data/.travis.yml +6 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +29 -0
- data/Rakefile +7 -0
- data/lib/unicafe.rb +20 -0
- data/lib/unicafe/lunch.rb +62 -0
- data/lib/unicafe/price_parser.rb +29 -0
- data/lib/unicafe/restaurant.rb +82 -0
- data/lib/unicafe/version.rb +3 -0
- data/spec/fixtures/example_menu.xml +1 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/unicafe/lunch_spec.rb +106 -0
- data/spec/unicafe/price_parser_spec.rb +33 -0
- data/spec/unicafe/restaurant_spec.rb +136 -0
- data/spec/unicafe_spec.rb +19 -0
- data/unicafe.gemspec +25 -0
- metadata +127 -0
checksums.yaml
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
ZDBhNmQzZDQ0MWFmMWQxMzk2NDhiZmI1MDkyMTQxYjZmZDZkODBiMA==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
ZjRkN2JkMjQ1NWRhM2E3NWFjNGMxMjYwZmE5YTZmYzM2ZmFlMDRjMQ==
|
7
|
+
SHA512:
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
YmE3YzY2ZTEyMjBhY2MxY2ExMDZjNWQyNGJkZDAwMWMyNGEzNzhlZWRjMzI4
|
10
|
+
ZmJmMzc4MDRlNTg3OTczMDNmYzA1YTNkNmI2YTIyMDg2ZGJhNzk4ODg0ZDVl
|
11
|
+
NWI0NDA2ZjllYTFhYTgzMzcwZDc4ODY4ZGVlNTVkZjE3MGI0ZDk=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
ZDI0ZTExNjZkYTU5MDljNWFlYmEzZWI3ZjRiZTg3ZGM4MjFiYjMyMWZmZWY0
|
14
|
+
M2FjMmRiMWVkNDJlMThkOWNmOTgwZjdjNzQ2MTI3M2UwNTQxMDZlYjZjOTdj
|
15
|
+
YWYzYTk1M2UzYmIxZjdmNTNjOWY2NWQwNmY1MjQyNDQwMWE2N2I=
|
data/.gitignore
ADDED
data/.rspec
ADDED
data/.ruby-gemset
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
unicafe
|
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
ruby-1.9.3-p448
|
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2012 Sami Saada
|
2
|
+
|
3
|
+
MIT License
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# Unicafe
|
2
|
+
|
3
|
+
Fetch Unicafe lunches with ruby!
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'unicafe'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install unicafe
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
TODO: Write usage instructions here
|
22
|
+
|
23
|
+
## Contributing
|
24
|
+
|
25
|
+
1. Fork it
|
26
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
27
|
+
3. Commit your changes (`git commit -am 'Added some feature'`)
|
28
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
29
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/unicafe.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require "unicafe/version"
|
2
|
+
require "unicafe/price_parser"
|
3
|
+
require "unicafe/lunch"
|
4
|
+
require "unicafe/restaurant"
|
5
|
+
require "geocoder"
|
6
|
+
|
7
|
+
module Unicafe
|
8
|
+
|
9
|
+
def self.get_restaurant name
|
10
|
+
::Unicafe::Restaurant.find_by_name name
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.nearest(latitude, longitude)
|
14
|
+
::Unicafe::Restaurant.nearest(latitude, longitude)
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.distances(latitude, longitude)
|
18
|
+
::Unicafe::Restaurant.distances(latitude, longitude)
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'feedzirra'
|
4
|
+
require 'net/http'
|
5
|
+
|
6
|
+
module Unicafe
|
7
|
+
class Lunch
|
8
|
+
|
9
|
+
attr_reader :name, :price, :date
|
10
|
+
|
11
|
+
def initialize name, price, date
|
12
|
+
@name = name
|
13
|
+
@price = price
|
14
|
+
@date = date
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.lunches_for_restaurant id
|
18
|
+
content = ::Net::HTTP.get(URI.parse("http://www.hyyravintolat.fi/rss/fin/#{id}/"))
|
19
|
+
self.parse_data content
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.parse_data data
|
23
|
+
parsed_data = Feedzirra::Feed.parse data
|
24
|
+
self.format_data parsed_data
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.format_data data
|
28
|
+
data.entries.map{|date| self.format_lunches_of_date(date)}.flatten.compact
|
29
|
+
end
|
30
|
+
|
31
|
+
def self.format_lunches_of_date data
|
32
|
+
date = self.parse_date data.title
|
33
|
+
Nokogiri::HTML::DocumentFragment.parse(data.summary).children.map{ |lunch|
|
34
|
+
self.format_lunch date, lunch
|
35
|
+
}
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.format_lunch date, data
|
39
|
+
self.new self.format_name(data), self.format_price(data), date
|
40
|
+
rescue Exception => e
|
41
|
+
end
|
42
|
+
|
43
|
+
def self.parse_date date
|
44
|
+
require 'date'
|
45
|
+
Date.parse date
|
46
|
+
end
|
47
|
+
|
48
|
+
def self.format_name data
|
49
|
+
name_span = data.children.select{|elem| elem.name == 'span' && elem[:class] == "meal"}.first
|
50
|
+
text_element = name_span.children.first
|
51
|
+
text_element.to_s
|
52
|
+
end
|
53
|
+
|
54
|
+
def self.format_price data
|
55
|
+
name_span = data.children.select{|elem| elem.name == 'span' && elem[:class] == "priceinfo"}.first
|
56
|
+
text_element = name_span.children.first
|
57
|
+
parser = ::Unicafe::PriceParser.new
|
58
|
+
parser.parse text_element.to_s
|
59
|
+
end
|
60
|
+
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Unicafe
|
4
|
+
class PriceParser
|
5
|
+
|
6
|
+
MAUKKAASTI = '4.20'
|
7
|
+
EDULLISESTI = '2.60'
|
8
|
+
KEVYESTI = '2.30'
|
9
|
+
|
10
|
+
def parse price_string
|
11
|
+
case price_string
|
12
|
+
when 'Maukkaasti'
|
13
|
+
MAUKKAASTI
|
14
|
+
when 'Edullisesti'
|
15
|
+
EDULLISESTI
|
16
|
+
when 'Kevyesti'
|
17
|
+
KEVYESTI
|
18
|
+
when /^Makeasti (.*)$/
|
19
|
+
price_string.match(/([,0-9]+)/)[0].gsub(',','.')
|
20
|
+
else
|
21
|
+
raise PriceError
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class PriceError < Exception
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
module Unicafe
|
4
|
+
class Restaurant
|
5
|
+
|
6
|
+
LIST_OF_RESTAURANTS = {
|
7
|
+
1 => {name: "Metsätalo", latitude: 60.172577, longitude: 24.948878},
|
8
|
+
2 => {name: "Olivia", latitude: 60.175077, longitude: 24.952979},
|
9
|
+
3 => {name: "Porthania", latitude: 60.169878, longitude: 24.948669},
|
10
|
+
4 => {name: "Päärakennus", latitude: 60.169178, longitude: 24.949297},
|
11
|
+
5 => {name: "Rotunda", latitude: 60.170332, longitude: 24.950791},
|
12
|
+
6 => {name: "Topelias", latitude: 60.171806, longitude: 24.95067},
|
13
|
+
7 => {name: "Valtiotiede", latitude: 60.173897, longitude: 24.953095},
|
14
|
+
8 => {name: "Ylioppilasaukio", latitude: 60.169092, longitude: 24.93992},
|
15
|
+
10 => {name: "Chemicum", latitude: 60.205108, longitude: 24.963357},
|
16
|
+
11 => {name: "Exactum", latitude: 60.20509, longitude: 24.961209},
|
17
|
+
12 => {name: "Physicum", latitude: 60.204755, longitude: 24.963200},
|
18
|
+
13 => {name: "Meilahti", latitude: 60.190212, longitude: 24.908911},
|
19
|
+
14 => {name: "Ruskeasuo", latitude: 60.206341, longitude: 24.895871},
|
20
|
+
15 => {name: "Soc & kom", latitude: 60.173054, longitude: 24.95049},
|
21
|
+
16 => {name: "Kookos", latitude: 60.181034, longitude: 24.958652},
|
22
|
+
18 => {name: "Biokeskus", latitude: 60.226922, longitude: 25.013707},
|
23
|
+
19 => {name: "Korona", latitude: 60.226922, longitude: 25.013707},
|
24
|
+
21 => {name: "Viikuna", latitude: 60.23049, longitude: 25.020544}
|
25
|
+
}
|
26
|
+
|
27
|
+
def initialize id
|
28
|
+
@id = id
|
29
|
+
end
|
30
|
+
|
31
|
+
def name
|
32
|
+
@name ||= LIST_OF_RESTAURANTS[@id][:name]
|
33
|
+
end
|
34
|
+
|
35
|
+
def latitude
|
36
|
+
@latitude ||= LIST_OF_RESTAURANTS[@id][:latitude]
|
37
|
+
end
|
38
|
+
|
39
|
+
def longitude
|
40
|
+
@longitude ||= LIST_OF_RESTAURANTS[@id][:longitude]
|
41
|
+
end
|
42
|
+
|
43
|
+
def lunches
|
44
|
+
::Unicafe::Lunch.lunches_for_restaurant(@id)
|
45
|
+
end
|
46
|
+
|
47
|
+
def distance(latitude, longitude)
|
48
|
+
Geocoder::Calculations.distance_between([self.latitude, self.longitude],
|
49
|
+
[latitude, longitude],
|
50
|
+
:units => :km)
|
51
|
+
end
|
52
|
+
|
53
|
+
def self.find_by_id id
|
54
|
+
self.new id
|
55
|
+
end
|
56
|
+
|
57
|
+
def self.find_by_name name
|
58
|
+
self.find_by_id self.name_to_id(name)
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.name_to_id name
|
62
|
+
LIST_OF_RESTAURANTS.select{|key, hash| hash[:name] == name}.map{|key, hash| key}.first || raise(NotFound)
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.nearest(latitude, longitude)
|
66
|
+
distances_with_restaurants = self.distances(latitude, longitude)
|
67
|
+
distances_with_restaurants[distances_with_restaurants.keys.min]
|
68
|
+
end
|
69
|
+
|
70
|
+
def self.distances(latitude, longitude)
|
71
|
+
distances = {}
|
72
|
+
LIST_OF_RESTAURANTS.each do |key, hash|
|
73
|
+
restaurant = self.find_by_id(key)
|
74
|
+
distances[restaurant.distance(latitude, longitude)] = restaurant
|
75
|
+
end
|
76
|
+
distances
|
77
|
+
end
|
78
|
+
|
79
|
+
class NotFound < Exception
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><atom:link href="http://www.unicafe.fi/lounastyokalu/index.php?option=com_ruokalista_rss&lang=fin&rid=10&day=" rel="self" type="application/rss+xml" /><title><![CDATA[Kumpula / Chemicum]]></title><link>/lounas/ravintolat#chemicum</link><description><![CDATA[Ruokalista / viikko 39]]></description><item><title><![CDATA[Maanantai 24.09.2012]]></title><description><![CDATA[<p><span class="meal">Nakkistroganoffia</span> <em>(l,v)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Kalaa tacokastikkeessa</span> <em>(g,l,v)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Kasvispihvit, papusalsaa</span> <em>(g,k,l,v,ve)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Meksikolainen jauhelihasalaatti</span> <em>(g,l,v)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Hedelmärahkaa</span> <em>(g,k,vl)</em>, <span class="priceinfo">Makeasti 1,20€</span></p>]]></description><guid isPermaLink="false">/lounas/ravintolat#chemicum&d=1</guid></item><item><title><![CDATA[Tiistai 25.09.2012]]></title><description><![CDATA[<p><span class="meal">Härkä-nuudeliwokkia</span> <em>(l,v)</em>, <span class="priceinfo">Maukkaasti</span></p><p><span class="meal">Rosepippuri broilerkastiketta</span> <em>(v,vl)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Sieni-kasvisrisotto</span> <em>(g,k,l,v,ve)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Kinkkusalaatti</span> <em>(g,l)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Kasvisborschkeittoa</span> <em>(g,k,l,se)</em>, <span class="priceinfo">Kevyesti</span></p><p><span class="meal">Sitruunakakkua</span> <em>(k,l)</em>, <span class="priceinfo">Makeasti 1,20€</span></p>]]></description><guid isPermaLink="false">/lounas/ravintolat#chemicum&d=2</guid></item><item><title><![CDATA[Keskiviikko 26.09.2012]]></title><description><![CDATA[<p><span class="meal">Porsaanleike, pippurikastiketta</span> <em>(l)</em>, <span class="priceinfo">Maukkaasti</span></p><p><span class="meal">Lohilasagnea</span> <em>(vl)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Soijaa thaicurrykastikkeessa</span> <em>(g,k,l,v,ve)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Lihakeittoa</span> <em>(g,l)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Hedelmäinen kalkkunasalaatti</span> <em>(l)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Kaurapaistosta</span> <em>(k,vl)</em>, <span class="priceinfo">Makeasti 1,20€</span></p>]]></description><guid isPermaLink="false">/lounas/ravintolat#chemicum&d=3</guid></item><item><title><![CDATA[Torstai 27.09.2012]]></title><description><![CDATA[<p><span class="meal">Burgundin härkää</span> <em>(g,l,v)</em>, <span class="priceinfo">Maukkaasti</span></p><p><span class="meal">Seitä pestokastikkeessa</span> <em>(g,l,pä,v)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Hernekeittoa</span> <em>(g,l)</em>, <span class="meal">pannukakkua ja hilloa</span>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Kasvishernekeittoa</span> <em>(g,k,l,ve)</em>, <span class="meal">pannukakkua ja hilloa</span>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Raejuustosalaatti</span> <em>(k,l)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Kasvishernekeittoa</span> <em>(g,k,l,ve)</em>, <span class="priceinfo">Kevyesti</span></p><p><span class="meal">Pannukakkua ja hilloa</span> <em>(k)</em>, <span class="priceinfo">Makeasti 1,00€</span></p>]]></description><guid isPermaLink="false">/lounas/ravintolat#chemicum&d=4</guid></item><item><title><![CDATA[Perjantai 28.09.2012]]></title><description><![CDATA[<p><span class="meal">Smetanalohta</span> <em>(g,l)</em>, <span class="priceinfo">Maukkaasti</span></p><p><span class="meal">Kebabkastiketta</span> <em>(g,l,v)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Kasvispyttipannua</span> <em>(g,k,l,ve)</em>, <span class="priceinfo">Edullisesti</span></p><p><span class="meal">Mango-juustokakkua</span> <em>(k,vl)</em>, <span class="priceinfo">Makeasti 1,20€</span></p>]]></description><guid isPermaLink="false">/lounas/ravintolat#chemicum&d=5</guid></item><item><title><![CDATA[Lauantai 29.09.2012]]></title><description><![CDATA[<p><span class="meal">HYVÄÄ VIIKONLOPPUA !</span></p>]]></description><guid isPermaLink="false">/lounas/ravintolat#chemicum&d=6</guid></item><item><title><![CDATA[Sunnuntai 30.09.2012]]></title><description><![CDATA[<p><span class="meal">HYVÄÄ VIIKONLOPPUA !</span></p>]]></description><guid isPermaLink="false">/lounas/ravintolat#chemicum&d=7</guid></item></channel></rss>
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rspec'
|
2
|
+
require 'fakeweb'
|
3
|
+
require 'unicafe'
|
4
|
+
|
5
|
+
FakeWeb.allow_net_connect = false
|
6
|
+
|
7
|
+
FIXTURES = File.dirname(File.expand_path(__FILE__)) + "/fixtures"
|
8
|
+
|
9
|
+
def example_menu_xml
|
10
|
+
File.read("#{FIXTURES}/example_menu.xml")
|
11
|
+
end
|
12
|
+
|
13
|
+
RSpec.configure do |config|
|
14
|
+
config.treat_symbols_as_metadata_keys_with_true_values = true
|
15
|
+
config.run_all_when_everything_filtered = true
|
16
|
+
config.filter_run :focus
|
17
|
+
config.order = 'random'
|
18
|
+
config.before(:each) do
|
19
|
+
FakeWeb.clean_registry
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,106 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Unicafe::Lunch do
|
6
|
+
|
7
|
+
let(:restaurant_id) {123}
|
8
|
+
let(:lunch_mock) {mock(Unicafe::Lunch)}
|
9
|
+
let(:lunches_mock) {[lunch_mock]}
|
10
|
+
let(:restaurant_data) {example_menu_xml}
|
11
|
+
let(:menu_uri) {"http://www.hyyravintolat.fi/rss/fin/#{restaurant_id}/"}
|
12
|
+
let(:parsed_data) {mock(Feedzirra::Feed)}
|
13
|
+
let(:entry_mock) {mock(Feedzirra::Parser::RSSEntry)}
|
14
|
+
let(:entries_mock) {[entry_mock]}
|
15
|
+
let(:date) {mock(Date)}
|
16
|
+
let(:lunch_html_mock) {mock(Nokogiri::XML::Element)}
|
17
|
+
let(:name) {"fish & chips (VL)"}
|
18
|
+
let(:price) {"2.60"}
|
19
|
+
let(:text_element_mock) {mock(Nokogiri::XML::Text)}
|
20
|
+
let(:span_mock) {mock(Nokogiri::XML::Element)}
|
21
|
+
let(:spans_mock) {[span_mock]}
|
22
|
+
|
23
|
+
it "should fetch lunches according restaurant" do
|
24
|
+
FakeWeb.register_uri(:get, menu_uri, body: restaurant_data)
|
25
|
+
Unicafe::Lunch.should_receive(:parse_data).with(restaurant_data).and_return(lunches_mock)
|
26
|
+
Unicafe::Lunch.lunches_for_restaurant(restaurant_id).should == lunches_mock
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should parse lunches from XML" do
|
30
|
+
Feedzirra::Feed.should_receive(:parse).with(restaurant_data).and_return(parsed_data)
|
31
|
+
Unicafe::Lunch.should_receive(:format_data).with(parsed_data).and_return(lunches_mock)
|
32
|
+
Unicafe::Lunch.parse_data(restaurant_data).should == lunches_mock
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should format lunches from feed" do
|
36
|
+
parsed_data.should_receive(:entries).and_return(entries_mock)
|
37
|
+
Unicafe::Lunch.should_receive(:format_lunches_of_date).with(entry_mock).and_return(lunches_mock)
|
38
|
+
Unicafe::Lunch.format_data(parsed_data).should == lunches_mock
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should format lunches of day" do
|
42
|
+
title_text = "foobar"
|
43
|
+
summary = "foobar"
|
44
|
+
lunches_html_mock = mock(Nokogiri::XML::Element)
|
45
|
+
entry_mock.should_receive(:title).and_return(title_text)
|
46
|
+
entry_mock.should_receive(:summary).and_return(summary)
|
47
|
+
Unicafe::Lunch.should_receive(:parse_date).with(title_text).and_return(date)
|
48
|
+
Nokogiri::HTML::DocumentFragment.should_receive(:parse).with(summary).and_return(lunches_html_mock)
|
49
|
+
lunches_html_mock.should_receive(:children).and_return([lunch_html_mock])
|
50
|
+
Unicafe::Lunch.should_receive(:format_lunch).with(date, lunch_html_mock).and_return(lunch_mock)
|
51
|
+
Unicafe::Lunch.format_lunches_of_date(entry_mock).should == lunches_mock
|
52
|
+
end
|
53
|
+
|
54
|
+
it "should format lunch with date and data" do
|
55
|
+
Unicafe::Lunch.should_receive(:format_name).with(lunch_html_mock).and_return(name)
|
56
|
+
Unicafe::Lunch.should_receive(:format_price).with(lunch_html_mock).and_return(price)
|
57
|
+
Unicafe::Lunch.should_receive(:new).with(name, price, date).and_return(lunch_mock)
|
58
|
+
Unicafe::Lunch.format_lunch(date, lunch_html_mock).should == lunch_mock
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should parse date" do
|
62
|
+
date_string = "Maanantai 24.09.2012"
|
63
|
+
Unicafe::Lunch.parse_date(date_string).to_s.should == "2012-09-24"
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should format name" do
|
67
|
+
lunch_html_mock.should_receive(:children).and_return(spans_mock)
|
68
|
+
span_mock.should_receive(:name).and_return('span')
|
69
|
+
span_mock.should_receive(:[]).with(:class).and_return('meal')
|
70
|
+
span_mock.should_receive(:children).and_return([text_element_mock])
|
71
|
+
text_element_mock.should_receive(:to_s).and_return(name)
|
72
|
+
Unicafe::Lunch.format_name(lunch_html_mock).should == name
|
73
|
+
end
|
74
|
+
|
75
|
+
it "should format price" do
|
76
|
+
price_string = "Edullisesti"
|
77
|
+
price_parser = mock(Unicafe::PriceParser)
|
78
|
+
lunch_html_mock.should_receive(:children).and_return(spans_mock)
|
79
|
+
span_mock.should_receive(:name).and_return('span')
|
80
|
+
span_mock.should_receive(:[]).with(:class).and_return('priceinfo')
|
81
|
+
span_mock.should_receive(:children).and_return([text_element_mock])
|
82
|
+
text_element_mock.should_receive(:to_s).and_return(price_string)
|
83
|
+
Unicafe::PriceParser.should_receive(:new).and_return(price_parser)
|
84
|
+
price_parser.should_receive(:parse).with(price_string).and_return(price)
|
85
|
+
Unicafe::Lunch.format_price(lunch_html_mock).should == price
|
86
|
+
end
|
87
|
+
|
88
|
+
context "instance" do
|
89
|
+
|
90
|
+
let!(:lunch) {Unicafe::Lunch.new(name, price, date)}
|
91
|
+
|
92
|
+
it "should give name" do
|
93
|
+
lunch.name.should == name
|
94
|
+
end
|
95
|
+
|
96
|
+
it "should give price" do
|
97
|
+
lunch.price.should == price
|
98
|
+
end
|
99
|
+
|
100
|
+
it "should give date" do
|
101
|
+
lunch.date.should == date
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Unicafe::PriceParser do
|
6
|
+
|
7
|
+
let!(:parser) {Unicafe::PriceParser.new}
|
8
|
+
|
9
|
+
it "should replace maukkaasti with correct value" do
|
10
|
+
parser.parse("Maukkaasti").should == Unicafe::PriceParser::MAUKKAASTI
|
11
|
+
end
|
12
|
+
|
13
|
+
it "should replace edullisesti with correct value" do
|
14
|
+
parser.parse("Edullisesti").should == Unicafe::PriceParser::EDULLISESTI
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should replace kevyesti with correct value" do
|
18
|
+
parser.parse("Kevyesti").should == Unicafe::PriceParser::KEVYESTI
|
19
|
+
end
|
20
|
+
|
21
|
+
it "should replace makeasti with one value" do
|
22
|
+
parser.parse("Makeasti 1,20€").should == '1.20'
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should replace makeasti with another value" do
|
26
|
+
parser.parse("Makeasti 1,00€").should == '1.00'
|
27
|
+
end
|
28
|
+
|
29
|
+
it "should raise error when not supported value" do
|
30
|
+
expect{parser.parse("Trololoo")}.to raise_error(Unicafe::PriceParser::PriceError)
|
31
|
+
end
|
32
|
+
|
33
|
+
end
|
@@ -0,0 +1,136 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe Unicafe::Restaurant do
|
6
|
+
|
7
|
+
let(:name) {"Example restaurant"}
|
8
|
+
let(:id) {123}
|
9
|
+
let(:restaurant_mock) {mock(Unicafe::Restaurant)}
|
10
|
+
|
11
|
+
it "should find restaurant by name" do
|
12
|
+
Unicafe::Restaurant.should_receive(:name_to_id).with(name).and_return(id)
|
13
|
+
Unicafe::Restaurant.should_receive(:find_by_id).with(id).and_return(restaurant_mock)
|
14
|
+
Unicafe::Restaurant.find_by_name(name).should == restaurant_mock
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should raise error if name isn't supported" do
|
18
|
+
Unicafe::Restaurant.should_receive(:name_to_id).with(name).and_raise(Unicafe::Restaurant::NotFound)
|
19
|
+
expect{Unicafe::Restaurant.find_by_name(name)}.to raise_error(Unicafe::Restaurant::NotFound)
|
20
|
+
end
|
21
|
+
|
22
|
+
it "should give restaurant object with correct id" do
|
23
|
+
Unicafe::Restaurant.should_receive(:new).with(id).and_return(restaurant_mock)
|
24
|
+
Unicafe::Restaurant.find_by_id(id).should == restaurant_mock
|
25
|
+
end
|
26
|
+
|
27
|
+
describe "#name_to_id" do
|
28
|
+
|
29
|
+
it "should give correct id for given restaurant" do
|
30
|
+
Unicafe::Restaurant::LIST_OF_RESTAURANTS.each do |id, r_hash|
|
31
|
+
Unicafe::Restaurant.name_to_id(r_hash[:name]).should == id
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
it "should raise error when unknown name is given" do
|
36
|
+
expect{ Unicafe::Restaurant.name_to_id("This restaurant doesn't exist") }.to raise_error(Unicafe::Restaurant::NotFound)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
describe "list of restaurants" do
|
42
|
+
|
43
|
+
def check_id_name_pairs id, name
|
44
|
+
Unicafe::Restaurant::LIST_OF_RESTAURANTS[id].should == name
|
45
|
+
end
|
46
|
+
|
47
|
+
it "should contain all defined pairs" do
|
48
|
+
[
|
49
|
+
{id: 1, hash: {name: "Metsätalo", latitude: 60.172577, longitude: 24.948878}},
|
50
|
+
{id: 2, hash: {name: "Olivia", latitude: 60.175077, longitude: 24.952979}},
|
51
|
+
{id: 3, hash: {name: "Porthania", latitude: 60.169878, longitude: 24.948669}},
|
52
|
+
{id: 4, hash: {name: "Päärakennus", latitude: 60.169178, longitude: 24.949297}},
|
53
|
+
{id: 5, hash: {name: "Rotunda", latitude: 60.170332, longitude: 24.950791}},
|
54
|
+
{id: 6, hash: {name: "Topelias", latitude: 60.171806, longitude: 24.95067}},
|
55
|
+
{id: 7, hash: {name: "Valtiotiede", latitude: 60.173897, longitude: 24.953095}},
|
56
|
+
{id: 8, hash: {name: "Ylioppilasaukio", latitude: 60.169092, longitude: 24.93992}},
|
57
|
+
{id: 10, hash: {name: "Chemicum", latitude: 60.205108, longitude: 24.963357}},
|
58
|
+
{id: 11, hash: {name: "Exactum", latitude: 60.20509, longitude: 24.961209}},
|
59
|
+
{id: 12, hash: {name: "Physicum", latitude: 60.204755, longitude: 24.963200}},
|
60
|
+
{id: 13, hash: {name: "Meilahti", latitude: 60.190212, longitude: 24.908911}},
|
61
|
+
{id: 14, hash: {name: "Ruskeasuo", latitude: 60.206341, longitude: 24.895871}},
|
62
|
+
{id: 15, hash: {name: "Soc & kom", latitude: 60.173054, longitude: 24.95049}},
|
63
|
+
{id: 16, hash: {name: "Kookos", latitude: 60.181034, longitude: 24.958652}},
|
64
|
+
{id: 18, hash: {name: "Biokeskus", latitude: 60.226922, longitude: 25.013707}},
|
65
|
+
{id: 19, hash: {name: "Korona", latitude: 60.226922, longitude: 25.013707}},
|
66
|
+
{id: 21, hash: {name: "Viikuna", latitude: 60.23049, longitude: 25.020544}},
|
67
|
+
].each do |hash|
|
68
|
+
check_id_name_pairs hash[:id], hash[:hash]
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
|
74
|
+
context "instance" do
|
75
|
+
|
76
|
+
let!(:restaurant) {Unicafe::Restaurant.new(id)}
|
77
|
+
let(:lunches_mock) {[mock(Unicafe::Lunch)]}
|
78
|
+
let(:latitude) {1.234}
|
79
|
+
let(:longitude) {1.234}
|
80
|
+
let(:hash) {{name: name, latitude: latitude, longitude: longitude}}
|
81
|
+
|
82
|
+
it "should have correct name" do
|
83
|
+
Unicafe::Restaurant::LIST_OF_RESTAURANTS.should_receive(:[]).with(id).and_return(hash)
|
84
|
+
restaurant.name.should == name
|
85
|
+
end
|
86
|
+
|
87
|
+
it "should give lunches" do
|
88
|
+
Unicafe::Lunch.should_receive(:lunches_for_restaurant).with(id).and_return(lunches_mock)
|
89
|
+
restaurant.lunches.should == lunches_mock
|
90
|
+
end
|
91
|
+
|
92
|
+
it "should give latitude" do
|
93
|
+
Unicafe::Restaurant::LIST_OF_RESTAURANTS.should_receive(:[]).with(id).and_return(hash)
|
94
|
+
restaurant.latitude.should == latitude
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should give longitude" do
|
98
|
+
Unicafe::Restaurant::LIST_OF_RESTAURANTS.should_receive(:[]).with(id).and_return(hash)
|
99
|
+
restaurant.longitude.should == longitude
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
context "geocoding" do
|
105
|
+
let(:latitude) {60.170331}
|
106
|
+
let(:longitude) {24.950792}
|
107
|
+
let(:distance) {0.123}
|
108
|
+
let(:restaurants_distances_mock) {{distance => restaurant_mock}}
|
109
|
+
|
110
|
+
it "nearest should return restaurant with nearest location" do
|
111
|
+
Unicafe::Restaurant.should_receive(:distances).with(latitude, longitude).and_return(restaurants_distances_mock)
|
112
|
+
restaurants_distances_mock.should_receive(:keys).and_return([distance])
|
113
|
+
Unicafe::Restaurant.nearest(latitude, longitude).should == restaurant_mock
|
114
|
+
end
|
115
|
+
|
116
|
+
it "distances should return distances in km to each restaurant" do
|
117
|
+
Unicafe::Restaurant.should_receive(:find_by_id).exactly(Unicafe::Restaurant::LIST_OF_RESTAURANTS.size).and_return(restaurant_mock)
|
118
|
+
restaurant_mock.should_receive(:distance).with(latitude, longitude).exactly(Unicafe::Restaurant::LIST_OF_RESTAURANTS.size).and_return(distance)
|
119
|
+
Unicafe::Restaurant.distances(latitude, longitude)
|
120
|
+
end
|
121
|
+
|
122
|
+
it "should return distance in km to restaurant" do
|
123
|
+
restaurant_latitude, restaurant_longitude = 1.23, 1.23
|
124
|
+
restaurant = Unicafe::Restaurant.new(99)
|
125
|
+
restaurant.should_receive(:latitude).and_return(restaurant_latitude)
|
126
|
+
restaurant.should_receive(:longitude).and_return(restaurant_longitude)
|
127
|
+
Geocoder::Calculations.should_receive(:distance_between).
|
128
|
+
with([restaurant_latitude, restaurant_longitude],
|
129
|
+
[latitude, longitude],
|
130
|
+
:units => :km).
|
131
|
+
and_return(distance)
|
132
|
+
restaurant.distance(latitude, longitude).should == distance
|
133
|
+
end
|
134
|
+
|
135
|
+
end
|
136
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Unicafe do
|
4
|
+
|
5
|
+
let(:restaurant_name) {"example restaurant"}
|
6
|
+
|
7
|
+
it "should be able to give specific restaurant" do
|
8
|
+
restaurant_mock = mock(Unicafe::Restaurant)
|
9
|
+
Unicafe::Restaurant.should_receive(:find_by_name).with(restaurant_name).and_return(restaurant_mock)
|
10
|
+
|
11
|
+
Unicafe.get_restaurant(restaurant_name).should == restaurant_mock
|
12
|
+
end
|
13
|
+
|
14
|
+
it "should raise error if specific restaurant can't be found" do
|
15
|
+
Unicafe::Restaurant.should_receive(:find_by_name).with(restaurant_name).and_raise(Unicafe::Restaurant::NotFound)
|
16
|
+
expect{Unicafe.get_restaurant(restaurant_name)}.to raise_error(Unicafe::Restaurant::NotFound)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
data/unicafe.gemspec
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/unicafe/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Sami Saada"]
|
6
|
+
gem.email = ["sami.saada@gmail.com"]
|
7
|
+
gem.description = %q{Gem for fetching Unicafe lunch data}
|
8
|
+
gem.summary = %q{Fetches and parses Unicafe rss feeds, might even find
|
9
|
+
lunch data.}
|
10
|
+
gem.homepage = "https://github.com/samitheberber/unicafe"
|
11
|
+
gem.license = 'MIT'
|
12
|
+
|
13
|
+
gem.files = `git ls-files`.split($\)
|
14
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
15
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
16
|
+
gem.name = "unicafe"
|
17
|
+
gem.require_paths = ["lib"]
|
18
|
+
gem.version = Unicafe::VERSION
|
19
|
+
|
20
|
+
gem.add_dependency 'feedzirra'
|
21
|
+
gem.add_dependency 'geocoder'
|
22
|
+
|
23
|
+
gem.add_development_dependency 'rspec'
|
24
|
+
gem.add_development_dependency 'fakeweb'
|
25
|
+
end
|
metadata
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: unicafe
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sami Saada
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-10-23 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: feedzirra
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - ! '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - ! '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: geocoder
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ! '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ! '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ! '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: fakeweb
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - ! '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
description: Gem for fetching Unicafe lunch data
|
70
|
+
email:
|
71
|
+
- sami.saada@gmail.com
|
72
|
+
executables: []
|
73
|
+
extensions: []
|
74
|
+
extra_rdoc_files: []
|
75
|
+
files:
|
76
|
+
- .gitignore
|
77
|
+
- .rspec
|
78
|
+
- .ruby-gemset
|
79
|
+
- .ruby-version
|
80
|
+
- .travis.yml
|
81
|
+
- Gemfile
|
82
|
+
- LICENSE
|
83
|
+
- README.md
|
84
|
+
- Rakefile
|
85
|
+
- lib/unicafe.rb
|
86
|
+
- lib/unicafe/lunch.rb
|
87
|
+
- lib/unicafe/price_parser.rb
|
88
|
+
- lib/unicafe/restaurant.rb
|
89
|
+
- lib/unicafe/version.rb
|
90
|
+
- spec/fixtures/example_menu.xml
|
91
|
+
- spec/spec_helper.rb
|
92
|
+
- spec/unicafe/lunch_spec.rb
|
93
|
+
- spec/unicafe/price_parser_spec.rb
|
94
|
+
- spec/unicafe/restaurant_spec.rb
|
95
|
+
- spec/unicafe_spec.rb
|
96
|
+
- unicafe.gemspec
|
97
|
+
homepage: https://github.com/samitheberber/unicafe
|
98
|
+
licenses:
|
99
|
+
- MIT
|
100
|
+
metadata: {}
|
101
|
+
post_install_message:
|
102
|
+
rdoc_options: []
|
103
|
+
require_paths:
|
104
|
+
- lib
|
105
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
106
|
+
requirements:
|
107
|
+
- - ! '>='
|
108
|
+
- !ruby/object:Gem::Version
|
109
|
+
version: '0'
|
110
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
111
|
+
requirements:
|
112
|
+
- - ! '>='
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '0'
|
115
|
+
requirements: []
|
116
|
+
rubyforge_project:
|
117
|
+
rubygems_version: 2.1.9
|
118
|
+
signing_key:
|
119
|
+
specification_version: 4
|
120
|
+
summary: Fetches and parses Unicafe rss feeds, might even find lunch data.
|
121
|
+
test_files:
|
122
|
+
- spec/fixtures/example_menu.xml
|
123
|
+
- spec/spec_helper.rb
|
124
|
+
- spec/unicafe/lunch_spec.rb
|
125
|
+
- spec/unicafe/price_parser_spec.rb
|
126
|
+
- spec/unicafe/restaurant_spec.rb
|
127
|
+
- spec/unicafe_spec.rb
|