forecast_io-cache 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
- checksums.yaml.gz.sig +1 -0
- data/LICENSE.txt +22 -0
- data/README.md +35 -0
- data/features/accessing_a_forecast.feature +32 -0
- data/features/accessing_a_forecast_at_a_specific_time.feature +27 -0
- data/features/accessing_the_api.feature +17 -0
- data/features/step_definitions/api_steps.rb +8 -0
- data/features/step_definitions/cache_steps.rb +18 -0
- data/features/step_definitions/forecast_steps.rb +33 -0
- data/features/support/api.rb +13 -0
- data/features/support/cassettes/forecast_for_latitude_and_longitude.yml +60 -0
- data/features/support/config.rb +9 -0
- data/features/support/env.rb +2 -0
- data/features/support/expect.rb +9 -0
- data/features/support/fixtures.rb +20 -0
- data/features/support/mongo.rb +5 -0
- data/features/support/parse.rb +14 -0
- data/features/support/time.rb +17 -0
- data/features/support/vcr.rb +7 -0
- data/forecast_io-cache.gemspec +36 -0
- data/lib/forecast_io/cache/app.rb +35 -0
- data/lib/forecast_io/cache/configuration.rb +25 -0
- data/lib/forecast_io/cache/forecast.rb +26 -0
- data/lib/forecast_io/cache/forecast_data.rb +37 -0
- data/lib/forecast_io/cache/generate.rb +42 -0
- data/lib/forecast_io/cache/store.rb +50 -0
- data/lib/forecast_io/cache/version.rb +7 -0
- data/lib/forecast_io/cache.rb +32 -0
- data/lib/forecast_io-cache.rb +1 -0
- data/spec/forecast_io/cache/configuration_spec.rb +30 -0
- data/spec/forecast_io/cache/forecast_data_spec.rb +32 -0
- data/spec/forecast_io/cache/forecast_spec.rb +41 -0
- data/spec/forecast_io/cache/generate_spec.rb +34 -0
- data/spec/forecast_io/cache/store_spec.rb +37 -0
- data/spec/forecast_io/cache_spec.rb +15 -0
- data/spec/support/mongo.rb +11 -0
- data.tar.gz.sig +0 -0
- metadata +291 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: ef116f597c9ede4482828322869a00aa395b6393
|
4
|
+
data.tar.gz: ad798a1fe9c3277bbba85b9d5491bacf9dae46c3
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: c7cc51e0c2d98fae59f8b8b93d15272ae44e1c9c57c9ba5904043a248dacc007c20a058b2f81c010f36364693a50846aea47d39c36c258bd60aec8cf586319f8
|
7
|
+
data.tar.gz: 3d22fc5b23f791948db6277bcadd6ac6a7b085feef49ea093680a27df3bb327806966c721ca9c7c6294bd5c279266ef04d0fdd8b0c51cbae60f6450c1e7d8ca5
|
checksums.yaml.gz.sig
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
����_�h#(��M�/}�Tl�BRc��S��/��Z���aNj��oDgi�R)�IH]�g�Ξ��V#FGJ����Vwϑ�:�"�e<L>���%�u_�덲��x ��,P���X`(���Xĝ�0����U����" P�k~�nԭ9!^�qX 1.����s!�ŧ�ȫd���3���`-��˓%#�� 0��5�/��o��K��o�f����g��L>�7�5���b�b���s��;9��B�J�B>
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Jon Rowe
|
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,35 @@
|
|
1
|
+
# Forecast::IO::Cache
|
2
|
+
|
3
|
+
A local caching setup for [Forecast.IO](https://developer.forecast.io)
|
4
|
+
|
5
|
+
## Installation
|
6
|
+
|
7
|
+
Add this line to your application's Gemfile:
|
8
|
+
|
9
|
+
gem 'forecast_io-cache'
|
10
|
+
|
11
|
+
And then execute:
|
12
|
+
|
13
|
+
$ bundle
|
14
|
+
|
15
|
+
Or install it yourself as:
|
16
|
+
|
17
|
+
$ gem install forecast_io-cache
|
18
|
+
|
19
|
+
## Usage
|
20
|
+
|
21
|
+
Load the gem and then rackup the service.
|
22
|
+
|
23
|
+
use ForecastIO::Cache
|
24
|
+
|
25
|
+
or
|
26
|
+
|
27
|
+
run ForecastIO::Cache
|
28
|
+
|
29
|
+
## Contributing
|
30
|
+
|
31
|
+
1. Fork it
|
32
|
+
2. Create your feature branch (`git checkout -b my-new-feature`)
|
33
|
+
3. Commit your changes (`git commit -am 'Add some feature'`)
|
34
|
+
4. Push to the branch (`git push origin my-new-feature`)
|
35
|
+
5. Create new Pull Request
|
@@ -0,0 +1,32 @@
|
|
1
|
+
Feature: Accessing a forecast
|
2
|
+
In order to provide weather information to my users
|
3
|
+
I want to access a forecast for a specific location
|
4
|
+
|
5
|
+
@time-frozen
|
6
|
+
Scenario: Not cached
|
7
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823'
|
8
|
+
Then I am given the forecast as returned from forecast.io
|
9
|
+
|
10
|
+
@time-frozen
|
11
|
+
Scenario: Cached
|
12
|
+
Given I have cached this forecast recently
|
13
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823'
|
14
|
+
Then I am given the cached forecast
|
15
|
+
|
16
|
+
@time-frozen
|
17
|
+
Scenario: Cached nearby
|
18
|
+
Given I have a cache result nearby
|
19
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823'
|
20
|
+
Then I am given the cached forecast
|
21
|
+
|
22
|
+
@time-frozen
|
23
|
+
Scenario: Cached close but beyond radius
|
24
|
+
Given I have a cache result for close by
|
25
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823'
|
26
|
+
Then I am given the forecast as returned from forecast.io
|
27
|
+
|
28
|
+
@time-frozen
|
29
|
+
Scenario: Cache expired
|
30
|
+
Given I have a cached result but for a time beyond the threshold
|
31
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823'
|
32
|
+
Then I am given the forecast as returned from forecast.io
|
@@ -0,0 +1,27 @@
|
|
1
|
+
Feature: Accessing a forecast for a specific time
|
2
|
+
In order to provide weather information to my users
|
3
|
+
I want to access a forecast for a specific location at a specific time
|
4
|
+
|
5
|
+
Scenario: Not cached
|
6
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823,123456789'
|
7
|
+
Then I am given the forecast as returned from forecast.io
|
8
|
+
|
9
|
+
Scenario: Cached
|
10
|
+
Given I have cached this forecast recently
|
11
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823,123456789'
|
12
|
+
Then I am given the cached forecast
|
13
|
+
|
14
|
+
Scenario: Cached nearby
|
15
|
+
Given I have a cache result nearby
|
16
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823,123456789'
|
17
|
+
Then I am given the cached forecast
|
18
|
+
|
19
|
+
Scenario: Cached close but beyond radius
|
20
|
+
Given I have a cache result for close by
|
21
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823,123456789'
|
22
|
+
Then I am given the forecast as returned from forecast.io
|
23
|
+
|
24
|
+
Scenario: Cache expired
|
25
|
+
Given I have a cached result but for a time beyond the threshold
|
26
|
+
When I request '/forecast/API_KEY/-33.857444,151.238823,123456789'
|
27
|
+
Then I am given the forecast as returned from forecast.io
|
@@ -0,0 +1,17 @@
|
|
1
|
+
Feature: Accessing the api
|
2
|
+
In order to provide information about the api
|
3
|
+
When I access the root of the api I want to receive hypermedia information
|
4
|
+
|
5
|
+
Scenario: Access /
|
6
|
+
When I request '/'
|
7
|
+
Then I am given the json response detailing the available actions:
|
8
|
+
"""
|
9
|
+
{
|
10
|
+
"actions":
|
11
|
+
{
|
12
|
+
"forecast": "/forecast/:key/:latitude,:longitude",
|
13
|
+
"forecast_at_time": "/forecast/:key/:latitude,:longitude,:time"
|
14
|
+
},
|
15
|
+
"status": "Not Found"
|
16
|
+
}
|
17
|
+
"""
|
@@ -0,0 +1,18 @@
|
|
1
|
+
Given "I have cached this forecast recently" do
|
2
|
+
@position = [151.238823,-33.857444]
|
3
|
+
setup "position" => @position, "time" => 123456789
|
4
|
+
end
|
5
|
+
|
6
|
+
Given "I have a cache result nearby" do
|
7
|
+
@position = [151.238823,-33.857333]
|
8
|
+
setup "position" => @position, "time" => 123456789
|
9
|
+
end
|
10
|
+
|
11
|
+
Given "I have a cache result for close by" do
|
12
|
+
@position = [151.238823,-33.957333]
|
13
|
+
setup "position" => @position, "time" => 123456789
|
14
|
+
end
|
15
|
+
|
16
|
+
Given "I have a cached result but for a time beyond the threshold" do
|
17
|
+
setup "position" => [151.238823,-33.757444], "time" => 1234567890
|
18
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'forecast_io'
|
2
|
+
|
3
|
+
Then "I am given the forecast as returned from forecast.io" do
|
4
|
+
VCR.use_cassette 'forecast_for_latitude_and_longitude' do
|
5
|
+
expect_ok { get @path }
|
6
|
+
end
|
7
|
+
expect(parsed_response).to eq({
|
8
|
+
"time" => 123456789,
|
9
|
+
"latitude" => -33.857444,
|
10
|
+
"longitude" => 151.238823,
|
11
|
+
"wind_speed" => 1.87,
|
12
|
+
"wind_bearing" => 186,
|
13
|
+
"humidity" => 0.7,
|
14
|
+
"pressure" => 1013.1,
|
15
|
+
"visibility" => 10,
|
16
|
+
"temperature" => 68.47,
|
17
|
+
})
|
18
|
+
end
|
19
|
+
|
20
|
+
Then "I am given the cached forecast" do
|
21
|
+
expect_ok { get @path }
|
22
|
+
expect(parsed_response).to eq({
|
23
|
+
"time" => 123456789,
|
24
|
+
"latitude" => @position[1],
|
25
|
+
"longitude" => @position[0],
|
26
|
+
"wind_speed" => 6.66,
|
27
|
+
"wind_bearing" => 200,
|
28
|
+
"humidity" => 0.9,
|
29
|
+
"pressure" => 1015.1,
|
30
|
+
"visibility" => 13,
|
31
|
+
"temperature" => 68.72
|
32
|
+
})
|
33
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
---
|
2
|
+
http_interactions:
|
3
|
+
- request:
|
4
|
+
method: get
|
5
|
+
uri: https://api.forecast.io/forecast/API_KEY/-33.857444,151.238823,123456789
|
6
|
+
body:
|
7
|
+
encoding: US-ASCII
|
8
|
+
string: ''
|
9
|
+
headers:
|
10
|
+
User-Agent:
|
11
|
+
- Faraday v0.8.7
|
12
|
+
response:
|
13
|
+
status:
|
14
|
+
code: 200
|
15
|
+
message:
|
16
|
+
headers:
|
17
|
+
server:
|
18
|
+
- nginx/1.1.19
|
19
|
+
date:
|
20
|
+
- Thu, 02 May 2013 09:51:16 GMT
|
21
|
+
content-type:
|
22
|
+
- application/json; charset=utf-8
|
23
|
+
transfer-encoding:
|
24
|
+
- chunked
|
25
|
+
connection:
|
26
|
+
- close
|
27
|
+
x-powered-by:
|
28
|
+
- Express
|
29
|
+
cache-control:
|
30
|
+
- max-age=86400
|
31
|
+
expires:
|
32
|
+
- Fri, 03 May 2013 09:51:16 +0000
|
33
|
+
etag:
|
34
|
+
- '"-102868148"'
|
35
|
+
x-response-time:
|
36
|
+
- 113ms
|
37
|
+
body:
|
38
|
+
encoding: UTF-8
|
39
|
+
string: '{"latitude":-33.857444,"longitude":151.238823,"timezone":"Australia/Sydney","offset":11,"currently":{"time":123456789,"summary":"Partly
|
40
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":68.47,"dewPoint":58.01,"windSpeed":1.87,"windBearing":186,"cloudCover":0.25,"humidity":0.7,"pressure":1013.1,"visibility":10},"hourly":{"summary":"Partly
|
41
|
+
cloudy throughout the day.","icon":"partly-cloudy-day","data":[{"time":123426000,"summary":"Clear","icon":"clear-night","precipType":"rain","temperature":64.24,"dewPoint":53.66,"windSpeed":6.74,"windBearing":299,"cloudCover":0,"humidity":0.69},{"time":123429600,"summary":"Clear","icon":"clear-night","precipType":"rain","temperature":63.69,"dewPoint":53.33,"windSpeed":4.99,"windBearing":269,"cloudCover":0,"humidity":0.69},{"time":123433200,"summary":"Clear","icon":"clear-night","precipType":"rain","temperature":63.49,"dewPoint":52.61,"windSpeed":3.49,"windBearing":210,"cloudCover":0,"humidity":0.68},{"time":123436800,"summary":"Clear","icon":"clear-night","precipType":"rain","temperature":62.99,"dewPoint":52.07,"windSpeed":2.24,"windBearing":180,"cloudCover":0,"humidity":0.68},{"time":123440400,"summary":"Clear","icon":"clear-night","precipType":"rain","temperature":59,"dewPoint":52.15,"windSpeed":0,"cloudCover":0,"humidity":0.78,"pressure":1011.4,"visibility":10},{"time":123444000,"summary":"Clear","icon":"clear-night","precipType":"rain","temperature":59.58,"dewPoint":54.04,"windSpeed":0.62,"windBearing":293,"cloudCover":0.02,"humidity":0.82,"pressure":1011.4,"visibility":10},{"time":123447600,"summary":"Clear","icon":"clear-day","precipType":"rain","temperature":60.26,"dewPoint":55.72,"windSpeed":1.34,"windBearing":293,"cloudCover":0.07,"humidity":0.85},{"time":123451200,"summary":"Partly
|
42
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":57,"dewPoint":54.09,"windSpeed":0,"cloudCover":0.31,"humidity":0.9,"pressure":1013.1,"visibility":10},{"time":123454800,"summary":"Partly
|
43
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":64.24,"dewPoint":56.64,"windSpeed":1.12,"windBearing":186,"cloudCover":0.29,"humidity":0.78,"pressure":1013.1,"visibility":10},{"time":123458400,"summary":"Partly
|
44
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":71.89,"dewPoint":59.11,"windSpeed":2.48,"windBearing":186,"cloudCover":0.21,"humidity":0.64},{"time":123462000,"summary":"Clear","icon":"clear-day","precipType":"rain","temperature":72,"dewPoint":56.88,"windSpeed":3,"windBearing":210,"cloudCover":0,"humidity":0.59,"pressure":1016,"visibility":10},{"time":123465600,"summary":"Clear","icon":"clear-day","precipType":"rain","temperature":74,"dewPoint":56.54,"windSpeed":5.89,"windBearing":152,"cloudCover":0.14,"humidity":0.55,"pressure":1016,"visibility":10},{"time":123469200,"summary":"Partly
|
45
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":75.87,"dewPoint":56.21,"windSpeed":8.07,"windBearing":90,"cloudCover":0.31,"humidity":0.51},{"time":123472800,"summary":"Partly
|
46
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":75,"dewPoint":53.96,"windSpeed":3,"windBearing":90,"cloudCover":0.31,"humidity":0.48,"pressure":1016.1,"visibility":10},{"time":123476400,"summary":"Partly
|
47
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":75.28,"dewPoint":56.24,"windSpeed":8.45,"windBearing":88,"cloudCover":0.31,"humidity":0.52,"pressure":1016.1,"visibility":10},{"time":123480000,"summary":"Partly
|
48
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":75.82,"dewPoint":58.6,"windSpeed":13.8,"windBearing":86,"cloudCover":0.31,"humidity":0.55},{"time":123483600,"summary":"Partly
|
49
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":77,"dewPoint":56.92,"windSpeed":8,"windBearing":80,"cloudCover":0.31,"humidity":0.5,"pressure":1015.6,"visibility":10},{"time":123487200,"summary":"Partly
|
50
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":75.2,"dewPoint":57.66,"windSpeed":9.92,"windBearing":87,"cloudCover":0.31,"humidity":0.55,"pressure":1015.6,"visibility":10},{"time":123490800,"summary":"Partly
|
51
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":73,"dewPoint":59.2,"windSpeed":11.98,"windBearing":90,"cloudCover":0.31,"humidity":0.62},{"time":123494400,"summary":"Partly
|
52
|
+
Cloudy","icon":"partly-cloudy-day","precipType":"rain","temperature":72.15,"dewPoint":60.69,"windSpeed":10.94,"windBearing":78,"cloudCover":0.31,"humidity":0.67},{"time":123498000,"summary":"Partly
|
53
|
+
Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":71.52,"dewPoint":62.46,"windSpeed":8.93,"windBearing":59,"cloudCover":0.31,"humidity":0.73},{"time":123501600,"summary":"Partly
|
54
|
+
Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":71,"dewPoint":63.87,"windSpeed":7.23,"windBearing":45,"cloudCover":0.31,"humidity":0.78},{"time":123505200,"summary":"Partly
|
55
|
+
Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":70.6,"dewPoint":64.68,"windSpeed":6.49,"windBearing":43,"cloudCover":0.31,"humidity":0.81},{"time":123508800,"summary":"Partly
|
56
|
+
Cloudy","icon":"partly-cloudy-night","precipType":"rain","temperature":70.31,"dewPoint":65.15,"windSpeed":6.04,"windBearing":41,"cloudCover":0.31,"humidity":0.84}]},"daily":{"data":[{"time":123426000,"summary":"Partly
|
57
|
+
cloudy throughout the day.","icon":"partly-cloudy-day","sunriseTime":123446333,"sunsetTime":123497495,"temperatureMin":57,"temperatureMinTime":123451200,"temperatureMax":77,"temperatureMaxTime":123483600,"dewPoint":57.19,"windSpeed":5.62,"windBearing":100,"cloudCover":0.2,"humidity":0.67,"pressure":1014.44,"visibility":10}]},"flags":{"sources":["isd"],"isd-stations":["947650-99999","947670-99999","947675-99999","947680-99999","949999-00242"],"units":"us"}}'
|
58
|
+
http_version:
|
59
|
+
recorded_at: Thu, 02 May 2013 09:51:16 GMT
|
60
|
+
recorded_with: VCR 2.4.0
|
@@ -0,0 +1,20 @@
|
|
1
|
+
require './features/support/mongo'
|
2
|
+
module Fixtures
|
3
|
+
|
4
|
+
def setup attrs = {}
|
5
|
+
MongoAdaptor.db.collection('forecasts').insert(
|
6
|
+
{
|
7
|
+
"time" => 123456789,
|
8
|
+
"position" => [151.238823,-33.857444],
|
9
|
+
"wind_speed" => 6.66,
|
10
|
+
"wind_bearing" => 200,
|
11
|
+
"humidity" => 0.9,
|
12
|
+
"pressure" => 1015.1,
|
13
|
+
"visibility" => 13,
|
14
|
+
"temperature" => 68.72
|
15
|
+
}.merge(attrs)
|
16
|
+
)
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
World Fixtures
|
@@ -0,0 +1,17 @@
|
|
1
|
+
class Time
|
2
|
+
class << self
|
3
|
+
attr_accessor :stub_to
|
4
|
+
|
5
|
+
alias __now now
|
6
|
+
def now
|
7
|
+
stub_to || __now
|
8
|
+
end
|
9
|
+
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Around '@time-frozen' do |scenario, block|
|
14
|
+
Time.stub_to = Time.at(123456789)
|
15
|
+
block.call
|
16
|
+
Time.stub_to = nil
|
17
|
+
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'forecast_io/cache/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = "forecast_io-cache"
|
8
|
+
spec.version = Forecast::IO::Cache::VERSION
|
9
|
+
spec.authors = ["Jon Rowe"]
|
10
|
+
spec.email = ["hello@jonrowe.co.uk"]
|
11
|
+
spec.description = %q{Caching layer for forecast_io}
|
12
|
+
spec.summary = %q{Caching layer for forecast_io}
|
13
|
+
spec.homepage = "https://github.com/Tacktical/forecast_io-cache"
|
14
|
+
spec.license = "MIT"
|
15
|
+
|
16
|
+
spec.files = `git ls-files lib/** spec/** features/** LICENSE.txt README.md forecast_io-cache.gemspec`.split($/)
|
17
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
18
|
+
spec.test_files = spec.files.grep(%r{^(spec|features)/})
|
19
|
+
spec.require_paths = ["lib"]
|
20
|
+
|
21
|
+
spec.add_runtime_dependency "forecast_io"
|
22
|
+
spec.add_runtime_dependency "sinatra"
|
23
|
+
spec.add_runtime_dependency "mongo_adaptor"
|
24
|
+
|
25
|
+
spec.add_development_dependency "bundler", "~> 1.3"
|
26
|
+
spec.add_development_dependency "rake"
|
27
|
+
spec.add_development_dependency "rspec"
|
28
|
+
spec.add_development_dependency "cucumber"
|
29
|
+
spec.add_development_dependency "rack-test"
|
30
|
+
spec.add_development_dependency "vcr"
|
31
|
+
|
32
|
+
spec.add_development_dependency "mongo"
|
33
|
+
spec.add_development_dependency "bson"
|
34
|
+
spec.add_development_dependency "bson_ext"
|
35
|
+
|
36
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'sinatra/base'
|
3
|
+
|
4
|
+
module Forecast
|
5
|
+
module IO
|
6
|
+
module Cache
|
7
|
+
class App < Sinatra::Base
|
8
|
+
LATITUDE = LONGITUDE = /(-?\d+(?:.\d+))/
|
9
|
+
TIME = /(\d+)/
|
10
|
+
|
11
|
+
get %r[/forecast/#{ENV['API_KEY']}/#{LATITUDE},#{LONGITUDE}(?:,#{TIME})?], provides: :json do |latitude,longitude,time|
|
12
|
+
respond_as_json 200, Forecast.new.for(latitude.to_f, longitude.to_f, (time || Time.now).to_i).to_json
|
13
|
+
end
|
14
|
+
|
15
|
+
not_found do
|
16
|
+
respond_as_json 404, status: 'Not Found', actions: actions
|
17
|
+
end
|
18
|
+
|
19
|
+
private
|
20
|
+
|
21
|
+
def respond_as_json code, body
|
22
|
+
[code, { "Content-Type" => "application/json" }, JSON.dump(body) ]
|
23
|
+
end
|
24
|
+
|
25
|
+
def actions
|
26
|
+
{
|
27
|
+
forecast: "/forecast/:key/:latitude,:longitude",
|
28
|
+
forecast_at_time: "/forecast/:key/:latitude,:longitude,:time"
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'mongo-configure'
|
2
|
+
|
3
|
+
module Forecast
|
4
|
+
module IO
|
5
|
+
module Cache
|
6
|
+
class Configuration
|
7
|
+
|
8
|
+
attr_writer :radius, :timeframe
|
9
|
+
|
10
|
+
def radius
|
11
|
+
@radius ||= 5
|
12
|
+
end
|
13
|
+
|
14
|
+
def timeframe
|
15
|
+
@timeframe ||= 60
|
16
|
+
end
|
17
|
+
|
18
|
+
def mongo_uri uri
|
19
|
+
Mongo::Configure.from_uri uri
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Forecast
|
2
|
+
module IO
|
3
|
+
module Cache
|
4
|
+
class Forecast
|
5
|
+
|
6
|
+
def initialize cache = Store.new, generate = Generate
|
7
|
+
@cache, @generate = cache, generate
|
8
|
+
end
|
9
|
+
|
10
|
+
def for lat, lon, time
|
11
|
+
cached(lat, lon, time) or fetch_and_store(lat, lon, time)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def cached lat, lon, time
|
16
|
+
@cache.fetch lat, lon, time
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch_and_store lat, lon, time
|
20
|
+
@generate.for lat, lon, time, @cache
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
module Forecast
|
2
|
+
module IO
|
3
|
+
module Cache
|
4
|
+
class ForecastData < Struct.new(:position, :time, :wind_speed, :wind_bearing,
|
5
|
+
:humidity, :pressure, :visibility, :temperature)
|
6
|
+
|
7
|
+
def self.generate lat, lon, time, forecast
|
8
|
+
new.tap do |data|
|
9
|
+
data.time = time
|
10
|
+
data.position = [lon,lat].map(&:to_f)
|
11
|
+
data.wind_speed = forecast['windSpeed']
|
12
|
+
data.wind_bearing = forecast['windBearing']
|
13
|
+
data.humidity = forecast['humidity']
|
14
|
+
data.pressure = forecast['pressure']
|
15
|
+
data.visibility = forecast['visibility']
|
16
|
+
data.temperature = forecast['temperature']
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_json
|
21
|
+
{
|
22
|
+
time: time,
|
23
|
+
latitude: position[1],
|
24
|
+
longitude: position[0],
|
25
|
+
wind_speed: wind_speed,
|
26
|
+
wind_bearing: wind_bearing,
|
27
|
+
humidity: humidity,
|
28
|
+
pressure: pressure,
|
29
|
+
visibility: visibility,
|
30
|
+
temperature: temperature
|
31
|
+
}
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'forecast_io/cache/forecast_data'
|
2
|
+
|
3
|
+
module Forecast
|
4
|
+
module IO
|
5
|
+
module Cache
|
6
|
+
class Generate
|
7
|
+
|
8
|
+
def self.for lat, lon, time, cache
|
9
|
+
new(lat, lon, time, cache).forecasts.first
|
10
|
+
end
|
11
|
+
|
12
|
+
def initialize lat, lon, time, cache, api = ::Forecast::IO
|
13
|
+
@lat, @lon, @time, @cache, @api = lat, lon, time, cache, api
|
14
|
+
end
|
15
|
+
|
16
|
+
def forecasts
|
17
|
+
([current] + hourly).map do |forecast|
|
18
|
+
@cache.store generate forecast
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
private
|
23
|
+
def current
|
24
|
+
forecast["currently"]
|
25
|
+
end
|
26
|
+
|
27
|
+
def hourly
|
28
|
+
forecast["hourly"]["data"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def forecast
|
32
|
+
@forecast ||= @api.forecast @lat, @lon, time: @time
|
33
|
+
end
|
34
|
+
|
35
|
+
def generate data
|
36
|
+
ForecastData.generate @lat, @lon, @time, data
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,50 @@
|
|
1
|
+
require 'mongo_adaptor'
|
2
|
+
require 'forecast_io/cache/forecast_data'
|
3
|
+
|
4
|
+
module Forecast
|
5
|
+
module IO
|
6
|
+
module Cache
|
7
|
+
class Store
|
8
|
+
|
9
|
+
def initialize config = Cache.configuration
|
10
|
+
@config = config
|
11
|
+
@backend = MongoAdaptor.new('forecasts',ForecastData)
|
12
|
+
end
|
13
|
+
|
14
|
+
def fetch lat, lon, time
|
15
|
+
@backend.fetch 'position' => { '$near' => [lon,lat], '$maxDistance' => radius },
|
16
|
+
'time' => { '$lt' => time+offset, '$gt' => time-offset }
|
17
|
+
rescue Mongo::OperationFailure => error
|
18
|
+
if index_error? error
|
19
|
+
ensure_index!
|
20
|
+
retry
|
21
|
+
else
|
22
|
+
raise
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def store forecast
|
27
|
+
@backend.insert forecast
|
28
|
+
forecast
|
29
|
+
end
|
30
|
+
|
31
|
+
private
|
32
|
+
def radius
|
33
|
+
@config.radius * (1/111.694)
|
34
|
+
end
|
35
|
+
|
36
|
+
def offset
|
37
|
+
@config.timeframe*60
|
38
|
+
end
|
39
|
+
|
40
|
+
def index_error? error
|
41
|
+
error.message =~ /can't find any special indices/
|
42
|
+
end
|
43
|
+
|
44
|
+
def ensure_index!
|
45
|
+
MongoAdaptor.db.collection('forecasts').ensure_index position: Mongo::GEO2D
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'forecast_io'
|
2
|
+
require "forecast_io/cache/version"
|
3
|
+
require "forecast_io/cache/configuration"
|
4
|
+
require "forecast_io/cache/forecast"
|
5
|
+
require "forecast_io/cache/store"
|
6
|
+
require "forecast_io/cache/generate"
|
7
|
+
require "forecast_io/cache/app"
|
8
|
+
|
9
|
+
module Forecast
|
10
|
+
module IO
|
11
|
+
module Cache
|
12
|
+
|
13
|
+
def self.configure
|
14
|
+
yield configuration
|
15
|
+
end
|
16
|
+
def self.configuration
|
17
|
+
@config ||= Configuration.new
|
18
|
+
end
|
19
|
+
|
20
|
+
|
21
|
+
def self.initialize!
|
22
|
+
@app ||= App.new
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.call env
|
26
|
+
initialize!
|
27
|
+
@app.call env
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'forecast_io/cache'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'forecast_io/cache/configuration'
|
2
|
+
|
3
|
+
describe 'service configuration' do
|
4
|
+
|
5
|
+
let(:config) { Forecast::IO::Cache::Configuration.new }
|
6
|
+
|
7
|
+
it 'has a radius default of 5' do
|
8
|
+
expect(config.radius).to eq 5
|
9
|
+
end
|
10
|
+
|
11
|
+
it 'has a configurable radius' do
|
12
|
+
config.radius = 12
|
13
|
+
expect(config.radius).to eq 12
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'has a default timeframe of 60' do
|
17
|
+
expect(config.timeframe).to eq 60
|
18
|
+
end
|
19
|
+
|
20
|
+
it 'has a configurable timeframe' do
|
21
|
+
config.timeframe = 12
|
22
|
+
expect(config.timeframe).to eq 12
|
23
|
+
end
|
24
|
+
|
25
|
+
it 'loads mongo config urls' do
|
26
|
+
Mongo::Configure.should_receive(:from_uri).with 'mongodb://example.com'
|
27
|
+
config.mongo_uri 'mongodb://example.com'
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'forecast_io/cache/forecast_data'
|
2
|
+
|
3
|
+
describe 'representing data from the api' do
|
4
|
+
subject { Forecast::IO::Cache::ForecastData.generate "1.1", "-1.2", time, data }
|
5
|
+
|
6
|
+
let(:data) do
|
7
|
+
{
|
8
|
+
'windSpeed' => wind_speed,
|
9
|
+
'windBearing' => wind_bearing,
|
10
|
+
'humidity' => humidity,
|
11
|
+
'temperature' => temperature,
|
12
|
+
'pressure' => pressure,
|
13
|
+
'visibility' => visibility,
|
14
|
+
'percipIntensity' => percip_intensity
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
%W[time wind_speed wind_bearing humidity temperature pressure visibility percip_intensity].each do |name|
|
19
|
+
let(name) { double name }
|
20
|
+
end
|
21
|
+
|
22
|
+
its(:time) { should eq time }
|
23
|
+
its(:position) { should eq [-1.2,1.1] }
|
24
|
+
its(:wind_speed) { wind_speed }
|
25
|
+
its(:wind_bearing) { wind_bearing }
|
26
|
+
its(:humidity) { humidity }
|
27
|
+
its(:temperature) { temperature }
|
28
|
+
its(:pressure) { pressure }
|
29
|
+
its(:visibility) { visibility }
|
30
|
+
its(:precip_intensity) { percip_intensity }
|
31
|
+
|
32
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
require 'forecast_io/cache/forecast'
|
2
|
+
|
3
|
+
describe 'producing a forecast' do
|
4
|
+
|
5
|
+
let(:forecast) { Forecast::IO::Cache::Forecast.new store, generate }
|
6
|
+
|
7
|
+
%w[generate lat lon time data store].map do |name|
|
8
|
+
let(name) { double name }
|
9
|
+
end
|
10
|
+
|
11
|
+
context 'when cached data exists' do
|
12
|
+
before { store.stub(:fetch).and_return data }
|
13
|
+
|
14
|
+
it 'checks the store for a previous cached result' do
|
15
|
+
store.should_receive(:fetch).with(lat, lon, time)
|
16
|
+
forecast.for lat, lon, time
|
17
|
+
end
|
18
|
+
|
19
|
+
it 'returns the cached result when it exists' do
|
20
|
+
expect(forecast.for lat, lon, time).to eq data
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
context 'when cached data doesnt exist' do
|
25
|
+
before do
|
26
|
+
generate.stub(:for).and_return data
|
27
|
+
store.stub(:fetch)
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'checks the store for a previous cached result but then calls the generate' do
|
31
|
+
store.should_receive(:fetch).with(lat, lon, time)
|
32
|
+
generate.should_receive(:for).with(lat, lon, time, store)
|
33
|
+
forecast.for lat, lon, time
|
34
|
+
end
|
35
|
+
|
36
|
+
it 'returns the stored result' do
|
37
|
+
expect(forecast.for lat, lon, time).to eq data
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'forecast_io/cache/generate'
|
2
|
+
|
3
|
+
describe 'generating new cached results' do
|
4
|
+
|
5
|
+
let(:generate) { Forecast::IO::Cache::Generate.new lat, lon, time, cache, api }
|
6
|
+
let(:api) { double "api", forecast: data }
|
7
|
+
let(:data) { { "currently" => "data_1", "hourly" => { "data" => ["data_2","data_3"] } } }
|
8
|
+
|
9
|
+
%w[lat lon time data_1 data_2 data_3 cache].each do |name|
|
10
|
+
let(name) { double name }
|
11
|
+
end
|
12
|
+
|
13
|
+
before do
|
14
|
+
Forecast::IO::Cache::ForecastData.stub(:generate) { |lat,lon,time,data| send data }
|
15
|
+
cache.stub(:store) { |data| data }
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'gets the forecast from the api' do
|
19
|
+
api.should_receive(:forecast).with(lat,lon,time: time)
|
20
|
+
generate.forecasts
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'stores the current forecast and all the hourlies' do
|
24
|
+
cache.should_receive(:store).with(data_1)
|
25
|
+
cache.should_receive(:store).with(data_2)
|
26
|
+
cache.should_receive(:store).with(data_3)
|
27
|
+
generate.forecasts
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'returns the forecasts' do
|
31
|
+
expect(generate.forecasts).to eq [data_1,data_2,data_3]
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'support/mongo'
|
2
|
+
require 'forecast_io/cache/store'
|
3
|
+
|
4
|
+
describe database: true do
|
5
|
+
let(:config) { double radius: 1, timeframe: 3 }
|
6
|
+
let(:store) { Forecast::IO::Cache::Store.new config }
|
7
|
+
let(:data) { Forecast::IO::Cache::ForecastData }
|
8
|
+
|
9
|
+
describe 'retrieving forecasts from the store' do
|
10
|
+
let(:forecast_1) { data.new [151.23775,-33.858264], 1 }
|
11
|
+
let(:forecast_2) { data.new [151.23675,-33.858664], 3 }
|
12
|
+
|
13
|
+
before do
|
14
|
+
store.store forecast_1
|
15
|
+
store.store forecast_2
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'will return the closet forecast' do
|
19
|
+
expect(store.fetch -33.858254, 151.23775, 3).to eq forecast_1
|
20
|
+
end
|
21
|
+
it 'will return a forecast nearby' do
|
22
|
+
expect(store.fetch -33.858254, 151.23780, 2).to eq forecast_1
|
23
|
+
end
|
24
|
+
it 'will not return a forecast outside the time params' do
|
25
|
+
expect(store.fetch -33.858254, 151.23780, 200).to eq nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
describe 'inserting forecasts into the store' do
|
30
|
+
let(:forecast) { data.new [151.23775,-33.858264], 1 }
|
31
|
+
|
32
|
+
it 'returns the forecast' do
|
33
|
+
expect(store.store forecast).to eq forecast
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
@@ -0,0 +1,15 @@
|
|
1
|
+
require 'forecast_io/cache'
|
2
|
+
|
3
|
+
describe 'Forecast::IO caching service' do
|
4
|
+
|
5
|
+
it 'is configurable by a block' do
|
6
|
+
Forecast::IO::Cache.configure { |config| @config = config }
|
7
|
+
expect(@config).to be_a Forecast::IO::Cache::Configuration
|
8
|
+
end
|
9
|
+
|
10
|
+
it 'is retrievable' do
|
11
|
+
Forecast::IO::Cache.configure { |config| @config = config }
|
12
|
+
expect(Forecast::IO::Cache.configuration).to eq @config
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
require 'mongo-configure'
|
2
|
+
|
3
|
+
Mongo::Configure.from_database 'forecast-test-spec'
|
4
|
+
|
5
|
+
RSpec.configure do |config|
|
6
|
+
|
7
|
+
config.after :each, database: true do
|
8
|
+
Mongo::Configure.current.load.collections.select { |c| c.name !~ /^system\./ }.each &:remove
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
data.tar.gz.sig
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,291 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: forecast_io-cache
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Jon Rowe
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDVjCCAj6gAwIBAgIBADANBgkqhkiG9w0BAQUFADBRMQ4wDAYDVQQDDAVoZWxs
|
14
|
+
bzEXMBUGCgmSJomT8ixkARkWB2pvbnJvd2UxEjAQBgoJkiaJk/IsZAEZFgJjbzES
|
15
|
+
MBAGCgmSJomT8ixkARkWAnVrMB4XDTEzMDIwMzA4MTgwNloXDTE0MDIwMzA4MTgw
|
16
|
+
NlowUTEOMAwGA1UEAwwFaGVsbG8xFzAVBgoJkiaJk/IsZAEZFgdqb25yb3dlMRIw
|
17
|
+
EAYKCZImiZPyLGQBGRYCY28xEjAQBgoJkiaJk/IsZAEZFgJ1azCCASIwDQYJKoZI
|
18
|
+
hvcNAQEBBQADggEPADCCAQoCggEBAMhs6ng/SRrYfG7RtQx8liJTZs8tpz7PBnlH
|
19
|
+
qyOwuU0weJc7nh6C9C8LGyJzpkbjJJo1rfTMg7huDyL14Py82dfMDomApif8jNNI
|
20
|
+
8KtviAgB1DrWq0fCDLtu/M77+yuVV3OhDdrAFaBkT/euvdJ8cAKrLxbJ+klgvrcB
|
21
|
+
FK+c4PUV3/zBKghe0l7FuDhyQCsuLNDbWyFvDS97tPjeN6yWuszwg22vZMDdsuzN
|
22
|
+
Cj3M4LLSkbrt+AOUcysEJxI4t6uv2U1bRzHsDfAF0RI/Q7OMtUr+Dtz/2YJ47KKs
|
23
|
+
51ZRjLLGHd10XrIfFSfGyJj1dMbDgLsEBj1sFH4e6dy7gau8TaUCAwEAAaM5MDcw
|
24
|
+
CQYDVR0TBAIwADAdBgNVHQ4EFgQULu5JH2g7RAjoXaZt+fbrfNDI9IkwCwYDVR0P
|
25
|
+
BAQDAgSwMA0GCSqGSIb3DQEBBQUAA4IBAQAf5A4kB769DPKGjPZ++v42FwVi7X7v
|
26
|
+
RpufPs6R4YHyzHXaJmAqnhleZhVJijBgsdb2SigNRbg+IK8XYHg7jkonMgO8OS3D
|
27
|
+
C6w8VB5bI0PqyDOwCGcQkYHYlZZWCghAyBTSBowHAekMb9V3QjJtJ8XkizjshETO
|
28
|
+
ZCVI2AObjlJi8I10aK2tSo9sv2riCKZ92BVSM13zYWn+C/eCP/m9BDiw37UQtuQq
|
29
|
+
2feWfO4gCNmvfFjULOAYHq9JHEjN5SLSXvj5HdSnDcCyIfJKn5Ya3JahWQaWIsXf
|
30
|
+
/NPE/mB57TOwj+d7XUa2NC4HUadF8R51IYEcIB0PpIEzJlKtfXFcOZxO
|
31
|
+
-----END CERTIFICATE-----
|
32
|
+
date: 2013-05-02 00:00:00.000000000 Z
|
33
|
+
dependencies:
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: forecast_io
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - '>='
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '0'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: sinatra
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: mongo_adaptor
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - '>='
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0'
|
69
|
+
type: :runtime
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - '>='
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: bundler
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ~>
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.3'
|
83
|
+
type: :development
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ~>
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '1.3'
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: rake
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - '>='
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0'
|
97
|
+
type: :development
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - '>='
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: rspec
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - '>='
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0'
|
111
|
+
type: :development
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - '>='
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0'
|
118
|
+
- !ruby/object:Gem::Dependency
|
119
|
+
name: cucumber
|
120
|
+
requirement: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - '>='
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0'
|
125
|
+
type: :development
|
126
|
+
prerelease: false
|
127
|
+
version_requirements: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - '>='
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '0'
|
132
|
+
- !ruby/object:Gem::Dependency
|
133
|
+
name: rack-test
|
134
|
+
requirement: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - '>='
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '0'
|
139
|
+
type: :development
|
140
|
+
prerelease: false
|
141
|
+
version_requirements: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - '>='
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '0'
|
146
|
+
- !ruby/object:Gem::Dependency
|
147
|
+
name: vcr
|
148
|
+
requirement: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - '>='
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '0'
|
153
|
+
type: :development
|
154
|
+
prerelease: false
|
155
|
+
version_requirements: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - '>='
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '0'
|
160
|
+
- !ruby/object:Gem::Dependency
|
161
|
+
name: mongo
|
162
|
+
requirement: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - '>='
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '0'
|
167
|
+
type: :development
|
168
|
+
prerelease: false
|
169
|
+
version_requirements: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - '>='
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0'
|
174
|
+
- !ruby/object:Gem::Dependency
|
175
|
+
name: bson
|
176
|
+
requirement: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - '>='
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0'
|
181
|
+
type: :development
|
182
|
+
prerelease: false
|
183
|
+
version_requirements: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - '>='
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0'
|
188
|
+
- !ruby/object:Gem::Dependency
|
189
|
+
name: bson_ext
|
190
|
+
requirement: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - '>='
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0'
|
195
|
+
type: :development
|
196
|
+
prerelease: false
|
197
|
+
version_requirements: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - '>='
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '0'
|
202
|
+
description: Caching layer for forecast_io
|
203
|
+
email:
|
204
|
+
- hello@jonrowe.co.uk
|
205
|
+
executables: []
|
206
|
+
extensions: []
|
207
|
+
extra_rdoc_files: []
|
208
|
+
files:
|
209
|
+
- LICENSE.txt
|
210
|
+
- README.md
|
211
|
+
- features/accessing_a_forecast.feature
|
212
|
+
- features/accessing_a_forecast_at_a_specific_time.feature
|
213
|
+
- features/accessing_the_api.feature
|
214
|
+
- features/step_definitions/api_steps.rb
|
215
|
+
- features/step_definitions/cache_steps.rb
|
216
|
+
- features/step_definitions/forecast_steps.rb
|
217
|
+
- features/support/api.rb
|
218
|
+
- features/support/cassettes/forecast_for_latitude_and_longitude.yml
|
219
|
+
- features/support/config.rb
|
220
|
+
- features/support/env.rb
|
221
|
+
- features/support/expect.rb
|
222
|
+
- features/support/fixtures.rb
|
223
|
+
- features/support/mongo.rb
|
224
|
+
- features/support/parse.rb
|
225
|
+
- features/support/time.rb
|
226
|
+
- features/support/vcr.rb
|
227
|
+
- forecast_io-cache.gemspec
|
228
|
+
- lib/forecast_io-cache.rb
|
229
|
+
- lib/forecast_io/cache.rb
|
230
|
+
- lib/forecast_io/cache/app.rb
|
231
|
+
- lib/forecast_io/cache/configuration.rb
|
232
|
+
- lib/forecast_io/cache/forecast.rb
|
233
|
+
- lib/forecast_io/cache/forecast_data.rb
|
234
|
+
- lib/forecast_io/cache/generate.rb
|
235
|
+
- lib/forecast_io/cache/store.rb
|
236
|
+
- lib/forecast_io/cache/version.rb
|
237
|
+
- spec/forecast_io/cache/configuration_spec.rb
|
238
|
+
- spec/forecast_io/cache/forecast_data_spec.rb
|
239
|
+
- spec/forecast_io/cache/forecast_spec.rb
|
240
|
+
- spec/forecast_io/cache/generate_spec.rb
|
241
|
+
- spec/forecast_io/cache/store_spec.rb
|
242
|
+
- spec/forecast_io/cache_spec.rb
|
243
|
+
- spec/support/mongo.rb
|
244
|
+
homepage: https://github.com/Tacktical/forecast_io-cache
|
245
|
+
licenses:
|
246
|
+
- MIT
|
247
|
+
metadata: {}
|
248
|
+
post_install_message:
|
249
|
+
rdoc_options: []
|
250
|
+
require_paths:
|
251
|
+
- lib
|
252
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
253
|
+
requirements:
|
254
|
+
- - '>='
|
255
|
+
- !ruby/object:Gem::Version
|
256
|
+
version: '0'
|
257
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
258
|
+
requirements:
|
259
|
+
- - '>='
|
260
|
+
- !ruby/object:Gem::Version
|
261
|
+
version: '0'
|
262
|
+
requirements: []
|
263
|
+
rubyforge_project:
|
264
|
+
rubygems_version: 2.0.3
|
265
|
+
signing_key:
|
266
|
+
specification_version: 4
|
267
|
+
summary: Caching layer for forecast_io
|
268
|
+
test_files:
|
269
|
+
- features/accessing_a_forecast.feature
|
270
|
+
- features/accessing_a_forecast_at_a_specific_time.feature
|
271
|
+
- features/accessing_the_api.feature
|
272
|
+
- features/step_definitions/api_steps.rb
|
273
|
+
- features/step_definitions/cache_steps.rb
|
274
|
+
- features/step_definitions/forecast_steps.rb
|
275
|
+
- features/support/api.rb
|
276
|
+
- features/support/cassettes/forecast_for_latitude_and_longitude.yml
|
277
|
+
- features/support/config.rb
|
278
|
+
- features/support/env.rb
|
279
|
+
- features/support/expect.rb
|
280
|
+
- features/support/fixtures.rb
|
281
|
+
- features/support/mongo.rb
|
282
|
+
- features/support/parse.rb
|
283
|
+
- features/support/time.rb
|
284
|
+
- features/support/vcr.rb
|
285
|
+
- spec/forecast_io/cache/configuration_spec.rb
|
286
|
+
- spec/forecast_io/cache/forecast_data_spec.rb
|
287
|
+
- spec/forecast_io/cache/forecast_spec.rb
|
288
|
+
- spec/forecast_io/cache/generate_spec.rb
|
289
|
+
- spec/forecast_io/cache/store_spec.rb
|
290
|
+
- spec/forecast_io/cache_spec.rb
|
291
|
+
- spec/support/mongo.rb
|
metadata.gz.sig
ADDED
Binary file
|