weather_data 0.0.1
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 +7 -0
- data/.gitignore +20 -0
- data/.travis.yml +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +31 -0
- data/Rakefile +4 -0
- data/lib/weather_data.rb +8 -0
- data/lib/weather_data/humidity.rb +18 -0
- data/lib/weather_data/humidity/relative.rb +23 -0
- data/lib/weather_data/temperature.rb +55 -0
- data/lib/weather_data/temperature/base.rb +69 -0
- data/lib/weather_data/temperature/celsius.rb +30 -0
- data/lib/weather_data/temperature/fahrenheit.rb +30 -0
- data/lib/weather_data/temperature/kelvin.rb +36 -0
- data/lib/weather_data/version.rb +5 -0
- data/lib/weather_data/weather.rb +105 -0
- data/lib/weather_data/wind.rb +99 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/weather_data/humidity_spec.rb +32 -0
- data/spec/weather_data/temperature/base_spec.rb +85 -0
- data/spec/weather_data/temperature/celsius_spec.rb +23 -0
- data/spec/weather_data/temperature/fahrenheit_spec.rb +23 -0
- data/spec/weather_data/temperature/kelvin_spec.rb +23 -0
- data/spec/weather_data/temperature_spec.rb +60 -0
- data/spec/weather_data/version_spec.rb +9 -0
- data/spec/weather_data/weather_spec.rb +57 -0
- data/spec/weather_data/wind_spec.rb +19 -0
- data/weather_data.gemspec +22 -0
- metadata +109 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 1cd482e05d0bd9499c8e5384e655717cb70b434f
|
4
|
+
data.tar.gz: fe495d208cba3e096ecf7025b70c629374d2820f
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4a30928855e482408e32d32b8ef5dbbfec633a38564a587baafe4d0bd5b03dc0c0d46208c80a5eb5fa710fe4dc4e688b266234481bc56f5b5db27ebe0d395f22
|
7
|
+
data.tar.gz: 0261711b5709412fe77c1ba769504e2c6283287bf12d5c1e0e8688784dc59412cb88774e0bfd99592a6c8b888a1407dcc214b29068941baac0ab322e9737b86e
|
data/.gitignore
ADDED
data/.travis.yml
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Andrew Slotin
|
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,31 @@
|
|
1
|
+
[](https://travis-ci.org/andrewslotin/weather_data)
|
2
|
+
|
3
|
+
# WeatherData
|
4
|
+
|
5
|
+
TODO: Write a gem description
|
6
|
+
|
7
|
+
## Installation
|
8
|
+
|
9
|
+
Add this line to your application's Gemfile:
|
10
|
+
|
11
|
+
gem 'weather_data'
|
12
|
+
|
13
|
+
And then execute:
|
14
|
+
|
15
|
+
$ bundle
|
16
|
+
|
17
|
+
Or install it yourself as:
|
18
|
+
|
19
|
+
$ gem install weather_data
|
20
|
+
|
21
|
+
## Usage
|
22
|
+
|
23
|
+
TODO: Write usage instructions here
|
24
|
+
|
25
|
+
## Contributing
|
26
|
+
|
27
|
+
1. Fork it
|
28
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
29
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
30
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
31
|
+
5. Create new Pull Request
|
data/Rakefile
ADDED
data/lib/weather_data.rb
ADDED
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require "weather_data/humidity/relative"
|
4
|
+
|
5
|
+
module WeatherData
|
6
|
+
module Humidity
|
7
|
+
def self.parse(s)
|
8
|
+
_, points, units = /\b(\d+(?:\.\d+)?)%/.match(s).to_a
|
9
|
+
|
10
|
+
raise ArgumentError.new(%Q{"#{s.inspect}" doesn't contain humidity}) unless points
|
11
|
+
|
12
|
+
percents = points.to_f
|
13
|
+
raise ArgumentError.new("Invalid value for relative humidity: #{percents.inspect}") unless percents > 0 && percents <= 100
|
14
|
+
|
15
|
+
Relative.new(percents)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module WeatherData
|
6
|
+
module Humidity
|
7
|
+
class Relative < ::SimpleDelegator
|
8
|
+
def initialize(points)
|
9
|
+
super points.to_f
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_s
|
13
|
+
"#{percentage}%"
|
14
|
+
end
|
15
|
+
|
16
|
+
protected
|
17
|
+
|
18
|
+
def percentage
|
19
|
+
__getobj__
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require "weather_data/temperature/base"
|
4
|
+
require "weather_data/temperature/celsius"
|
5
|
+
require "weather_data/temperature/fahrenheit"
|
6
|
+
require "weather_data/temperature/kelvin"
|
7
|
+
|
8
|
+
module WeatherData
|
9
|
+
module Temperature
|
10
|
+
extend self
|
11
|
+
|
12
|
+
CELSIUS = :C
|
13
|
+
FAHRENHEIT = :F
|
14
|
+
KELVIN = :K
|
15
|
+
|
16
|
+
def parse(s)
|
17
|
+
degrees, units = s.match(/([+-]?\d+(?:\.\d+)?)\s*(°?#{CELSIUS}|°?#{FAHRENHEIT}|#{KELVIN})\b/u).to_a[1..2]
|
18
|
+
|
19
|
+
raise ArgumentError.new(%Q{"#{s}" doesn't contain temperature}) unless degrees && units
|
20
|
+
|
21
|
+
units_class(units).new(degrees.to_f)
|
22
|
+
end
|
23
|
+
|
24
|
+
module HelperMethods
|
25
|
+
def celsius
|
26
|
+
Celsius.new(self)
|
27
|
+
end
|
28
|
+
|
29
|
+
def fahrenheit
|
30
|
+
Fahrenheit.new(self)
|
31
|
+
end
|
32
|
+
|
33
|
+
def kelvin
|
34
|
+
Kelvin.new(self)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
def units_class(units)
|
41
|
+
case units.gsub(/\A°/, '').to_sym
|
42
|
+
when CELSIUS
|
43
|
+
Celsius
|
44
|
+
when FAHRENHEIT
|
45
|
+
Fahrenheit
|
46
|
+
when KELVIN
|
47
|
+
Kelvin
|
48
|
+
else
|
49
|
+
raise ArgumentError.new(units)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
[Integer, Float].each { |klass| klass.send :include, WeatherData::Temperature::HelperMethods }
|
@@ -0,0 +1,69 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module WeatherData
|
4
|
+
module Temperature
|
5
|
+
class Base
|
6
|
+
include Comparable
|
7
|
+
|
8
|
+
attr_accessor :degrees
|
9
|
+
|
10
|
+
def initialize(deg)
|
11
|
+
@degrees = if deg.is_a? Numeric
|
12
|
+
deg
|
13
|
+
else
|
14
|
+
to_self(deg).degrees
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def ==(value)
|
19
|
+
if value.is_a? Numeric
|
20
|
+
degrees == value
|
21
|
+
else
|
22
|
+
super
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def degrees
|
27
|
+
@degrees.to_f
|
28
|
+
end
|
29
|
+
|
30
|
+
def -@
|
31
|
+
self.class.new(-degrees)
|
32
|
+
end
|
33
|
+
|
34
|
+
def <=>(value)
|
35
|
+
degrees <=> to_self(value).degrees
|
36
|
+
end
|
37
|
+
|
38
|
+
def +(value)
|
39
|
+
self.class.new(degrees + to_self(value).degrees)
|
40
|
+
end
|
41
|
+
|
42
|
+
def -(value)
|
43
|
+
-(-self + value)
|
44
|
+
end
|
45
|
+
|
46
|
+
def *(value)
|
47
|
+
self.class.new(value * degrees)
|
48
|
+
end
|
49
|
+
|
50
|
+
def /(value)
|
51
|
+
self.class.new(value * (1 / degrees))
|
52
|
+
end
|
53
|
+
|
54
|
+
def to_i
|
55
|
+
degrees.round
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_f
|
59
|
+
degrees
|
60
|
+
end
|
61
|
+
|
62
|
+
private
|
63
|
+
|
64
|
+
def to_self(value)
|
65
|
+
value
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module WeatherData
|
4
|
+
module Temperature
|
5
|
+
class Celsius < Base
|
6
|
+
def to_celsius
|
7
|
+
self
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_fahrenheit
|
11
|
+
Fahrenheit.new(degrees * 9.0 / 5 + 32)
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_kelvin
|
15
|
+
Kelvin.new(degrees + 273.15)
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"#{degrees}°C"
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def to_self(value)
|
25
|
+
raise ArgumentError unless value.respond_to? :to_celsius
|
26
|
+
value.to_celsius
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module WeatherData
|
4
|
+
module Temperature
|
5
|
+
class Fahrenheit < Base
|
6
|
+
def to_celsius
|
7
|
+
Celsius.new((degrees - 32) * 5.0 / 9)
|
8
|
+
end
|
9
|
+
|
10
|
+
def to_fahrenheit
|
11
|
+
self
|
12
|
+
end
|
13
|
+
|
14
|
+
def to_kelvin
|
15
|
+
to_celsius.to_kelvin
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s
|
19
|
+
"#{degrees}°F"
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
|
24
|
+
def to_self(value)
|
25
|
+
raise ArgumentError unless value.respond_to? :to_fahrenheit
|
26
|
+
value.to_fahrenheit
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
module WeatherData
|
4
|
+
module Temperature
|
5
|
+
class Kelvin < Base
|
6
|
+
def initialize(deg)
|
7
|
+
super
|
8
|
+
|
9
|
+
@degrees = 0 if @degrees < 0
|
10
|
+
end
|
11
|
+
|
12
|
+
def to_celsius
|
13
|
+
Celsius.new(degrees - 273.15)
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_fahrenheit
|
17
|
+
to_celsius.to_fahrenheit
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_kelvin
|
21
|
+
self
|
22
|
+
end
|
23
|
+
|
24
|
+
def to_s
|
25
|
+
"#{degrees}"
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def to_self(value)
|
31
|
+
raise ArgumentError unless value.respond_to? :to_kelvin
|
32
|
+
value.to_kelvin
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require "weather_data/temperature"
|
4
|
+
require "weather_data/humidity"
|
5
|
+
require "weather_data/wind"
|
6
|
+
|
7
|
+
module WeatherData
|
8
|
+
class Weather
|
9
|
+
ATTRIBUTES = [
|
10
|
+
:temperature,
|
11
|
+
:humidity,
|
12
|
+
:wind
|
13
|
+
].freeze
|
14
|
+
|
15
|
+
attr_accessor *ATTRIBUTES
|
16
|
+
|
17
|
+
def self.parse(s)
|
18
|
+
attributes = {}.tap do |attrs|
|
19
|
+
{
|
20
|
+
:temperature => Temperature,
|
21
|
+
:humidity => Humidity,
|
22
|
+
:wind => Wind
|
23
|
+
}.each do |attr, parser|
|
24
|
+
begin
|
25
|
+
attrs[attr] = parser.parse(s)
|
26
|
+
rescue ArgumentError
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
raise ArgumentError.new(%Q{"#{s}" doesn't contain weather data}) if attributes.empty?
|
32
|
+
|
33
|
+
new(attributes)
|
34
|
+
end
|
35
|
+
|
36
|
+
def initialize(attributes = {})
|
37
|
+
ATTRIBUTES.each do |attr|
|
38
|
+
self.send(:"#{attr}=", attributes[attr])
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
def dew_point
|
43
|
+
if humidity && temperature
|
44
|
+
t = temperature.to_celsius.degrees
|
45
|
+
a, b, c = magnus_formula_constants(t)
|
46
|
+
gamma = Math.log(humidity / 100) + b * t / (c + t)
|
47
|
+
|
48
|
+
Temperature::Celsius.new(c * gamma / (b - gamma))
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def vapour_pressure
|
53
|
+
if humidity
|
54
|
+
t = temperature.to_celsius.degrees
|
55
|
+
(humidity / 100.0) * 6.105 * Math.exp(17.27 * t / (237.7 + t))
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
def humindex
|
60
|
+
if temperature && dew_point
|
61
|
+
t = temperature.to_celsius.degrees + 0.5555 * (6.11 * Math.exp(5417.753 * (1 / 273.16 - 1 / dew_point.to_kelvin.degrees)) - 10)
|
62
|
+
t.celsius
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def apparent_temperature
|
67
|
+
if vapour_pressure && temperature
|
68
|
+
t = temperature.to_celsius.degrees
|
69
|
+
Temperature::Celsius.new(t + 0.33 * vapour_pressure - 0.7 * wind.to_f - 4.0)
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def temperature=(value)
|
74
|
+
@temperature = case value
|
75
|
+
when String
|
76
|
+
Temperature.parse(value)
|
77
|
+
when Temperature::Base
|
78
|
+
value
|
79
|
+
else
|
80
|
+
raise ArgumentError.new(%Q{#{value.inspect} must be String or kind of WeatherData::Temperature::Base})
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def humidity=(value)
|
85
|
+
@humidity = case value
|
86
|
+
when String
|
87
|
+
Humidity.parse(value)
|
88
|
+
when Humidity::Relative
|
89
|
+
value
|
90
|
+
else
|
91
|
+
raise ArgumentError.new(%Q{#{value.inspect} must be String or an instance of WeatherData::Humidity::Relative})
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
private
|
96
|
+
|
97
|
+
def magnus_formula_constants(temperature)
|
98
|
+
if temperature > 0
|
99
|
+
[6.1121, 17.368, 238.88]
|
100
|
+
else
|
101
|
+
[6.1121, 17.966, 247.15]
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'delegate'
|
4
|
+
|
5
|
+
module WeatherData
|
6
|
+
class Wind < ::SimpleDelegator
|
7
|
+
ATTRIBUTES = [
|
8
|
+
:direction,
|
9
|
+
:speed
|
10
|
+
].freeze
|
11
|
+
|
12
|
+
CARDINAL_DIRECTIONS = {
|
13
|
+
:N => 0.0,
|
14
|
+
:NNE => 22.5,
|
15
|
+
:NE => 45.0,
|
16
|
+
:ENE => 67.5,
|
17
|
+
:E => 90.0,
|
18
|
+
:ESE => 112.5,
|
19
|
+
:SE => 135.0,
|
20
|
+
:SSE => 157.5,
|
21
|
+
:S => 180.0,
|
22
|
+
:SSW => 202.5,
|
23
|
+
:SW => 225.0,
|
24
|
+
:WSW => 247.5,
|
25
|
+
:W => 270.0,
|
26
|
+
:WNW => 292.5,
|
27
|
+
:NW => 315.0,
|
28
|
+
:NNW => 337.5
|
29
|
+
}.freeze
|
30
|
+
|
31
|
+
attr_accessor *ATTRIBUTES
|
32
|
+
|
33
|
+
def self.parse(s)
|
34
|
+
_, speed, units = /(\d+(?:\.\d+)?)\s*(m\/?s(?:ec)?|km\/?h(?:our)?)/i.match(s).to_a
|
35
|
+
units ||= "m/s"
|
36
|
+
|
37
|
+
raise ArgumentError.new(%Q{"#{s.inspect}" doesn't contain wind speed}) unless speed
|
38
|
+
|
39
|
+
_, direction = /(#{CARDINAL_DIRECTIONS.keys.join('|')})/.match(s).to_a
|
40
|
+
|
41
|
+
new(speed.to_f, units, direction)
|
42
|
+
end
|
43
|
+
|
44
|
+
def initialize(speed, units = "m/s", azimuth_or_direction = nil)
|
45
|
+
super normalize_speed(speed, units)
|
46
|
+
@direction = to_direction(azimuth_or_direction) if azimuth_or_direction
|
47
|
+
end
|
48
|
+
|
49
|
+
def speed
|
50
|
+
__getobj__
|
51
|
+
end
|
52
|
+
|
53
|
+
def speed=(value)
|
54
|
+
__setobj__(value)
|
55
|
+
end
|
56
|
+
|
57
|
+
def units
|
58
|
+
'm/s'
|
59
|
+
end
|
60
|
+
|
61
|
+
def to_kmh
|
62
|
+
speed * 3.6
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_s
|
66
|
+
["#{speed} #{units}", direction].compact.join(" ")
|
67
|
+
end
|
68
|
+
|
69
|
+
protected
|
70
|
+
|
71
|
+
def normalize_speed(speed, units)
|
72
|
+
if units.downcase.gsub('/', '') == 'kmh'
|
73
|
+
speed / 3.6
|
74
|
+
else
|
75
|
+
speed
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def to_direction(azimuth_or_direction)
|
80
|
+
if azimuth_or_direction.is_a? Numeric
|
81
|
+
d = (azimuth_or_direction + 360) % 360
|
82
|
+
|
83
|
+
unless d >= 0 && d < 360
|
84
|
+
raise ArgumentError.new("Invalid azimuth for wind direction: #{azimuth_or_direction.inspect}")
|
85
|
+
end
|
86
|
+
|
87
|
+
CARDINAL_DIRECTIONS.find { |direction, azimuth| (d - azimuth).abs < 22.5 }.first
|
88
|
+
else
|
89
|
+
d = azimuth_or_direction.upcase.gsub('/', '').to_sym
|
90
|
+
|
91
|
+
unless CARDINAL_DIRECTIONS.has_key? d
|
92
|
+
raise ArgumentError.new("Invalid azimuth for wind direction: #{azimuth_or_direction.inspect}")
|
93
|
+
end
|
94
|
+
|
95
|
+
d
|
96
|
+
end
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe WeatherData::Humidity do
|
6
|
+
describe ".parse" do
|
7
|
+
it "returns an initialized instance of WeatherData::RelativeHumidity" do
|
8
|
+
h = described_class.parse("Humidity: 80%")
|
9
|
+
|
10
|
+
expect(h).to be_kind_of WeatherData::Humidity::Relative
|
11
|
+
expect(h).to eq 80
|
12
|
+
end
|
13
|
+
|
14
|
+
context "if given string does not contain humidity" do
|
15
|
+
it "raises ArgumentError" do
|
16
|
+
expect { described_class.parse("string") }.to raise_error ArgumentError
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
context "if given value 0% or less" do
|
21
|
+
it "raises ArgumentError" do
|
22
|
+
expect { described_class.parse("0%") }.to raise_error ArgumentError
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
context "if given value greater than 100%" do
|
27
|
+
it "raises ArgumentError" do
|
28
|
+
expect { described_class.parse("101%") }.to raise_error ArgumentError
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WeatherData::Temperature::Base do
|
4
|
+
subject { WeatherData::Temperature::Base.new(10) }
|
5
|
+
|
6
|
+
shared_examples_for "returning new instance" do
|
7
|
+
it do
|
8
|
+
expect(subject.send(method_name, subject)).not_to eql subject
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#+" do
|
13
|
+
it_should_behave_like "returning new instance" do
|
14
|
+
let(:method_name) { :+ }
|
15
|
+
end
|
16
|
+
|
17
|
+
it "converts given value" do
|
18
|
+
subject.should_receive(:to_self).with(subject).and_return(subject)
|
19
|
+
|
20
|
+
subject + subject
|
21
|
+
end
|
22
|
+
|
23
|
+
it "returns sum of degrees" do
|
24
|
+
expect((subject + subject).degrees).to eq 20
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#-" do
|
29
|
+
it_should_behave_like "returning new instance" do
|
30
|
+
let(:method_name) { :- }
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns dfference between degrees" do
|
34
|
+
expect((subject - subject).degrees).to eq 0
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#*" do
|
39
|
+
it_should_behave_like "returning new instance" do
|
40
|
+
let(:method_name) { :* }
|
41
|
+
end
|
42
|
+
|
43
|
+
it "returns degrees multiplied by value" do
|
44
|
+
expect((subject * 10).degrees).to eq 100
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "#/" do
|
49
|
+
it_should_behave_like "returning new instance" do
|
50
|
+
let(:method_name) { :/ }
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns degrees divided by value" do
|
54
|
+
expect((subject / 10).degrees).to eq 1
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
describe "#==" do
|
59
|
+
subject { WeatherData::Temperature::Base }
|
60
|
+
|
61
|
+
it "can be compared to Numeric" do
|
62
|
+
expect(subject.new(10) == 10).to be_true
|
63
|
+
expect(subject.new(10) == 10.0).to be_true
|
64
|
+
expect(subject.new(10) == 11).to be_false
|
65
|
+
end
|
66
|
+
end
|
67
|
+
|
68
|
+
describe "#to_i" do
|
69
|
+
subject { WeatherData::Temperature::Base }
|
70
|
+
|
71
|
+
it "returns degrees rounded" do
|
72
|
+
expect(subject.new(15.6).to_i).to eq 16
|
73
|
+
expect(subject.new(15.1).to_i).to eq 15
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
describe "#to_f" do
|
78
|
+
subject { WeatherData::Temperature::Base }
|
79
|
+
|
80
|
+
it "returns degrees" do
|
81
|
+
expect(subject.new(15.6).to_f).to eq 15.6
|
82
|
+
expect(subject.new(15.1).to_f).to eq 15.1
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WeatherData::Temperature::Celsius do
|
4
|
+
subject { WeatherData::Temperature::Celsius.new(0) }
|
5
|
+
|
6
|
+
describe "#to_celsius" do
|
7
|
+
it "returns self" do
|
8
|
+
expect(subject.to_celsius).to eq subject
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#to_fahrenheit" do
|
13
|
+
it "returns degrees in Fahrenheit" do
|
14
|
+
expect(subject.to_fahrenheit).to eq 32
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#to_kelvin" do
|
19
|
+
it "returns degrees in Kelvin" do
|
20
|
+
expect(subject.to_kelvin).to eq 273.15
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WeatherData::Temperature::Fahrenheit do
|
4
|
+
subject { WeatherData::Temperature::Fahrenheit.new(32) }
|
5
|
+
|
6
|
+
describe "#to_celsius" do
|
7
|
+
it "returns degrees in Celsius" do
|
8
|
+
expect(subject.to_celsius).to eq 0
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#to_fahrenheit" do
|
13
|
+
it "returns self" do
|
14
|
+
expect(subject.to_fahrenheit).to eq subject
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#to_kelvin" do
|
19
|
+
it "returns degrees in Kelvin" do
|
20
|
+
expect(subject.to_kelvin).to eq 273.15
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe WeatherData::Temperature::Kelvin do
|
4
|
+
subject { WeatherData::Temperature::Kelvin.new(273.15) }
|
5
|
+
|
6
|
+
describe "#to_celsius" do
|
7
|
+
it "returns degrees in Celsius" do
|
8
|
+
expect(subject.to_celsius).to eq 0
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
describe "#to_fahrenheit" do
|
13
|
+
it "returns degrees in Fahrenheit" do
|
14
|
+
expect(subject.to_fahrenheit).to eq 32
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
describe "#to_kelvin" do
|
19
|
+
it "returns self" do
|
20
|
+
expect(subject.to_kelvin).to eq subject
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe WeatherData::Temperature do
|
6
|
+
[0, 0.0].each do |number|
|
7
|
+
describe "extends #{number.class}" do
|
8
|
+
{
|
9
|
+
:celsius => WeatherData::Temperature::Celsius,
|
10
|
+
:fahrenheit => WeatherData::Temperature::Fahrenheit,
|
11
|
+
:kelvin => WeatherData::Temperature::Kelvin
|
12
|
+
}.each do |method_name, klass|
|
13
|
+
it "adds #{method_name}" do
|
14
|
+
expect(number).to respond_to(method_name)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "#{method_name} returns an instance of #{klass.name}" do
|
18
|
+
expect(number.send(method_name)).to be_kind_of klass
|
19
|
+
expect(number.send(method_name).degrees).to eq number
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
describe "#parse" do
|
26
|
+
it "parses temperature in Celsius" do
|
27
|
+
[15.2, -15.2].each do |deg|
|
28
|
+
["#{deg} °C", "#{deg}°C", "#{deg}C", "#{deg} C", "Temperature: #{deg}°C"].each do |s|
|
29
|
+
t = subject.parse(s)
|
30
|
+
expect(t).to be_kind_of WeatherData::Temperature::Celsius
|
31
|
+
expect(t).to eq deg
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
it "parses temperature in Fahrenheit" do
|
37
|
+
[15.2, -15.2].each do |deg|
|
38
|
+
["#{deg} °F", "#{deg}°F", "#{deg}F", "#{deg} F", "Temperature: #{deg}°F"].each do |s|
|
39
|
+
t = subject.parse(s)
|
40
|
+
expect(t).to be_kind_of WeatherData::Temperature::Fahrenheit
|
41
|
+
expect(t).to eq deg
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
it "parses temperature in Kelvin" do
|
47
|
+
["15.2K", "15.2 K"].each do |s|
|
48
|
+
t = subject.parse(s)
|
49
|
+
expect(t).to be_kind_of WeatherData::Temperature::Kelvin
|
50
|
+
expect(t).to eq 15.2
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when there is no temperature within given string" do
|
55
|
+
it "raises ArgumentError" do
|
56
|
+
expect { subject.parse("bogus string") }.to raise_error ArgumentError
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe WeatherData::Weather do
|
6
|
+
subject { described_class.new(:temperature => "+30C", :humidity => "60%") }
|
7
|
+
|
8
|
+
describe ".parse" do
|
9
|
+
it "returns an instance of WeatherData::Weather" do
|
10
|
+
expect(described_class.parse("10C, 40%")).to be_an_instance_of described_class
|
11
|
+
end
|
12
|
+
|
13
|
+
it "initializes temperature" do
|
14
|
+
expect(described_class.parse("10C, 40%").temperature).to eq 10
|
15
|
+
end
|
16
|
+
|
17
|
+
it "initializes humidity" do
|
18
|
+
expect(described_class.parse("10C, 40%").humidity).to eq 40
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
describe "#dew_point" do
|
23
|
+
it "returns dew point based on Magnus formula" do
|
24
|
+
expect(subject.dew_point.to_celsius.degrees.round).to eq 21
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "#humindex" do
|
29
|
+
before do
|
30
|
+
subject.stub(:dew_point).and_return WeatherData::Temperature::Celsius.new(15)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "returns humindex" do
|
34
|
+
expect(subject.humindex.to_celsius.degrees.round).to eq 34
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
describe "#vapour_pressure" do
|
39
|
+
it "returns vapour pressure in hPa for given conditions" do
|
40
|
+
expect(subject.vapour_pressure.round).to eq ((42 * subject.humidity / 100.0).round)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
describe "#apparent_temperature" do
|
45
|
+
before do
|
46
|
+
subject.stub(
|
47
|
+
:temperature => WeatherData::Temperature::Celsius.new(18),
|
48
|
+
:humidity => WeatherData::Humidity::Relative.new(70),
|
49
|
+
:wind => WeatherData::Wind.new(10, "km/h")
|
50
|
+
)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns Australian apparent temperature for given conditions" do
|
54
|
+
expect(subject.apparent_temperature.to_celsius.degrees.round).to eq 17
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding : utf-8 -*-
|
2
|
+
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
describe WeatherData::Wind do
|
6
|
+
subject { described_class.new(1) }
|
7
|
+
|
8
|
+
describe ".parse" do
|
9
|
+
end
|
10
|
+
|
11
|
+
describe "#initialize" do
|
12
|
+
end
|
13
|
+
|
14
|
+
describe "#to_kmh" do
|
15
|
+
it "returns speed in km/h" do
|
16
|
+
expect(subject.to_kmh).to eq 3.6
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'weather_data/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "weather_data"
|
8
|
+
gem.version = WeatherData::VERSION
|
9
|
+
gem.authors = ["Andrew Slotin"]
|
10
|
+
gem.email = ["andrew.slotin@gmail.com"]
|
11
|
+
gem.description = %q{Simple weather data manipulation}
|
12
|
+
gem.summary = %q{Provides conversion between temperature units etc.}
|
13
|
+
gem.homepage = ""
|
14
|
+
|
15
|
+
gem.files = `git ls-files`.split($/)
|
16
|
+
gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
|
17
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
18
|
+
gem.require_paths = ["lib"]
|
19
|
+
|
20
|
+
gem.add_development_dependency('rake')
|
21
|
+
gem.add_development_dependency('rspec')
|
22
|
+
end
|
metadata
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: weather_data
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Andrew Slotin
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-03-28 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rake
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
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: rspec
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Simple weather data manipulation
|
42
|
+
email:
|
43
|
+
- andrew.slotin@gmail.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- .travis.yml
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- lib/weather_data.rb
|
55
|
+
- lib/weather_data/humidity.rb
|
56
|
+
- lib/weather_data/humidity/relative.rb
|
57
|
+
- lib/weather_data/temperature.rb
|
58
|
+
- lib/weather_data/temperature/base.rb
|
59
|
+
- lib/weather_data/temperature/celsius.rb
|
60
|
+
- lib/weather_data/temperature/fahrenheit.rb
|
61
|
+
- lib/weather_data/temperature/kelvin.rb
|
62
|
+
- lib/weather_data/version.rb
|
63
|
+
- lib/weather_data/weather.rb
|
64
|
+
- lib/weather_data/wind.rb
|
65
|
+
- spec/spec_helper.rb
|
66
|
+
- spec/weather_data/humidity_spec.rb
|
67
|
+
- spec/weather_data/temperature/base_spec.rb
|
68
|
+
- spec/weather_data/temperature/celsius_spec.rb
|
69
|
+
- spec/weather_data/temperature/fahrenheit_spec.rb
|
70
|
+
- spec/weather_data/temperature/kelvin_spec.rb
|
71
|
+
- spec/weather_data/temperature_spec.rb
|
72
|
+
- spec/weather_data/version_spec.rb
|
73
|
+
- spec/weather_data/weather_spec.rb
|
74
|
+
- spec/weather_data/wind_spec.rb
|
75
|
+
- weather_data.gemspec
|
76
|
+
homepage: ''
|
77
|
+
licenses: []
|
78
|
+
metadata: {}
|
79
|
+
post_install_message:
|
80
|
+
rdoc_options: []
|
81
|
+
require_paths:
|
82
|
+
- lib
|
83
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
84
|
+
requirements:
|
85
|
+
- - '>='
|
86
|
+
- !ruby/object:Gem::Version
|
87
|
+
version: '0'
|
88
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
|
+
requirements:
|
90
|
+
- - '>='
|
91
|
+
- !ruby/object:Gem::Version
|
92
|
+
version: '0'
|
93
|
+
requirements: []
|
94
|
+
rubyforge_project:
|
95
|
+
rubygems_version: 2.0.2
|
96
|
+
signing_key:
|
97
|
+
specification_version: 4
|
98
|
+
summary: Provides conversion between temperature units etc.
|
99
|
+
test_files:
|
100
|
+
- spec/spec_helper.rb
|
101
|
+
- spec/weather_data/humidity_spec.rb
|
102
|
+
- spec/weather_data/temperature/base_spec.rb
|
103
|
+
- spec/weather_data/temperature/celsius_spec.rb
|
104
|
+
- spec/weather_data/temperature/fahrenheit_spec.rb
|
105
|
+
- spec/weather_data/temperature/kelvin_spec.rb
|
106
|
+
- spec/weather_data/temperature_spec.rb
|
107
|
+
- spec/weather_data/version_spec.rb
|
108
|
+
- spec/weather_data/weather_spec.rb
|
109
|
+
- spec/weather_data/wind_spec.rb
|