wasserstand 0.0.4 → 0.0.5

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/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