wasserstand 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/lib/wasserstand.rb CHANGED
@@ -7,10 +7,14 @@ require_rel 'wasserstand'
7
7
 
8
8
  module Wasserstand
9
9
  class << self
10
- attr_writer :provider
10
+ attr_writer :waterway_provider, :level_provider
11
11
 
12
- def provider
13
- @provider ||= Provider::PegelOnline.new
12
+ def waterway_provider
13
+ @waterway_provider ||= PegelOnline::WaterwayProvider.new
14
+ end
15
+
16
+ def level_provider
17
+ @level_provider ||= PegelOnline::LevelProvider.new
14
18
  end
15
19
  end
16
20
  end
@@ -3,16 +3,39 @@ module Wasserstand
3
3
  # see http://www.pegelonline.wsv.de/gast/hilfe#hilfe_pegelparameter
4
4
  #
5
5
  class Level # Pegel
6
- attr_reader :id
7
- attr_accessor :name, :km, :measurements
6
+ class << self
7
+ def [](name)
8
+ Wasserstand.level_provider[name]
9
+ end
8
10
 
9
- def initialize(id)
10
- @id = id
11
+ def all
12
+ Wasserstand.level_provider.all
13
+ end
14
+
15
+ def find_by_name(regex)
16
+ Wasserstand.level_provider.find_by_name(regex)
17
+ end
18
+ end
19
+
20
+ attr_reader :name
21
+ attr_accessor :level_id, :km, :measurements
22
+ attr_writer :waterway
23
+
24
+ def initialize(name)
25
+ @name = name
11
26
  @measurements = []
12
27
  end
13
28
 
29
+ def waterway
30
+ if @waterway.respond_to?(:name)
31
+ @waterway
32
+ else
33
+ @waterway = Waterway[@waterway]
34
+ end
35
+ end
36
+
14
37
  def to_s
15
- "#{@name} (km #{@km}): #{@measurements.last}"
38
+ name
16
39
  end
17
40
  end
18
41
  end
@@ -0,0 +1,41 @@
1
+ module Wasserstand
2
+ module PegelOnline
3
+ =begin
4
+ <item>
5
+ <no>8</no>
6
+ <psmgr>320</psmgr>
7
+ <pegelname>KONSTANZ</pegelname>
8
+ <messwert>380,7</messwert>
9
+ <km>0</km>
10
+ <pnp>391,89</pnp>
11
+ <tendenz>Gleich</tendenz>
12
+ <datum>13.09.2012</datum>
13
+ <uhrzeit>20:00:00</uhrzeit>
14
+ <pegelnummer>0906</pegelnummer>
15
+ </item>
16
+ =end
17
+ class LevelMapper
18
+ class << self
19
+ def map(item)
20
+ level_name = item.xpath('pegelname').text
21
+
22
+ Level.new(level_name).tap do |pegel|
23
+ # The level class will resolve the name to a real object if required
24
+ pegel.waterway = item.xpath('../name').text
25
+
26
+ pegel.level_id = item.xpath('pegelnummer').text
27
+ pegel.km = item.xpath('km').text.sub(',', '.').to_f
28
+ datum = item.xpath('datum').text
29
+ uhrzeit = item.xpath('uhrzeit').text
30
+
31
+ messdatum = TZInfo::Timezone.get('Europe/Berlin').local_to_utc(Time.parse("#{datum} #{uhrzeit}"))
32
+ wert = item.xpath('messwert').text.sub(',', '.').to_f
33
+ tendenz = item.xpath('tendenz').text.downcase.to_sym
34
+
35
+ pegel.measurements << Measurement.new(messdatum, wert, tendenz)
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -1,23 +1,81 @@
1
1
  module Wasserstand
2
- module Provider
3
- class PegelOnline
2
+ module PegelOnline
3
+ class Provider
4
4
  def initialize(url = 'http://www.pegelonline.wsv.de/svgz/pegelstaende_neu.xml')
5
5
  @url = url
6
6
  end
7
7
 
8
8
  def [](name)
9
- doc = Nokogiri::HTML(open(@url).read)
10
- results = doc.xpath("//data/table/gewaesser[name/text() = '#{name.upcase}']")
9
+ doc = Nokogiri::HTML(fetch(@url), nil, 'ISO-8859-1')
10
+ results = doc.xpath(xpath_lookup(name))
11
11
 
