Skip to content

Commit

Permalink
Merge pull request #16 from marnen/3-cache-api-data
Browse files Browse the repository at this point in the history
Cache API data [closes #3]
  • Loading branch information
marnen committed Apr 4, 2020
2 parents 3fef250 + 9db7cbb commit e6d0d60
Show file tree
Hide file tree
Showing 10 changed files with 52 additions and 20 deletions.
5 changes: 5 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
language: ruby
services:
- redis-server
env:
global:
- REDIS_URL: "redis://localhost:6379"
cache:
bundler: true
directories:
Expand Down
3 changes: 2 additions & 1 deletion Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ gem 'jbuilder', '~> 2.7'
gem 'bootsnap', '>= 1.4.2', require: false

gem "haml-rails", "~> 2.0"
gem 'faraday', '~> 1.0.1'
gem 'redis'
gem 'typhoeus'

group :development, :test do
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
Expand Down
11 changes: 7 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -111,10 +111,10 @@ GEM
diff-lcs (1.3)
erubi (1.9.0)
erubis (2.7.0)
ethon (0.12.0)
ffi (>= 1.3.0)
faker (2.11.0)
i18n (>= 1.6, < 2)
faraday (1.0.1)
multipart-post (>= 1.2, < 3)
ffi (1.12.2)
formatador (0.2.5)
gherkin (5.1.0)
Expand Down Expand Up @@ -176,7 +176,6 @@ GEM
msgpack (1.3.3)
multi_json (1.14.1)
multi_test (0.1.2)
multipart-post (2.1.1)
nenv (0.3.0)
nio4r (2.5.2)
nokogiri (1.10.9)
Expand Down Expand Up @@ -225,6 +224,7 @@ GEM
rb-fsevent (0.10.3)
rb-inotify (0.10.1)
ffi (~> 1.0)
redis (4.1.3)
regexp_parser (1.7.0)
rspec (3.9.0)
rspec-core (~> 3.9.0)
Expand Down Expand Up @@ -281,6 +281,8 @@ GEM
turbolinks (5.2.1)
turbolinks-source (~> 5.2)
turbolinks-source (5.2.0)
typhoeus (1.3.1)
ethon (>= 0.9.0)
tzinfo (1.2.7)
thread_safe (~> 0.1)
vcr (5.1.0)
Expand Down Expand Up @@ -312,19 +314,20 @@ DEPENDENCIES
byebug
cucumber-rails
faker
faraday (~> 1.0.1)
guard-cucumber!
guard-rspec
haml-rails (~> 2.0)
jbuilder (~> 2.7)
listen (>= 3.0.5, < 3.2)
puma (~> 4.1)
rails (~> 6.0.2, >= 6.0.2.2)
redis
rspec-rails (~> 4.0.0)
sass-rails (>= 6)
spring
spring-watcher-listen (~> 2.0.0)
turbolinks (~> 5)
typhoeus
tzinfo-data
vcr (~> 5.1.0)
web-console (>= 3.3.0)
Expand Down
2 changes: 1 addition & 1 deletion app/models/chart.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ def url
params = {
cht: chart_type, chd: "a:#{data.join ','}", chs: size, chxt: axes, chxl: "0:#{axis_labels.map {|label| label.prepend '|' }.join }", chdl: @legend, chdlp: position, chls: line_thickness
}
url.query = Faraday::Utils.build_query params
url.query = URI.encode_www_form params
end
end

Expand Down
14 changes: 6 additions & 8 deletions app/models/state_daily.rb
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,14 @@ def initialize(state:, date:)
end

def url
@url ||= if date_range?
individuals.map &:url
else
URI('https://covidtracking.com/api/states/daily').tap do |url|
url.query = Faraday::Utils.build_query state: @state, date: @date.to_s(:number)
end
end
@url ||= date_range? ? individuals.map(&:url) : request.url
end

def fetch!
if date_range?
individuals.map(&:fetch!).reject {|response| response['date'].nil? }
else
JSON.parse Faraday.get(url).body
JSON.parse request.run.body
end
end

Expand All @@ -31,4 +25,8 @@ def date_range?
def individuals
@individuals ||= @date.map {|date| self.class.new state: @state, date: date }
end

def request
@request ||= Typhoeus::Request.new('https://covidtracking.com/api/states/daily', params: {state: @state, date: @date.to_s(:number)}, cache_ttl: 6.hours.to_i)
end
end
5 changes: 5 additions & 0 deletions config/initializers/typhoeus_cache.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
require 'addressable'
db = Rails.env.test? ? 1 : 0
redis = Redis.new url: "#{ENV['REDIS_URL']}/#{db}"
redis.flushdb if Rails.env.test?
Typhoeus::Config.cache = Typhoeus::Cache::Redis.new(redis)
5 changes: 5 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ services:
web:
environment:
PORT: 3000
REDIS_URL: "redis://cache:6379"
build: .
command: bundle exec rails s
volumes:
Expand All @@ -14,5 +15,9 @@ services:
ports: ["3000:3000"]
networks: [internal]
command: ["bundle", "exec", "rails", "server", "-b", "0.0.0.0"]
cache:
image: redis:5.0.8-alpine
expose: ["6379"]
networks: [internal]
volumes:
bundle: # see https://dev.to/k_penguin_sato/cache-rails-gems-using-docker-compose-3o3f
2 changes: 1 addition & 1 deletion features/step_definitions/web_steps.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
date_range = start_date..end_date

page.find 'img[src^="https://image-charts.com/chart?"]' do |img|
params = Faraday::Utils.parse_query URI(img['src']).query
params = Hash[URI.decode_www_form URI(img['src']).query]
expect(params).to include(
'cht' => 'lc', # line chart
'chd' => a_string_starting_with('a:'), # data
Expand Down
2 changes: 1 addition & 1 deletion spec/models/chart_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
end

describe '#url' do
let(:params) { Faraday::Utils.parse_query subject.query }
let(:params) { Hash[URI.decode_www_form URI(subject).query] }

subject { chart.url }

Expand Down
23 changes: 19 additions & 4 deletions spec/models/state_daily_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,14 +27,14 @@
subject { super().url }

context 'single date' do
it { is_expected.to be_a_kind_of URI }
it { is_expected.to be_a_kind_of String }

it 'points to the state daily data endpoint' do
expect(subject.to_s).to match %r{^https://covidtracking.com/api/states/daily\b([^/]|$)}
expect(subject).to match %r{^https://covidtracking.com/api/states/daily\b([^/]|$)}
end

context 'query string' do
let(:parsed_query) { Faraday::Utils.parse_query subject.query }
let(:parsed_query) { Hash[URI.decode_www_form URI(subject).query] }

it 'contains the state' do
expect(parsed_query['state']).to be == state
Expand Down Expand Up @@ -70,12 +70,27 @@

it "makes a GET request to the object's URL" do
subject.fetch!
expect(a_request(:get, subject.url)).to have_been_made
expect(a_request :get, subject.url).to have_been_made
end

it 'returns the parsed JSON from the request body' do
expect(subject.fetch!).to be == data
end

context 'caching' do
include ActiveSupport::Testing::TimeHelpers

before(:each) { travel 1.week }
after(:each) { travel_back }

it 'makes the request only once within 6 hours' do
pending "Webmock interferes with caching, so this test actually doesn't work"
subject.fetch!
travel(5.hours + 59.minutes)
subject.fetch!
expect(a_request :get, subject.url).to have_been_made.once
end
end
end

context 'date range' do
Expand Down

0 comments on commit e6d0d60

Please sign in to comment.