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.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. checksums.yaml.gz.sig +1 -0
  3. data/LICENSE.txt +22 -0
  4. data/README.md +35 -0
  5. data/features/accessing_a_forecast.feature +32 -0
  6. data/features/accessing_a_forecast_at_a_specific_time.feature +27 -0
  7. data/features/accessing_the_api.feature +17 -0
  8. data/features/step_definitions/api_steps.rb +8 -0
  9. data/features/step_definitions/cache_steps.rb +18 -0
  10. data/features/step_definitions/forecast_steps.rb +33 -0
  11. data/features/support/api.rb +13 -0
  12. data/features/support/cassettes/forecast_for_latitude_and_longitude.yml +60 -0
  13. data/features/support/config.rb +9 -0
  14. data/features/support/env.rb +2 -0
  15. data/features/support/expect.rb +9 -0
  16. data/features/support/fixtures.rb +20 -0
  17. data/features/support/mongo.rb +5 -0
  18. data/features/support/parse.rb +14 -0
  19. data/features/support/time.rb +17 -0
  20. data/features/support/vcr.rb +7 -0
  21. data/forecast_io-cache.gemspec +36 -0
  22. data/lib/forecast_io/cache/app.rb +35 -0
  23. data/lib/forecast_io/cache/configuration.rb +25 -0
  24. data/lib/forecast_io/cache/forecast.rb +26 -0
  25. data/lib/forecast_io/cache/forecast_data.rb +37 -0
  26. data/lib/forecast_io/cache/generate.rb +42 -0
  27. data/lib/forecast_io/cache/store.rb +50 -0
  28. data/lib/forecast_io/cache/version.rb +7 -0
  29. data/lib/forecast_io/cache.rb +32 -0
  30. data/lib/forecast_io-cache.rb +1 -0
  31. data/spec/forecast_io/cache/configuration_spec.rb +30 -0
  32. data/spec/forecast_io/cache/forecast_data_spec.rb +32 -0
  33. data/spec/forecast_io/cache/forecast_spec.rb +41 -0
  34. data/spec/forecast_io/cache/generate_spec.rb +34 -0
  35. data/spec/forecast_io/cache/store_spec.rb +37 -0
  36. data/spec/forecast_io/cache_spec.rb +15 -0
  37. data/spec/support/mongo.rb +11 -0
  38. data.tar.gz.sig +0 -0
  39. metadata +291 -0
  40. 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��oD gi�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,8 @@
1
+ When /^I request '(.*)'$/ do |path|
2
+ @path = path
3
+ end
4
+
5
+ Then /^I am given the json response.*$/ do |string|
6
+ get @path
7
+ expect(parsed_response).to eq parsed(string)
8
+ end
@@ -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,13 @@
1
+ require 'forecast_io-cache'
2
+ require 'rack/test'
3
+
4
+ module API
5
+ include Rack::Test::Methods
6
+
7
+ def app
8
+ Forecast::IO::Cache
9
+ end
10
+
11
+ end
12
+
13
+ World API
@@ -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,9 @@
1
+ require 'bson'
2
+
3
+ Forecast::IO.configure do |config|
4
+ config.api_key = ENV['API_KEY']
5
+ end
6
+
7
+ Forecast::IO::Cache.configure do |config|
8
+ config.mongo_uri 'mongodb://localhost/forecast-test'
9
+ end
@@ -0,0 +1,2 @@
1
+ ENV['RACK_ENV'] = 'test'
2
+ ENV['API_KEY'] = 'API_KEY'
@@ -0,0 +1,9 @@
1
+ module Expectation
2
+
3
+ def expect_ok
4
+ yield
5
+ expect(last_response.status).to eq 200
6
+ end
7
+
8
+ end
9
+ World Expectation
@@ -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,5 @@
1
+ require 'mongo-configure'
2
+
3
+ After do
4
+ Mongo::Configure.current.load.collections.select { |c| c.name !~ /^system\./ }.each &:remove
5
+ end
@@ -0,0 +1,14 @@
1
+ module Parse
2
+
3
+ def parsed string
4
+ JSON.parse string
5
+ rescue JSON::ParserError
6
+ raise "Expected [#{string}] to be valid json"
7
+ end
8
+
9
+ def parsed_response
10
+ parsed last_response.body
11
+ end
12
+
13
+ end
14
+ World Parse
@@ -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,7 @@
1
+ require 'vcr'
2
+
3
+ VCR.configure do |c|
4
+ c.cassette_library_dir = "./features/support/cassettes"
5
+ c.hook_into :faraday
6
+ c.allow_http_connections_when_no_cassette = false
7
+ 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,7 @@
1
+ module Forecast
2
+ module IO
3
+ module Cache
4
+ VERSION = "0.0.1"
5
+ end
6
+ end
7
+ 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