unicafe 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +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
|