stormglass 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 82b85c3b145480cb967b4138502dff4b3459c959
4
+ data.tar.gz: 263aa703403641202b4b7bbeec493012439af742
5
+ SHA512:
6
+ metadata.gz: '09229ef4fda64e6b36c9ee45d7483b1d41eee15b002c725a526bd7e5be99d61f3d3d508aa2e0d95660833c36c02805f0d9114490d9b90fda8de6c2b56af12651'
7
+ data.tar.gz: 7a0bbedde8f5df9ecabfcf2f8b2bac33adc00765edddeb50c2344e89acbc628868b262f656c755a8388747a670398079f71b1063a484f19116956f22319d31a3
@@ -0,0 +1,8 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
@@ -0,0 +1 @@
1
+ 2.4.2
@@ -0,0 +1,5 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.4
5
+ before_install: gem install bundler -v 1.16.1
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in stormglass.gemspec
6
+ gemspec
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ Stormglass (0.1.0)
5
+ geocoder
6
+ rest-client
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ domain_name (0.5.20180417)
12
+ unf (>= 0.0.5, < 1.0.0)
13
+ geocoder (1.5.0)
14
+ http-cookie (1.0.3)
15
+ domain_name (~> 0.5)
16
+ mime-types (3.2.2)
17
+ mime-types-data (~> 3.2015)
18
+ mime-types-data (3.2018.0812)
19
+ minitest (5.10.3)
20
+ netrc (0.11.0)
21
+ rake (10.5.0)
22
+ rest-client (2.0.2)
23
+ http-cookie (>= 1.0.2, < 2.0)
24
+ mime-types (>= 1.16, < 4.0)
25
+ netrc (~> 0.8)
26
+ unf (0.1.4)
27
+ unf_ext
28
+ unf_ext (0.0.7.5)
29
+
30
+ PLATFORMS
31
+ ruby
32
+
33
+ DEPENDENCIES
34
+ Stormglass!
35
+ bundler (~> 1.16)
36
+ minitest (~> 5.3)
37
+ rake (~> 10.0)
38
+
39
+ BUNDLED WITH
40
+ 1.16.6
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 Russell Jennings
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
@@ -0,0 +1,128 @@
1
+ # Stormglass
2
+
3
+ Ruby gem for access to the [Stormglass](https://stormglass.io/) Weather forecast API.
4
+
5
+ # Overview
6
+
7
+ This gem augments the Stormglass API responses to make consuming the API more accessible for ruby developers.
8
+
9
+ - Provides weather lookup for address and lat/lng
10
+ - Provides units and descriptions for all forecast types (from API docs)
11
+ - Specify start/end as DateTime
12
+ - specify `hours` to calcuate end offset from start (default 12)
13
+ - Creates alternative representations of values (such as Fahrenheit, or knots)
14
+ - Specify default units and sources via `Stormglass.configure` block, or override at method invocation
15
+
16
+ For more information, visit [Stormglass API docs](https://docs.stormglass.io/)
17
+
18
+ *Note: this gem currently only implements the `point` API endpoint. Accepting pull requests!*
19
+
20
+ # Installation
21
+
22
+ ```
23
+ gem install stormglass
24
+ ```
25
+
26
+ or in your gemfile
27
+
28
+ ```
29
+ gem 'stormglass'
30
+ ```
31
+
32
+ # API Key
33
+
34
+ To use Stormglass you'll need to provide an API key, which can be configured a number of ways:
35
+
36
+ 1. Enviroment variable `STORMGLASS_API_KEY`
37
+ 2. within a `Stormglass.configure` block (more on that below)
38
+ 3. with Rails secret `stormglass_api_key`
39
+ 4. at request invocation by passing `key`
40
+
41
+ # Configuration
42
+
43
+ You can configure the gem with the following block
44
+ ```
45
+ Stormglass.configure do |config|
46
+ config.units = {air_temperature: 'F', gust: 'knot'}
47
+ config.api_key = 'abcd-abcd-abcd'
48
+ config.source = 'sg'
49
+ end
50
+ ```
51
+
52
+ # Walkthrough
53
+
54
+ First, let's query the stormglass API for an address. This will look up via `Geocoder` and then call `Stormglass.for_lat_lng` for us with the first result.
55
+ ```
56
+ response = Stormglass.for_address('123 broad street philadelphia pa', hours: 24)
57
+ => #<Stormglass::Response remaining_requests=31,
58
+ hours=[#<Stormglass::Hour time='2018-12-12 21:00:00 +0000'> ...]>
59
+ ```
60
+ The methods we can call are underscored variants of what the camelCaps API returns. Taken from `stormglass/hour.rb`
61
+ ```
62
+ VALUES = [:air_temperature,:cloud_cover,:current_direction,:current_speed,:gust,:humidity,
63
+ :precipitation,:pressure,:sea_level,:swell_direction,:swell_height,:swell_period,
64
+ :visibility,:water_temperature,:wave_direction,:wave_height,:wave_period,
65
+ :wind_direction,:wind_speed,:wind_wave_direction,:wind_wave_height,:wind_wave_period]
66
+ ```
67
+ Let's see the first hour's air_temperature.
68
+ ```
69
+ response.hours.first.air_temperature
70
+ => #<Stormglass::Value value=44.91, unit='C', description='Air temperature as degrees Celsius',
71
+ unit_type='C', unit_types=["C", "F"], data_source='sg',
72
+ data_sources=["sg", "dwd", "noaa", "wrf"]>
73
+ ```
74
+ we can also call `response.first.air_temperature` or even `response.air_temperature` and it will defer to the first hour result.
75
+
76
+ But what if we don't want the first hour? In addition to calling `response.hours.select{...}`, you can use `#find` with your local timezone and it will convert to UTC for lookup
77
+ ```
78
+ response.find('7AM EST').air_temperature
79
+ => #<Stormglass::Value value=44.91, unit='C', description='Air temperature as degrees Celsius',
80
+ unit_type='C', unit_types=["C", "F"], data_source='sg', data_sources=["sg", "dwd", "noaa", "wrf"]>
81
+ ```
82
+ we can work with the `response.find('7AM EST').air_temperature.value` directly (a Float), or call `to_s` for a pretty string
83
+
84
+ ```
85
+ response.air_temperature.to_s
86
+ => "4.91 C"
87
+ ```
88
+
89
+ What if we want air temperature in Fahrenheit instead? No need to whip out the calculator, we've
90
+ got you covered!
91
+
92
+ ```
93
+ response.air_temperature(unit_type: 'F')
94
+ => #<Stormglass::Value value=44.91, unit='F', description='Air temperature as degrees Fahrenheit',
95
+ unit_type='F', unit_types=["C", "F"], data_source='sg', data_sources=["sg", "dwd", "noaa", "wrf"]>
96
+ ```
97
+
98
+ Or we can reference a different data source than the default
99
+ ```
100
+ response.air_temperature(unit_type: 'F', data_source: 'noaa')
101
+ => #<Stormglass::Value value=45.33, unit='F', description='Air temperature as degrees Fahrenheit',
102
+ unit_type='F', unit_types=["C", "F"], data_source='noaa', data_sources=["sg", "dwd", "noaa", "wrf"]>
103
+ ```
104
+
105
+ # Structure
106
+
107
+ There are a few primary classes involved:
108
+ 1. `Stormglass::Response` represents the result from stormglass, wrapping `hours` and `meta` responses.
109
+ Calling something like `#air_temperature` on this delegates to `#hours.first`
110
+ 2. `Stormglass::Hour` represents an hour portion of the API response
111
+ 3. `Stormglass::Value` represents a particular forecast value. along with the numeric `value` it also exposes
112
+ the `description` and `unit` used. It is just a wrapper to the collection of `Stormglass::Subvalue`'s and defers to the preferred/specified one.
113
+ 4. `Stormglass::Subvalue` represents a particular variant of forecast value, such as `air_temperature` in Celsius or in Fahrenheit.
114
+
115
+ For any of the above instances, you can call `#src` to access the raw (usually JSON) data that was passed in to populate it.
116
+
117
+ Because each forecast type has different sources and units, `Stormglass::Value` instances exposes the progmatic
118
+ available options `data_sources` and `unit_types` you can reference for alternatives to provide.
119
+
120
+ ```
121
+ response.air_temperature(unit_type: 'C')
122
+ => #<Stormglass::Value value=7.17, unit='C', description='Air temperature as degrees Celsius',
123
+ unit_type='C', unit_types=["C", "F"], data_source='sg', data_sources=["sg", "dwd", "noaa", "wrf"]>
124
+
125
+ response.air_temperature(unit_type: 'C', data_source: 'wrf')
126
+ => #<Stormglass::Value value=8.34, unit='C', description='Air temperature as degrees Celsius',
127
+ unit_type='C', unit_types=["C", "F"], data_source='noaa', data_sources=["sg", "dwd", "noaa", "wrf"]>
128
+ ```
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "stormglass"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,103 @@
1
+ require "stormglass/version"
2
+ require 'restclient'
3
+ require 'date'
4
+ require 'json'
5
+ require 'forwardable'
6
+ require 'geocoder'
7
+ require 'stormglass/configuration'
8
+ require 'stormglass/hour'
9
+ require 'stormglass/response'
10
+ require 'stormglass/string'
11
+ require 'stormglass/result_dict'
12
+ require 'stormglass/value'
13
+ require 'stormglass/subvalue'
14
+ require 'stormglass/alternate_values'
15
+
16
+ Dir[File.join(__dir__, 'stormglass', 'alternate_values', '*.rb')].each { |file| require file }
17
+
18
+
19
+ module Stormglass
20
+ class Error < StandardError; end
21
+ class ConnectionError < StandardError; end
22
+ class ExceededLimitError < StandardError; end
23
+
24
+
25
+ class << self
26
+ attr_accessor :settings
27
+
28
+ def configure
29
+ self.settings ||= Configuration.new
30
+ yield(settings)
31
+ end
32
+
33
+ def set_settings
34
+ self.settings = Configuration.new
35
+ end
36
+ end
37
+
38
+ # lookup an address (such as city + zip) and get the coordinates for the first match
39
+ def self.for_address(address_string, params={})
40
+ if results = Geocoder.search(address_string)
41
+ lat,lng = results.first.coordinates
42
+ self.for_lat_lng(lat: lat, lng: lng, params: params)
43
+ else
44
+ raise Error, 'Could not find address'
45
+ end
46
+ end
47
+
48
+ # query StormGlass given lat/lng.
49
+ # params:
50
+ # :lat - dateTime (default Now)
51
+ # :lng - Datetime (default 12 hours from :start)
52
+ # :params - additional params available to self.reqest
53
+ def self.for_lat_lng(lat:, lng:, params: {})
54
+ self.request(params: {lat: lat, lng: lng}.merge(params))
55
+ end
56
+
57
+ # Primary interface to StormGlass.
58
+ # params:
59
+ # :start - dateTime (default Now)
60
+ # :end - Datetime (default 12 hours from :start)
61
+ # :hours - number of hours to determine end (default 12)
62
+ # :key - API key (default to api_key method )
63
+ def self.request(endpoint: 'point', params: {})
64
+ hours = (params.delete(:hours) || 11) - 1
65
+ params[:key] ||= api_key
66
+ params[:start] ||= DateTime.now
67
+ params[:end] ||= hours_offset(params[:start], hours)
68
+ params[:start] = query_time_string(params[:start])
69
+ params[:end] = query_time_string(params[:end])
70
+ key = params.delete(:key)
71
+ begin
72
+ body = RestClient.get("https://api.stormglass.io/#{endpoint}", {params: params, 'Authorization' => key}).body
73
+ rescue SocketError => msg
74
+ puts msg
75
+ raise ConnectionError, 'error connecting to stormglass'
76
+ rescue RestClient::PaymentRequired => msg
77
+ puts msg
78
+ raise ExceededLimitError, 'exceeded limit. payment required for additional daily requests'
79
+ end
80
+ Stormglass::Response.new(body)
81
+ end
82
+
83
+ # API key for stormglass. key is sourced:
84
+ # - if set via enviroment variable
85
+ # - if set in configuration block
86
+ # - if defined in Rails secrets
87
+ # - when passed directly to parameters
88
+ def self.api_key
89
+ key = Stormglass.settings.api_key
90
+ key ||= Rails.application.secrets[:stormglass_api_key] if Gem.loaded_specs.has_key?('rails')
91
+ key
92
+ end
93
+
94
+ def self.hours_offset(start_time, hours=12)
95
+ (start_time + (Rational(1,24) * hours))
96
+ end
97
+
98
+ def self.query_time_string(datetime)
99
+ datetime.new_offset(0).iso8601
100
+ end
101
+
102
+ end
103
+ Stormglass.set_settings
@@ -0,0 +1,32 @@
1
+ class Stormglass::AlternateValues
2
+
3
+ def self.perform(subvalues)
4
+ @original = subvalues.first
5
+ case subvalues.first[:unit_type]
6
+ when "Ms" then self::MetersSec.perform(subvalues)
7
+ when "M" then self::Meters.perform(subvalues)
8
+ when "C" then self::Celsius.perform(subvalues)
9
+ when "Km" then self::Kilometers.perform(subvalues)
10
+ when 'Kgm2' then self::Kgm2.perform(subvalues)
11
+ when 'Deg' then self::Degrees.perform(subvalues)
12
+ end
13
+ end
14
+
15
+ def self.meters_sec(subvalues)
16
+
17
+ end
18
+
19
+ def self.meters(subvalues)
20
+
21
+ end
22
+
23
+ def self.c(subvalues)
24
+
25
+ end
26
+
27
+ def self.km(subvalues)
28
+
29
+ end
30
+
31
+
32
+ end
@@ -0,0 +1,13 @@
1
+ class Stormglass::AlternateValues::Celsius
2
+
3
+ def self.perform(subvalues)
4
+ inject_fahrenheit(subvalues)
5
+ end
6
+
7
+ private
8
+
9
+ def self.inject_fahrenheit(subvalues)
10
+ subvalues << {unit_type: 'F', value: (subvalues.first[:value] * 1.8 + 32).round(2), unit: 'F', description: subvalues.first[:description], unit_description: 'degrees Fahrenheit'}
11
+ end
12
+
13
+ end
@@ -0,0 +1,16 @@
1
+ class Stormglass::AlternateValues::Degrees
2
+
3
+ POINTS = ['N', 'NNE', 'NE', 'ENE', 'E', 'ESE', 'SE', 'SSE', 'S', 'SSW', 'SW', 'WSW', 'W', 'WNW', 'NW', 'NNW']
4
+
5
+ def self.perform(subvalues)
6
+ inject_compass_points(subvalues)
7
+ end
8
+
9
+ private
10
+
11
+ def self.inject_compass_points(subvalues)
12
+ compass_point = Geocoder::Calculations.compass_point(subvalues.first[:value], POINTS)
13
+ subvalues << {unit_type: 'compass', value: compass_point, unit: '', description: subvalues.first[:description], unit_description: 'From 16 shorthand compass points'}
14
+ end
15
+
16
+ end
@@ -0,0 +1,12 @@
1
+ class Stormglass::AlternateValues::Kgm2
2
+
3
+ def self.perform(subvalues)
4
+ inject_inches(subvalues)
5
+ end
6
+
7
+ private
8
+
9
+ def self.inject_inches(subvalues)
10
+ subvalues << {unit_type: 'inches', unit: 'in', value: (subvalues.first[:value] * 0.039370).round(2), description: subvalues.first[:description], unit_description: 'inches'}
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ class Stormglass::AlternateValues::Kilometers
2
+
3
+ def self.perform(subvalues)
4
+ inject_miles(subvalues)
5
+ end
6
+
7
+ private
8
+
9
+ def self.inject_miles(subvalues)
10
+ subvalues << {unit_type: 'miles', unit: 'mi', value: (subvalues.first[:value] / 1.609344).round(2), description: subvalues.first[:description], unit_description: 'miles'}
11
+ end
12
+ end
@@ -0,0 +1,12 @@
1
+ class Stormglass::AlternateValues::Meters
2
+
3
+ def self.perform(subvalues)
4
+ inject_feet(subvalues)
5
+ end
6
+
7
+ private
8
+
9
+ def self.inject_feet(subvalues)
10
+ subvalues << {unit_type: 'feet', unit: 'ft', value: (subvalues.first[:value] / 0.3048).round(2), description: subvalues.first[:description], unit_description: 'feet'}
11
+ end
12
+ end
@@ -0,0 +1,17 @@
1
+ class Stormglass::AlternateValues::MetersSec
2
+
3
+ def self.perform(subvalues)
4
+ inject_knots(subvalues)
5
+ inject_mph(subvalues)
6
+ end
7
+
8
+ private
9
+
10
+ def self.inject_knots(subvalues)
11
+ subvalues << {unit_type: 'knot', unit: 'Kn', value: (subvalues.first[:value] * 1.9438445).round(2), description: subvalues.first[:description], unit_description: 'nautical knots'}
12
+ end
13
+
14
+ def self.inject_mph(subvalues)
15
+ subvalues << {unit_type: 'MPH', unit: 'MPH', value: (subvalues.first[:value] * 2.236936).round(2), description: subvalues.first[:description], unit_description: 'miles per hour'}
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ class Configuration
2
+ attr_accessor :units, :api_key, :source
3
+
4
+ def initialize
5
+ @units = {}
6
+ @api_key = ENV['STORMGLASS_API_KEY']
7
+ @source = 'sg'
8
+ end
9
+ end
@@ -0,0 +1,61 @@
1
+ # represents an hour result from Stormglass
2
+ # VALUES represent callable methods
3
+ class Stormglass::Hour
4
+
5
+ VALUES = [:air_temperature,:cloud_cover,:current_direction,:current_speed,:gust,:humidity,
6
+ :precipitation,:pressure,:sea_level,:swell_direction,:swell_height,:swell_period,
7
+ :visibility,:water_temperature,:wave_direction,:wave_height,:wave_period,
8
+ :wind_direction,:wind_speed,:wind_wave_direction,:wind_wave_height,:wind_wave_period]
9
+
10
+ def initialize(src)
11
+ @src = src
12
+ end
13
+
14
+ def src
15
+ @src
16
+ end
17
+
18
+ def time
19
+ Time.parse(src["time"])
20
+ end
21
+
22
+ def values
23
+ src.keys.collect(&:underscore)
24
+ end
25
+
26
+ def method_missing(method, *args)
27
+ if VALUES.include?(method)
28
+ get_value(method, args)
29
+ else
30
+ super
31
+ end
32
+ end
33
+
34
+ def respond_to_missing?(method_name, include_private = false)
35
+ VALUES.include?(method_name) || super
36
+ end
37
+
38
+ def inspect
39
+ "#<#{self.class.to_s} time='#{time}'> "
40
+ end
41
+
42
+ # handler for each VALUES method (such as air_temperature)
43
+ # takes two optional arguments:
44
+ # data_source: - data source to use. (default 'sg')
45
+ # unit_type: - preferred unit type (default API result)
46
+ def get_value(attribute,args)
47
+ vals = fetch_value(args.first ? {attribute: attribute}.merge(args.first) : {attribute: attribute})
48
+ @src.keys.collect(&:underscore).zip(vals).to_h[attribute.to_s]
49
+ end
50
+
51
+ def fetch_value(data_source: nil, attribute:, unit_type: nil)
52
+ data_source ||= Stormglass.settings.source
53
+ @src.values.collect do |val|
54
+ if val.is_a?(String)
55
+ val
56
+ else
57
+ Stormglass::Value.new(attribute, val, data_source, unit_type)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,52 @@
1
+ # Responses from Stormglass API are wrapped in this class
2
+ class Stormglass::Response
3
+ extend Forwardable
4
+
5
+ # convenience delegate calls (like air_temperature)
6
+ # to the first hour returned
7
+ def_delegators :first, *Stormglass::Hour::VALUES
8
+
9
+
10
+ def initialize(src)
11
+ @src = JSON.parse(src)
12
+ end
13
+
14
+ # an array of Stormglass::Hour instances
15
+ def hours
16
+ @hours ||= []
17
+ return @hours if !@hours.empty?
18
+ src['hours'].each{|h| @hours << Stormglass::Hour.new(h) }
19
+ @hours
20
+ end
21
+
22
+ def src
23
+ @src
24
+ end
25
+
26
+ def inspect
27
+ string = '#<' + "#{self.class.to_s} remaining_requests=#{remaining_requests}, "
28
+ string +="hours=#{hours.to_s}> "
29
+ string
30
+ end
31
+
32
+ def first
33
+ hours.first
34
+ end
35
+
36
+ def last
37
+ hours.last
38
+ end
39
+
40
+ # takes a string like "7PM EST" and returns the relevant hour if found
41
+ def find(string)
42
+ hours.find{|h| h.time == Time.parse(string)}
43
+ end
44
+
45
+ def meta
46
+ src['meta']
47
+ end
48
+
49
+ def remaining_requests
50
+ meta['dailyQuota'] - meta['requestCount']
51
+ end
52
+ end
@@ -0,0 +1,29 @@
1
+ module Stormglass
2
+
3
+ RESULT_DICT= {
4
+ :air_temperature =>{:unit_type => "C", :unit=>"C", :description=>"Air temperature as", :unit_description =>"degrees Celsius"},
5
+ :air_pressure =>{:unit_type => "hPa", :unit=>"hPa", :description=>"Air pressure as", :unit_description =>"hectopascal"},
6
+ :cloud_cover =>{:unit_type => "%", :unit=>"%", :description=>"Total cloud coverage as", :unit_description =>"percent"},
7
+ :current_direction =>{:unit_type => "Deg", :unit=>"°", :description=>"Direction of current", :unit_description =>"0° indicates coming from north"},
8
+ :current_speed =>{:unit_type => "Ms", :unit=>"M/s", :description=>"Speed of current as", :unit_description =>"meters per second"},
9
+ :gust =>{:unit_type => "Ms", :unit=>"M/s", :description=>"Wind gust as", :unit_description =>"meters per second"},
10
+ :humidity =>{:unit_type => "%", :unit=>"%", :description=>"Relative humidity as", :unit_description => "percent"},
11
+ :ice_cover =>{:unit_type => "/1", :unit=>"/1", :description=>"Proportion", :unit_description=> "over 1"},
12
+ :precipitation =>{:unit_type => "Kgm2", :unit=>"kg/m²", :description=>"Mean precipitation as", :unit_description => "kilogram per square meter"},
13
+ :sea_level =>{:unit_type => "M", :unit=>"M", :description=>"Height of sea level as", :unit_description =>"meters"},
14
+ :snow_depth =>{:unit_type => "M", :unit=>"M", :description=>"Depth of snow as", :unit_description =>"meters"},
15
+ :swell_direction =>{:unit_type => "Deg", :unit=>"°", :description=>"Direction of swell waves.", :unit_description => "0° indicates coming from north"},
16
+ :swell_height =>{:unit_type => "M", :unit=>"M", :description=>"Height of swell waves as", :unit_description =>"meters"},
17
+ :swell_period =>{:unit_type => "Sec", :unit=>"s", :description=>"Period of swell waves as", :unit_description =>"seconds"},
18
+ :visibility =>{:unit_type => "Km", :unit=>"km", :description=>"Horizontal visibility as", :unit_description =>"Kilometer"},
19
+ :water_temperature =>{:unit_type => "C", :unit=>"C", :description=>"Water temperature as", :unit_description =>"degrees Celsius"},
20
+ :wave_direction =>{:unit_type => "Deg", :unit=>"°", :description=>"Direction of combined wind and swell waves.",:unit_description => "0° indicates coming from north"},
21
+ :wave_height =>{:unit_type => "M", :unit=>"M", :description=>"Height of combined wind and swell waves as", :unit_description =>"meters"},
22
+ :wave_period =>{:unit_type => "Sec", :unit=>"s", :description=>"Period of combined wind and swell waves as", :unit_description =>"seconds"},
23
+ :wind_wave_direction=>{:unit_type => "Deg", :unit=>"°", :description=>"Direction of wind waves.", :unit_description => "0° indicates coming from north"},
24
+ :wind_wave_height =>{:unit_type => "M", :unit=>"M", :description=>"Height of wind waves as", :unit_description =>"meters"},
25
+ :wind_wave_period =>{:unit_type => "Sec", :unit=>"s", :description=>"Period of wind waves as", :unit_description =>"seconds"},
26
+ :wind_direction =>{:unit_type => "Deg", :unit=>"°", :description=>"Direction of wind.", :unit_description => "0° indicates coming from north"},
27
+ :wind_speed =>{:unit_type => "Ms", :unit=>"M/s", :description=>"Speed of wind as", :unit_description =>"meters per second"}
28
+ }
29
+ end
@@ -0,0 +1,9 @@
1
+ class String
2
+ def underscore
3
+ self.gsub(/::/, '/').
4
+ gsub(/([A-Z]+)([A-Z][a-z])/,'\1_\2').
5
+ gsub(/([a-z\d])([A-Z])/,'\1_\2').
6
+ tr("-", "_").
7
+ downcase
8
+ end
9
+ end
@@ -0,0 +1,21 @@
1
+ class Stormglass::Subvalue < OpenStruct
2
+
3
+ def initialize(src)
4
+ @src = src
5
+ super
6
+ end
7
+
8
+ def to_s
9
+ "#{@src[:value]} #{@src[:unit]}"
10
+ end
11
+
12
+
13
+ def description
14
+ "#{@src[:description]} #{@src[:unit_description]}"
15
+ end
16
+
17
+ def src
18
+ @src
19
+ end
20
+
21
+ end
@@ -0,0 +1,78 @@
1
+ # represents a value for a stormglass prediction source with prefered unit_type
2
+ # contains a collection of Stormglass::Subvalue's, which represent a particular unit of measurement
3
+ # for example, a Stormglass::Value for air_temperature would have Stormglass::Subvalue's for Celsius and Fahrenheit
4
+ class Stormglass::Value
5
+ extend Forwardable
6
+
7
+ def_delegators :preffered_subvalue, :unit, :unit_type, :description, :unit_description
8
+
9
+
10
+ def initialize(attribute, src, data_source, unit_type)
11
+ @attribute = attribute
12
+ @src = src
13
+ @data_source = data_source
14
+ @unit_type = unit_type
15
+ end
16
+
17
+ def data_source
18
+ @data_source
19
+ end
20
+
21
+ def for_source
22
+ @src.collect do |v|
23
+ v['value'] if (!v['source'].nil? && v['source'] == data_source)
24
+ end.compact.first
25
+ end
26
+
27
+ # returns the sources available for this value
28
+ def data_sources
29
+ src.collect{|v| v['source']}
30
+ end
31
+
32
+ def to_s
33
+ preffered_subvalue.to_s
34
+ end
35
+
36
+ def attribute
37
+ @attribute
38
+ end
39
+
40
+ def src
41
+ @src
42
+ end
43
+
44
+ def inspect
45
+ string = "#<#{self.class.to_s} "
46
+ string +="value=#{preffered_subvalue.value}, unit='#{preffered_subvalue.unit}', description='#{preffered_subvalue.description}', "
47
+ string +="unit_type='#{preffered_subvalue.unit_type}', unit_types=#{unit_types.to_s}, data_source='#{@data_source}', data_sources=#{data_sources}>"
48
+ string
49
+ end
50
+
51
+ def unit_types
52
+ subvalues.collect(&:unit_type)
53
+ end
54
+
55
+ def preffered_subvalue
56
+ if @unit_type
57
+ subvalues.find{|subvalue| subvalue.unit_type == @unit_type}
58
+ elsif setting_key = Stormglass.settings.units[@attribute]
59
+ subvalues.find{|subvalue| subvalue.unit_type == setting_key}
60
+ else
61
+ subvalues.first
62
+ end
63
+ end
64
+
65
+ def dict
66
+ Stormglass::RESULT_DICT[attribute]
67
+ end
68
+
69
+ def subvalues
70
+ subvals = []
71
+ raw_val = for_source()
72
+ subvals << {value: raw_val}.merge(dict)
73
+ Stormglass::AlternateValues.perform(subvals)
74
+ subvals.collect{|subvalue| Stormglass::Subvalue.new(subvalue) }
75
+ end
76
+
77
+
78
+ end
@@ -0,0 +1,3 @@
1
+ module Stormglass
2
+ VERSION = "0.1.1"
3
+ end
@@ -0,0 +1,29 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "stormglass/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "stormglass"
8
+ spec.version = Stormglass::VERSION
9
+ spec.authors = ["Russell Jennings"]
10
+ spec.email = ["violentpurr@gmail.com"]
11
+
12
+ spec.summary = %q{Ruby client for the Stormglass weather API}
13
+ spec.homepage = "https://github.com/meesterdude/stormglass"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject do |f|
17
+ f.match(%r{^(test|spec|features)/})
18
+ end
19
+ spec.bindir = "exe"
20
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
21
+ spec.require_paths = ["lib"]
22
+
23
+
24
+ spec.add_dependency("rest-client")
25
+ spec.add_dependency("geocoder")
26
+ spec.add_development_dependency "bundler", "~> 1.16"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest", "~> 5.3"
29
+ end
metadata ADDED
@@ -0,0 +1,141 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: stormglass
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Russell Jennings
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2018-12-14 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rest-client
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
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: geocoder
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.16'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.16'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rake
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '10.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '10.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: minitest
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '5.3'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '5.3'
83
+ description:
84
+ email:
85
+ - violentpurr@gmail.com
86
+ executables: []
87
+ extensions: []
88
+ extra_rdoc_files: []
89
+ files:
90
+ - ".gitignore"
91
+ - ".ruby-version"
92
+ - ".travis.yml"
93
+ - Gemfile
94
+ - Gemfile.lock
95
+ - LICENSE.txt
96
+ - README.md
97
+ - Rakefile
98
+ - bin/console
99
+ - bin/setup
100
+ - lib/stormglass.rb
101
+ - lib/stormglass/alternate_values.rb
102
+ - lib/stormglass/alternate_values/celsius.rb
103
+ - lib/stormglass/alternate_values/degrees.rb
104
+ - lib/stormglass/alternate_values/kgm2.rb
105
+ - lib/stormglass/alternate_values/kilometers.rb
106
+ - lib/stormglass/alternate_values/meters.rb
107
+ - lib/stormglass/alternate_values/meters_sec.rb
108
+ - lib/stormglass/configuration.rb
109
+ - lib/stormglass/hour.rb
110
+ - lib/stormglass/response.rb
111
+ - lib/stormglass/result_dict.rb
112
+ - lib/stormglass/string.rb
113
+ - lib/stormglass/subvalue.rb
114
+ - lib/stormglass/value.rb
115
+ - lib/stormglass/version.rb
116
+ - stormglass.gemspec
117
+ homepage: https://github.com/meesterdude/stormglass
118
+ licenses:
119
+ - MIT
120
+ metadata: {}
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ required_rubygems_version: !ruby/object:Gem::Requirement
131
+ requirements:
132
+ - - ">="
133
+ - !ruby/object:Gem::Version
134
+ version: '0'
135
+ requirements: []
136
+ rubyforge_project:
137
+ rubygems_version: 2.6.13
138
+ signing_key:
139
+ specification_version: 4
140
+ summary: Ruby client for the Stormglass weather API
141
+ test_files: []