weather_data 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -0,0 +1,20 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ .rspec
19
+ .ruby-gemset
20
+ .ruby-version
data/.travis.yml ADDED
@@ -0,0 +1,17 @@
1
+ language: ruby
2
+ script:
3
+ - bundle exec rake spec
4
+ rvm:
5
+ - 1.8.7
6
+ - ree
7
+ - 1.9.2
8
+ - 1.9.3
9
+ - 2.0.0
10
+ - ruby-head
11
+ - rbx-18mode
12
+ - rbx-19mode
13
+ - jruby
14
+ - jruby-18mode
15
+ matrix:
16
+ allow_failures:
17
+ - rvm: ruby-head
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in weather_data.gemspec
4
+ gemspec
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
+ [![Build Status](https://travis-ci.org/andrewslotin/weather_data.png)](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
@@ -0,0 +1,4 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new('spec')
@@ -0,0 +1,8 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require "weather_data/version"
4
+ require "weather_data/weather"
5
+
6
+ module WeatherData
7
+
8
+ end
@@ -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,5 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ module WeatherData
4
+ VERSION = "0.0.1"
5
+ 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
@@ -0,0 +1,10 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'rubygems'
4
+ require 'bundler/setup'
5
+
6
+ require 'weather_data'
7
+
8
+ RSpec.configure do |config|
9
+
10
+ end
@@ -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,9 @@
1
+ # -*- encoding : utf-8 -*-
2
+
3
+ require 'spec_helper'
4
+
5
+ describe WeatherData do
6
+ subject { WeatherData }
7
+
8
+ it { expect(subject::VERSION).to eq '0.0.1' }
9
+ 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