t6d-mensa 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/lib/mensa/fare.rb ADDED
@@ -0,0 +1,89 @@
1
+ # coding: utf-8
2
+
3
+ module Mensa
4
+ class Fare
5
+ TYPES = {
6
+ 'S' => :pork,
7
+ 'R' => :beef,
8
+ 'K' => :veal,
9
+ 'L' => :lamb,
10
+ 'G' => :poultry,
11
+ 'F' => :fish,
12
+ 'FL' => :meatless
13
+ }
14
+
15
+ attr_accessor :name
16
+ attr_accessor :type
17
+ attr_accessor :price
18
+
19
+ def initialize(name, type = nil, price = nil)
20
+ self.name = name
21
+ self.type = type
22
+ self.price = price
23
+ end
24
+
25
+ def name=(value)
26
+ value = value.to_s.strip
27
+
28
+ raise NameEmptyError, "An empty string or nil are not allowed as argument", caller if
29
+ value.empty?
30
+
31
+ raise NameTooLongError, "Name of fare is greater than 255 characters", caller if
32
+ value.length > 255
33
+
34
+ @name = value
35
+ end
36
+
37
+ def type=(value)
38
+ if value.nil?
39
+ @type = nil
40
+ elsif TYPES.values.include?(value)
41
+ @type = value
42
+ elsif TYPES.keys.include?(value)
43
+ @type = TYPES[value]
44
+ else
45
+ raise TypeUnknownError, "Type #{value} is invalid", caller
46
+ end
47
+
48
+ @type
49
+ end
50
+
51
+ def price=(value)
52
+ if value.nil?
53
+ @price = nil
54
+ elsif value.kind_of?(Integer)
55
+ @price = value
56
+ elsif value.kind_of?(Float)
57
+ euros, cents = /(\d+)\.(\d{1,2})/.match(value.to_s)[1..2]
58
+ if cents.length < 2
59
+ @price = euros.to_i * 100 + cents.to_i * 10
60
+ else
61
+ @price = euros.to_i * 100 + cents.to_i
62
+ end
63
+ elsif value.respond_to?(:to_s)
64
+ if group = /(\d+)[.,](\d{2})/.match(value.to_s)
65
+ euros, cents = group[1..2]
66
+ @price = euros.to_i * 100 + cents.to_i
67
+ elsif value.to_s.empty?
68
+ @price = nil
69
+ else
70
+ raise PriceStringInvalid,
71
+ "Fare price could not be extracted out of #{value.inspect}",
72
+ caller
73
+ end
74
+ end
75
+
76
+ @price
77
+ end
78
+
79
+ def ==(other)
80
+ self.name == other.name &&
81
+ self.type == other.type &&
82
+ self.price == other.price
83
+ end
84
+
85
+ def inspect
86
+ "Fare(#{name.inspect}, #{type.inspect}, #{price})"
87
+ end
88
+ end
89
+ end
data/lib/mensa.rb ADDED
@@ -0,0 +1,18 @@
1
+ # coding: utf-8
2
+
3
+ require 'uri'
4
+ require 'net/http'
5
+ require 'iconv'
6
+
7
+ require 'rubygems'
8
+ require 'nokogiri'
9
+
10
+ require 'mensa/canteen'
11
+ require 'mensa/client'
12
+ require 'mensa/document'
13
+ require 'mensa/exceptions'
14
+ require 'mensa/fare'
15
+
16
+ module Mensa
17
+ MENSA_DIR = File.join(File.dirname(__FILE__), '..')
18
+ end
data/mensa.gemspec ADDED
@@ -0,0 +1,73 @@
1
+ # -*- encoding: utf-8 -*-
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = %q{mensa}
5
+ s.version = "0.1.0"
6
+
7
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
8
+ s.authors = ["Konstantin Tennhard"]
9
+ s.date = %q{2009-06-02}
10
+ s.default_executable = %q{mensa}
11
+ s.email = %q{development@tennhard.net}
12
+ s.executables = ["mensa"]
13
+ s.extra_rdoc_files = [
14
+ "LICENSE",
15
+ "README.rdoc"
16
+ ]
17
+ s.files = [
18
+ ".document",
19
+ ".gitignore",
20
+ "LICENSE",
21
+ "README.rdoc",
22
+ "Rakefile",
23
+ "VERSION",
24
+ "bin/mensa",
25
+ "config/canteens.rb",
26
+ "fixtures/canteens.rb",
27
+ "fixtures/mensa-dieburg.html",
28
+ "fixtures/mensa-haardtring.html",
29
+ "fixtures/mensa-lichtwiese.html",
30
+ "fixtures/mensa-schofferstrase.html",
31
+ "fixtures/mensa-stadtmitte.html",
32
+ "fixtures/output.txt",
33
+ "lib/mensa.rb",
34
+ "lib/mensa/canteen.rb",
35
+ "lib/mensa/client.rb",
36
+ "lib/mensa/document.rb",
37
+ "lib/mensa/exceptions.rb",
38
+ "lib/mensa/fare.rb",
39
+ "mensa.gemspec",
40
+ "spec/mensa/canteen_spec.rb",
41
+ "spec/mensa/client_spec.rb",
42
+ "spec/mensa/document_spec.rb",
43
+ "spec/mensa/fare_spec.rb",
44
+ "spec/mensa_spec.rb",
45
+ "spec/spec_helper.rb"
46
+ ]
47
+ s.homepage = %q{http://github.com/t6d/mensa}
48
+ s.rdoc_options = ["--charset=UTF-8"]
49
+ s.require_paths = ["lib"]
50
+ s.rubygems_version = %q{1.3.3}
51
+ s.summary = %q{Fetches the daily bill of fare from the canteen page of Studentenwerk Darmstadt.}
52
+ s.test_files = [
53
+ "spec/mensa/canteen_spec.rb",
54
+ "spec/mensa/client_spec.rb",
55
+ "spec/mensa/document_spec.rb",
56
+ "spec/mensa/fare_spec.rb",
57
+ "spec/mensa_spec.rb",
58
+ "spec/spec_helper.rb"
59
+ ]
60
+
61
+ if s.respond_to? :specification_version then
62
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
63
+ s.specification_version = 3
64
+
65
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
66
+ s.add_runtime_dependency(%q<nokogiri>, [">= 1.2.3"])
67
+ else
68
+ s.add_dependency(%q<nokogiri>, [">= 1.2.3"])
69
+ end
70
+ else
71
+ s.add_dependency(%q<nokogiri>, [">= 1.2.3"])
72
+ end
73
+ end
@@ -0,0 +1,81 @@
1
+ # coding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '../spec_helper')
4
+
5
+ module Mensa
6
+ uri = {}
7
+ uri[:stadtmitte] = "http://www.studentenwerkdarmstadt.de/essen/mensa-stadtmitte.html"
8
+ uri[:lichtwiese] = "http://www.studentenwerkdarmstadt.de/essen/mensa-lichtwiese.html"
9
+ uri[:haardtring] = "http://www.studentenwerkdarmstadt.de/essen/mensa-haardtring.html"
10
+
11
+ fares = {}
12
+ fares[:stadtmitte] = [
13
+ Fare.new("Gemüse- Maultaschen mit Tomatensauce", :meatless, 180),
14
+ Fare.new("Linsen-Orangensuppe mit Baguette", :meatless, 210),
15
+ Fare.new("Rühreier mit Rahmspinat", :meatless, 120),
16
+ Fare.new("Wokpfanne \"Hot-Asia-Style\" mit Hähnchenbrust und Mie-Nudeln", :poultry, 280),
17
+ Fare.new("Hähnchen-Nuggets-im Knuspermantel dazu ein Kräuterdip", :poultry, 190),
18
+ Fare.new("Grillbraten vom Schweinenacken mit Thymianjus", :pork, 200)
19
+ ]
20
+ fares[:lichtwiese] = [
21
+ Fare.new("Vier Kartoffelpuffer mit Apfelmus und eine Beilage nach Wahl", :meatless, 210),
22
+ Fare.new("Balkanröllchen mit Knoblauchdip dazu Pommes frites und eine Beilage nach Wahl", :pork, 250),
23
+ Fare.new("Hähnchenbrust Hawaii mit Ananas und Käse überbacken und Pommes frites dazu eine Beilage nach Wahl", :poultry, 290),
24
+ Fare.new("Schweineschnitzel mit Champignonrahmsauce dazu Pommes frites und eine Beilage nach Wahl", :pork, 350),
25
+ Fare.new("Seelachsfilet paniert mit Sauce Tatar", :fish, 150),
26
+ Fare.new("Pasta Bolognese", :beef, 160),
27
+ Fare.new("Wokpfanne \"Thai 7 Spice\" mit Putenbrust und Basmatireis", :poultry, 280),
28
+ Fare.new("Country Kartoffeln mit Quark", :meatless, 110)
29
+ ]
30
+ fares[:haardtring] = []
31
+
32
+ date = {}
33
+ date[:stadtmitte] = Time.local(2009, 5, 28)
34
+ date[:lichtwiese] = Time.local(2009, 5, 29)
35
+ date[:haardtring] = Time.local(2009, 6, 1)
36
+
37
+ name = {}
38
+ name[:stadtmitte] = "Mensa Stadtmitte"
39
+ name[:lichtwiese] = "Mensa Lichtwiese"
40
+ name[:haardtring] = "Mensa Haardtring"
41
+
42
+ [:stadtmitte, :lichtwiese, :haardtring].each do |canteen|
43
+ describe "#{canteen.to_s.capitalize} am #{date[canteen].strftime('%d.%m.%Y')} Canteen" do
44
+ subject { Canteen.new(uri[canteen]) }
45
+
46
+ it { should have(fares[canteen].length).fares }
47
+
48
+ it "should equal an other canteen with the same name, date and state" do
49
+ other = Canteen.new(uri[canteen])
50
+ subject.should == other
51
+ end
52
+
53
+ if fares[canteen].empty?
54
+ it { should be_closed }
55
+ else
56
+ it { should be_opened }
57
+ end
58
+
59
+ describe "#date" do
60
+ it "should be #{date[canteen]}" do
61
+ subject.date.should == date[canteen]
62
+ end
63
+ end
64
+
65
+ describe "#name" do
66
+ it "should be \"Mensa Stadtmitte\"" do
67
+ subject.name.should == name[canteen]
68
+ end
69
+ end
70
+
71
+ describe "#fares" do
72
+ fares[canteen].each do |fare|
73
+ it "should return an Array which includes #{fare.inspect}" do
74
+ subject.fares.should be_an_instance_of(Array)
75
+ subject.fares.should include(fare)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,78 @@
1
+ # coding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '../spec_helper')
4
+
5
+ module Mensa
6
+ describe Client do
7
+ user_config_file = File.join(ENV['HOME'], ".mensarc")
8
+ global_config_file = File.join(MENSA_DIR, 'config/canteens.rb')
9
+
10
+ canteens = {}
11
+ canteens[:stadtmitte] = Canteen.new(
12
+ 'http://www.studentenwerkdarmstadt.de/essen/mensa-stadtmitte.html'
13
+ )
14
+ canteens[:haardtring] = Canteen.new(
15
+ 'http://www.studentenwerkdarmstadt.de/essen/mensa-haardtring.html'
16
+ )
17
+
18
+ subject { Client.new(StringIO.new) }
19
+
20
+ describe 'with global settings (config/canteens.rb)' do
21
+
22
+ before do
23
+ File.should_receive(:exist?).with(user_config_file).ordered.and_return(false)
24
+ File.should_receive(:exist?).with(global_config_file).ordered.and_return(true)
25
+ end
26
+
27
+ it { should have(5).canteens }
28
+
29
+ end
30
+
31
+ describe 'with user specific settings (replaced with fixtures/canteens.rb)' do
32
+ config = File.read(File.join(MENSA_DIR, 'fixtures/canteens.rb'))
33
+
34
+ before do
35
+ File.should_receive(:exist?).with(user_config_file).and_return(true)
36
+ File.should_receive(:read).with(user_config_file).and_return(config)
37
+ end
38
+
39
+ it { should have(3).canteens }
40
+
41
+ it 'should have "Mensa Stadtmitte" as first canteen' do
42
+ subject.canteens[0].should == canteens[:stadtmitte]
43
+ end
44
+
45
+ it 'should have "Mensa Haardtring" as second cateen' do
46
+ subject.canteens[1].should == canteens[:haardtring]
47
+ end
48
+ end
49
+
50
+ describe "output with user specific settings" do
51
+ stdout = StringIO.new
52
+ output = File.read(File.join(MENSA_DIR, 'fixtures/output.txt'))
53
+ lines = nil
54
+
55
+ config = File.read(File.join(MENSA_DIR, 'fixtures/canteens.rb'))
56
+
57
+ before :all do
58
+ File.should_receive(:exist?).with(user_config_file).and_return(true)
59
+ File.should_receive(:read).with(user_config_file).and_return(config)
60
+
61
+ subject.run
62
+ lines = stdout.string.lines.to_a
63
+ end
64
+
65
+ subject { Client.new(stdout) }
66
+
67
+ output.lines.each_with_index do |expected_line, index|
68
+ it "#{index + 1}. line should be #{expected_line.inspect.gsub(' ', '⎵')}" do
69
+ if index < lines.length
70
+ lines[index].should == expected_line
71
+ else
72
+ fail "line missing"
73
+ end
74
+ end
75
+ end # do
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,31 @@
1
+ # coding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '../spec_helper')
4
+
5
+ module Mensa
6
+ describe Document do
7
+ subject { Document.new('http://www.studentenwerkdarmstadt.de/essen/mensa-stadtmitte.html') }
8
+
9
+ describe '.new("http://www.studentenwerkdarmstadt.de/essen/mensa-stadtmitte.html")' do
10
+ it 'should have the URI "http://www.studentenwerkdarmstadt.de/essen/mensa-stadtmitte.html"' do
11
+ subject.uri.should be_kind_of(URI)
12
+ subject.uri.to_s.should == 'http://www.studentenwerkdarmstadt.de/essen/mensa-stadtmitte.html'
13
+ end
14
+ end
15
+
16
+ describe '#to_s' do
17
+ it "should equal fixture 'mensa-stadtmitte.html'" do
18
+ subject.to_s.should == fixture('mensa-stadtmitte.html')
19
+ end
20
+
21
+ it "should fetch the document only once" do
22
+ Net::HTTP.should_receive(:get)
23
+ 5.times { subject.to_s }
24
+ end
25
+
26
+ it "should return a String" do
27
+ subject.to_s.should be_an_instance_of(String)
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,114 @@
1
+ # coding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), '../spec_helper')
4
+
5
+ module Mensa
6
+ describe Fare do
7
+ name, type, price = ['Rührei mit Spinat', 'FL', "1,80"]
8
+
9
+ subject { Fare.new(name, type, price) }
10
+
11
+ it "should be equal to another fare wich has the same name, price, and type" do
12
+ subject.should == Fare.new(name, type, price)
13
+ end
14
+
15
+ describe ".new('Rührei mit Spinat', 'FL', 180)" do
16
+ specify "#name should equal 'Rührei mit Spinat'" do
17
+ subject.name.should == 'Rührei mit Spinat'
18
+ end
19
+
20
+ specify "#type should equal :meatless" do
21
+ subject.type.should == :meatless
22
+ end
23
+
24
+ specify "#price should equal 180" do
25
+ subject.price.should == 180
26
+ end
27
+ end
28
+
29
+ describe "#type" do
30
+ it 'should raise an TypeUnknownError with :invalid as argument' do
31
+ lambda { subject.type = :invalid }.should raise_error(TypeUnknownError)
32
+ end
33
+
34
+ types = {
35
+ 'S' => :pork,
36
+ 'R' => :beef,
37
+ 'K' => :veal,
38
+ 'L' => :lamb,
39
+ 'G' => :poultry,
40
+ 'F' => :fish,
41
+ 'FL' => :meatless,
42
+ nil => nil
43
+ }
44
+
45
+ types.values.each do |type|
46
+ it "should recognise #{type.inspect} as valid type" do
47
+ lambda { subject.type = type }.should_not raise_error(TypeUnknownError)
48
+ end
49
+ end
50
+
51
+ types.keys.each do |type|
52
+ it "should recognise #{type.inspect} as valid type" do
53
+ lambda { subject.type = type }.should_not raise_error(TypeUnknownError)
54
+ subject.type.should == types[type]
55
+ end
56
+ end
57
+ end
58
+
59
+ describe "#name" do
60
+ it "should be an instance of String" do
61
+ subject.name.should be_an_instance_of(String)
62
+ end
63
+
64
+ it "should accept objects which respond to #to_s" do
65
+ fare_name = "Rührei mit Spinat"
66
+ fare = mock('fare', :to_s => fare_name)
67
+
68
+ subject.name = fare
69
+ subject.name.should == fare_name
70
+ end
71
+
72
+ it "should strip the name" do
73
+ subject.name = " #{name} "
74
+ subject.name.should == name
75
+ end
76
+
77
+ it "should be less than 255 characters" do
78
+ lambda { subject.name = 'a' * 300 }.should raise_error(NameTooLongError)
79
+ end
80
+
81
+ it "should not accept an empty string or nil as argument" do
82
+ lambda { subject.name = '' }.should raise_error(NameEmptyError)
83
+ lambda { subject.name = nil }.should raise_error(NameEmptyError)
84
+ end
85
+ end
86
+
87
+ describe "#price" do
88
+ prices = {
89
+ 230 => 230,
90
+ 2.30 => 230,
91
+ 2.99 => 299,
92
+ 2.999 => 299,
93
+ '2.30' => 230,
94
+ '2,30' => 230,
95
+ '' => nil,
96
+ nil => nil,
97
+ 'str' => PriceStringInvalid
98
+ }
99
+
100
+ prices.each do |given, expected|
101
+ if expected.instance_of?(Class)
102
+ it "should recognise #{given.inspect} as invalid and raise a #{expected}" do
103
+ lambda { subject.price = given }.should raise_error(expected)
104
+ end
105
+ else
106
+ it "should recognise #{given.inspect} as #{expected.inspect} Euro cents" do
107
+ subject.price = given
108
+ subject.price.should == expected
109
+ end
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,7 @@
1
+ # coding: utf-8
2
+
3
+ require File.join(File.dirname(__FILE__), 'spec_helper')
4
+
5
+ describe Mensa do
6
+
7
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+
3
+ require 'spec'
4
+ require 'fakeweb'
5
+
6
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
7
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
8
+
9
+ require 'mensa'
10
+
11
+ PROJECT_DIR = File.join(File.dirname(__FILE__), '..')
12
+
13
+ #
14
+ # Helper methods
15
+ def fixture(filename)
16
+ string = Iconv.iconv('UTF-8', 'ISO-8859-1', File.read(File.join(PROJECT_DIR, 'fixtures', filename))).first
17
+ string.sub(/[Ii][Ss][Oo]-8859-1/, 'utf-8')
18
+ end
19
+
20
+ ##
21
+ # FakeWeb setup
22
+ FakeWeb.allow_net_connect = false
23
+
24
+ Dir.chdir(File.join(PROJECT_DIR, 'fixtures')) do
25
+ Dir.glob('*.html').each do |file|
26
+ FakeWeb.register_uri(
27
+ :get,
28
+ "http://www.studentenwerkdarmstadt.de/essen/#{file}",
29
+ :string => File.read(file)
30
+ )
31
+ end
32
+ end
33
+
34
+ ##
35
+ # Spec::Runner configuration
36
+ Spec::Runner.configure do |config|
37
+
38
+ end
39
+
metadata ADDED
@@ -0,0 +1,95 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: t6d-mensa
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Konstantin Tennhard
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-06-02 00:00:00 -07:00
13
+ default_executable: mensa
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: nokogiri
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 1.2.3
24
+ version:
25
+ description:
26
+ email: development@tennhard.net
27
+ executables:
28
+ - mensa
29
+ extensions: []
30
+
31
+ extra_rdoc_files:
32
+ - LICENSE
33
+ - README.rdoc
34
+ files:
35
+ - .document
36
+ - .gitignore
37
+ - LICENSE
38
+ - README.rdoc
39
+ - Rakefile
40
+ - VERSION
41
+ - bin/mensa
42
+ - config/canteens.rb
43
+ - fixtures/canteens.rb
44
+ - fixtures/mensa-dieburg.html
45
+ - fixtures/mensa-haardtring.html
46
+ - fixtures/mensa-lichtwiese.html
47
+ - fixtures/mensa-schofferstrase.html
48
+ - fixtures/mensa-stadtmitte.html
49
+ - fixtures/output.txt
50
+ - lib/mensa.rb
51
+ - lib/mensa/canteen.rb
52
+ - lib/mensa/client.rb
53
+ - lib/mensa/document.rb
54
+ - lib/mensa/exceptions.rb
55
+ - lib/mensa/fare.rb
56
+ - mensa.gemspec
57
+ - spec/mensa/canteen_spec.rb
58
+ - spec/mensa/client_spec.rb
59
+ - spec/mensa/document_spec.rb
60
+ - spec/mensa/fare_spec.rb
61
+ - spec/mensa_spec.rb
62
+ - spec/spec_helper.rb
63
+ has_rdoc: false
64
+ homepage: http://github.com/t6d/mensa
65
+ post_install_message:
66
+ rdoc_options:
67
+ - --charset=UTF-8
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: "0"
75
+ version:
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ version: "0"
81
+ version:
82
+ requirements: []
83
+
84
+ rubyforge_project:
85
+ rubygems_version: 1.2.0
86
+ signing_key:
87
+ specification_version: 3
88
+ summary: Fetches the daily bill of fare from the canteen page of Studentenwerk Darmstadt.
89
+ test_files:
90
+ - spec/mensa/canteen_spec.rb
91
+ - spec/mensa/client_spec.rb
92
+ - spec/mensa/document_spec.rb
93
+ - spec/mensa/fare_spec.rb
94
+ - spec/mensa_spec.rb
95
+ - spec/spec_helper.rb