12
12
  case results.size
13
13
  when 0
14
- return []
14
+ return nil # loookup returns nil if not found. This is a lookup, not find_all.
15
15
  when 1
16
- return Mapper.map(results.first)
16
+ return mapper.map(results.first)
17
17
  else
18
- raise "Found #{results.size} results for #{name}."
18
+ raise "Name is not unique. Found #{results.size} results for #{name}."
19
19
  end
20
20
  end
21
+
22
+ def all
23
+ Nokogiri::HTML(fetch(@url), nil, 'ISO-8859-1').xpath(xpath_all).map{|o| mapper.map(o)}
24
+ end
25
+
26
+ def find_by_name(name_expression)
27
+ # Not the best performing way, but it gives us the ability to use the XPath 2.0 'matches' function
28
+ # which isn't supported in Nokogiri (yet).
29
+ Nokogiri::HTML(fetch(@url), nil, 'ISO-8859-1').xpath(xpath_finder(name_expression), Class.new{
30
+ def matches(node_set, regex)
31
+ node_set.find_all do |node|
32
+ node.to_s.match(%r{#{regex}})
33
+ end
34
+ end
35
+ }.new).map{|o| mapper.map(o)}
36
+ end
37
+
38
+ protected
39
+
40
+ def xpath_lookup(name)
41
+ "#{xpath_all}[#{name_attribute}/text() = '#{name.upcase}']"
42
+ end
43
+
44
+ def xpath_finder(regex)
45
+ "#{xpath_all}[matches(#{name_attribute}/text(), '#{regex}')]"
46
+ end
47
+
48
+ def fetch(url)
49
+ open(url).read
50
+ end
51
+
52
+ def name_attribute
53
+ 'name'
54
+ end
55
+ end
56
+
57
+ class WaterwayProvider < Provider
58
+ def xpath_all
59
+ "//data/table/gewaesser"
60
+ end
61
+
62
+ def mapper
63
+ Wasserstand::PegelOnline::WaterwayMapper
64
+ end
65
+ end
66
+
67
+ class LevelProvider < Provider
68
+ def xpath_all
69
+ "//data/table/gewaesser/item"
70
+ end
71
+
72
+ def mapper
73
+ Wasserstand::PegelOnline::LevelMapper
74
+ end
75
+
76
+ def name_attribute
77
+ 'pegelname'
78
+ end
21
79
  end
22
80
  end
23
81
  end
@@ -0,0 +1,34 @@
1
+ module Wasserstand
2
+ module PegelOnline
3
+ =begin
4
+ <gewaesser>
5
+ <name>BODENSEE</name>
6
+ <item>
7
+ <no>8</no>
8
+ <psmgr>320</psmgr>
9
+ <pegelname>KONSTANZ</pegelname>
10
+ <messwert>380,7</messwert>
11
+ <km>0</km>
12
+ <pnp>391,89</pnp>
13
+ <tendenz>Gleich</tendenz>
14
+ <datum>13.09.2012</datum>
15
+ <uhrzeit>20:00:00</uhrzeit>
16
+ <pegelnummer>0906</pegelnummer>
17
+ </item>
18
+ </gewaesser>
19
+ =end
20
+ class WaterwayMapper
21
+ class << self
22
+ def map(node)
23
+ Waterway.new(node.xpath('name').text).tap do |ww|
24
+ node.xpath('item').each do |item|
25
+ level = LevelMapper.map(item)
26
+ level.waterway = ww
27
+ ww.levels[level.name] = level
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -1,3 +1,3 @@
1
1
  module Wasserstand
2
- VERSION = "0.0.4"
2
+ VERSION = "0.0.5"
3
3
  end
@@ -1,8 +1,27 @@
1
1
  module Wasserstand
2
+ # http://stackoverflow.com/questions/2030336/how-do-i-create-a-hash-in-ruby-that-compares-strings-ignoring-case
3
+ class HashClod < Hash
4
+ def [](key)
5
+ key.respond_to?(:upcase) ? super(key.upcase) : super(key)
6
+ end
7
+
8
+ def []=(key, value)
9
+ key.respond_to?(:upcase) ? super(key.encode.upcase, value) : super(key, value)
10
+ end
11
+ end
12
+
2
13
  class Waterway
3
14
  class << self
4
15
  def [](name)
5
- Wasserstand.provider[name]
16
+ Wasserstand.waterway_provider[name]
17
+ end
18
+
19
+ def all
20
+ Wasserstand.waterway_provider.all
21
+ end
22
+
23
+ def find_by_name(regex)
24
+ Wasserstand.waterway_provider.find_by_name(regex)
6
25
  end
7
26
  end
8
27
 
@@ -10,7 +29,11 @@ module Wasserstand
10
29
 
11
30
  def initialize(name)
12
31
  @name = name
13
- @levels = {}
32
+ @levels = HashClod.new
33
+ end
34
+
35
+ def to_s
36
+ name
14
37
  end
15
38
  end
16
39
  end
data/test/helper.rb CHANGED
@@ -1,2 +1,11 @@
1
1
  require 'minitest/autorun'
2
2
  require 'wasserstand'
3
+
4
+ class WasserstandTestCase < MiniTest::Unit::TestCase
5
+ include Wasserstand
6
+
7
+ def setup
8
+ url = File.join(File.dirname(__FILE__), 'fixtures', 'pegelstaende_neu.xml')
9
+ Wasserstand.waterway_provider = PegelOnline::WaterwayProvider.new(url)
10
+ end
11
+ end
@@ -0,0 +1,37 @@
1
+ require 'helper'
2
+
3
+ class TestLevel < WasserstandTestCase
4
+ def test_size
5
+ assert_equal(534, Level.all.size)
6
+ end
7
+
8
+ def test_kms
9
+ elbe_levels = Waterway['ELBE'].levels
10
+ assert_equal(60, elbe_levels.size)
11
+
12
+ assert_level({:name => 'PIRNA', :km => 34.67, :measurements_size => 1}, elbe_levels['PIRNA'])
13
+ end
14
+
15
+ def test_finder
16
+ assert_equal(['HEIDELBERG UW', 'HAVELBERG EP', 'HAVELBERG STADT', 'HAVELBERG UP'], Level.find_by_name('ELBERG').map{|w| w.name})
17
+ assert_equal(21, Level.find_by_name('^E').size)
18
+ end
19
+
20
+ def test_lookup
21
+ pirna = Level['Pirna']
22
+ assert(pirna)
23
+ assert(pirna.waterway)
24
+ assert_equal('ELBE', pirna.waterway.name)
25
+
26
+ assert(Level['GENTHIN'])
27
+ end
28
+
29
+ private
30
+
31
+ def assert_level(values, level)
32
+ assert(level)
33
+ assert_equal(values[:name], level.name)
34
+ assert_equal(values[:km], level.km)
35
+ assert_equal(values[:measurements_size], level.measurements.size)
36
+ end
37
+ end
@@ -0,0 +1,11 @@
1
+ require 'helper'
2
+
3
+ class TestMeasurement < WasserstandTestCase
4
+ def test_single
5
+ konstanz_measurements = Waterway['BODENSEE'].levels['KONSTANZ'].measurements
6
+
7
+ assert_equal(Time.parse('2012-09-13 18:00:00 UTC'), konstanz_measurements.last.date)
8
+ assert_equal(380.7, konstanz_measurements.last.value)
9
+ assert_equal(:gleich, konstanz_measurements.last.trend)
10
+ end
11
+ end
@@ -0,0 +1,21 @@
1
+ require 'helper'
2
+
3
+ class TestWaterway < WasserstandTestCase
4
+ def test_finder
5
+ assert_equal(['ELBE', 'ELBE-HAVEL-KANAL', 'ELBESEITENKANAL'], Waterway.find_by_name('ELB.*').map{|w| w.name})
6
+ assert_equal(7, Waterway.find_by_name('^E').size)
7
+ end
8
+
9
+ def test_size
10
+ assert_equal(77, Waterway.all.size)
11
+ end
12
+
13
+ def test_levels
14
+ assert_equal(1, Waterway['BODENSEE'].levels.size)
15
+ end
16
+
17
+ def test_lookup
18
+ assert(Waterway['Elbe'].levels['Pirna'])
19
+ assert(Waterway['ELBE-HAVEL-KANAL'].levels['GENTHIN'])
20
+ end
21
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: wasserstand
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-09-15 00:00:00.000000000 Z
12
+ date: 2012-09-16 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: require_all
@@ -108,14 +108,17 @@ files:
108
108
  - bin/wasserstand
109
109
  - lib/wasserstand.rb
110
110
  - lib/wasserstand/level.rb
111
- - lib/wasserstand/mapper.rb
112
111
  - lib/wasserstand/measurement.rb
112
+ - lib/wasserstand/provider/level_mapper.rb
113
113
  - lib/wasserstand/provider/pegel-online.rb
114
+ - lib/wasserstand/provider/waterway_mapper.rb
114
115
  - lib/wasserstand/version.rb
115
116
  - lib/wasserstand/waterway.rb
116
117
  - test/fixtures/pegelstaende_neu.xml
117
118
  - test/helper.rb
118
- - test/unit/test_wasserstand.rb
119
+ - test/unit/test_level.rb
120
+ - test/unit/test_measurement.rb
121
+ - test/unit/test_waterway.rb
119
122
  - wasserstand.gemspec
120
123
  homepage: http://github.com/nerab/wasserstand
121
124
  licenses: []
@@ -144,4 +147,6 @@ summary: Wasserstand auf deutschen Flüssen
144
147
  test_files:
145
148
  - test/fixtures/pegelstaende_neu.xml
146
149
  - test/helper.rb
147
- - test/unit/test_wasserstand.rb
150
+ - test/unit/test_level.rb
151
+ - test/unit/test_measurement.rb
152
+ - test/unit/test_waterway.rb
@@ -1,43 +0,0 @@
1
- module Wasserstand
2
- =begin
3
- <gewaesser>
4
- <name>BODENSEE</name>
5
- <item>
6
- <no>8</no>
7
- <psmgr>320</psmgr>
8
- <pegelname>KONSTANZ</pegelname>
9
- <messwert>380,7</messwert>
10
- <km>0</km>
11
- <pnp>391,89</pnp>
12
- <tendenz>Gleich</tendenz>
13
- <datum>13.09.2012</datum>
14
- <uhrzeit>20:00:00</uhrzeit>
15
- <pegelnummer>0906</pegelnummer>
16
- </item>
17
- </gewaesser>
18
- =end
19
- class Mapper
20
- class << self
21
- def map(node)
22
- Waterway.new(node.xpath('name').text) .tap do |ww|
23
- node.xpath('item').each do |item|
24
- pegel_name = item.xpath('pegelname').text
25
- ww.levels[pegel_name] = Level.new(item.xpath('pegelnummer').text).tap do |pegel|
26
- pegel.name = pegel_name
27
- pegel.km = item.xpath('km').text
28
-
29
- datum = item.xpath('datum').text
30
- uhrzeit = item.xpath('uhrzeit').text
31
-
32
- messdatum = TZInfo::Timezone.get('Europe/Berlin').local_to_utc(Time.parse("#{datum} #{uhrzeit}"))
33
- wert = item.xpath('messwert').text.sub(',', '.').to_f
34
- tendenz = item.xpath('tendenz').text.downcase.to_sym
35
-
36
- pegel.measurements << Measurement.new(messdatum, wert, tendenz)
37
- end
38
- end
39
- end
40
- end
41
- end
42
- end
43
- end
@@ -1,31 +0,0 @@
1
- require 'helper'
2
-
3
- class TestWasserstand < MiniTest::Unit::TestCase
4
- include Wasserstand
5
-
6
- def setup
7
- url = File.join(File.dirname(__FILE__), '..', 'fixtures', 'pegelstaende_neu.xml')
8
- Wasserstand.provider = Provider::PegelOnline.new(url)
9
- end
10
-
11
- def test_plain
12
- assert Waterway['BODENSEE']
13
- end
14
-
15
- def test_levels
16
- assert_equal(1, Waterway['BODENSEE'].levels.size)
17
- end
18
-
19
- def test_level_lookup
20
- assert(Waterway['BODENSEE'].levels['KONSTANZ'])
21
- assert(Waterway['ELBE-HAVEL-KANAL'].levels['GENTHIN'])
22
- end
23
-
24
- def test_single_measurement
25
- konstanz_measurements = Waterway['BODENSEE'].levels['KONSTANZ'].measurements
26
-
27
- assert_equal(Time.parse('2012-09-13 18:00:00 UTC'), konstanz_measurements.last.date)
28
- assert_equal(380.7, konstanz_measurements.last.value)
29
- assert_equal(:gleich, konstanz_measurements.last.trend)
30
- end
31
- end