reviewed 0.5.0 → 0.6.0

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 (55) hide show
  1. data/lib/faraday/cache.rb +47 -0
  2. data/lib/reviewed/client.rb +4 -6
  3. data/lib/reviewed/request.rb +39 -2
  4. data/lib/reviewed/version.rb +1 -1
  5. data/lib/reviewed.rb +1 -0
  6. data/reviewed.gemspec +1 -0
  7. data/spec/article_spec.rb +3 -5
  8. data/spec/client_spec.rb +4 -0
  9. data/spec/collection_spec.rb +2 -1
  10. data/spec/faraday/cache_spec.rb +59 -0
  11. data/spec/fixtures/vcr/Reviewed_Article/associations/attachments/assigns_attachments_to_the_correct_class.yml +296 -304
  12. data/spec/fixtures/vcr/Reviewed_Article/associations/attachments/does_not_has_many_attachments.yml +231 -204
  13. data/spec/fixtures/vcr/Reviewed_Article/associations/attachments/does_not_have_any_matching_attachments.yml +296 -246
  14. data/spec/fixtures/vcr/Reviewed_Article/associations/attachments/finds_attachments_by_tag.yml +346 -276
  15. data/spec/fixtures/vcr/Reviewed_Article/associations/attachments/gets_gallery_attachments.yml +320 -306
  16. data/spec/fixtures/vcr/Reviewed_Article/associations/deals/has_many_deals.yml +281 -234
  17. data/spec/fixtures/vcr/Reviewed_Article/associations/pages/has_many_pages.yml +231 -204
  18. data/spec/fixtures/vcr/Reviewed_Article/associations/products/has_many_products.yml +231 -204
  19. data/spec/fixtures/vcr/Reviewed_Article/associations/products/returns_products_of_the_correct_class.yml +231 -204
  20. data/spec/fixtures/vcr/Reviewed_Article/associations/related_articles/has_many_related_articles.yml +231 -204
  21. data/spec/fixtures/vcr/Reviewed_Article/fetches_when_a_tag_is_not_in_pre-loaded_set.yml +443 -25
  22. data/spec/fixtures/vcr/Reviewed_Article/find_page/finds_a_page_with_a_matching_slug.yml +360 -313
  23. data/spec/fixtures/vcr/Reviewed_Article/primary_product/returns_a_product_of_the_correct_class.yml +511 -437
  24. data/spec/fixtures/vcr/Reviewed_Article/primary_product/returns_nil_if_does_not_respond_to_products.yml +461 -407
  25. data/spec/fixtures/vcr/Reviewed_Article/primary_product/returns_the_primary_product.yml +461 -407
  26. data/spec/fixtures/vcr/Reviewed_Article/returns_local_attachments_when_available.yml +443 -87
  27. data/spec/fixtures/vcr/Reviewed_Collection/collection_data/fetches_the_first_page_by_default.yml +201 -71
  28. data/spec/fixtures/vcr/Reviewed_Collection/collection_data/is_enumerable.yml +201 -71
  29. data/spec/fixtures/vcr/Reviewed_Collection/next_page/fetches_the_next_page_of_results.yml +312 -77
  30. data/spec/fixtures/vcr/Reviewed_Collection/page_attributes_pagination_/indicates_if_the_page_number_is_out_of_bounds.yml +201 -71
  31. data/spec/fixtures/vcr/Reviewed_Collection/page_attributes_pagination_/indicates_whether_this_is_the_first_or_last_page.yml +151 -41
  32. data/spec/fixtures/vcr/Reviewed_Collection/page_attributes_pagination_/returns_the_limit_value_max_per_page_.yml +151 -41
  33. data/spec/fixtures/vcr/Reviewed_Collection/page_attributes_pagination_/returns_the_number_of_entries_on_the_current_page.yml +201 -71
  34. data/spec/fixtures/vcr/Reviewed_Collection/page_attributes_pagination_/returns_the_offset.yml +201 -71
  35. data/spec/fixtures/vcr/Reviewed_Collection/page_attributes_pagination_/returns_the_total_item_count.yml +151 -41
  36. data/spec/fixtures/vcr/Reviewed_Collection/page_attributes_pagination_/returns_the_total_number_of_pages.yml +201 -71
  37. data/spec/fixtures/vcr/Reviewed_Collection/previous_page/fetches_the_previous_page_of_results.yml +561 -176
  38. data/spec/fixtures/vcr/Reviewed_Collection/previous_page/returns_nil_if_there_is_no_previous_page.yml +151 -41
  39. data/spec/fixtures/vcr/Reviewed_Product/associations/attachments/does_not_have_any_matching_attachments.yml +168 -95
  40. data/spec/fixtures/vcr/Reviewed_Product/associations/attachments/matches_attachments_by_tag_properly.yml +103 -113
  41. data/spec/fixtures/vcr/Reviewed_Product/associations/attachments/no_longer_has_many_attachments.yml +103 -53
  42. data/spec/fixtures/vcr/Reviewed_Product/associations/attachments/returns_attachments_by_tag.yml +103 -113
  43. data/spec/fixtures/vcr/Reviewed_Product/associations/attachments/returns_attachments_of_the_correct_class.yml +118 -65
  44. data/spec/fixtures/vcr/Reviewed_Product/manufacturer_specs/has_many_manufacturer_specs.yml +102 -52
  45. data/spec/fixtures/vcr/Reviewed_Product/manufacturer_specs/returns_attachments_of_the_correct_class.yml +102 -52
  46. data/spec/product_spec.rb +2 -1
  47. data/spec/request_spec.rb +21 -0
  48. data/spec/spec_helper.rb +1 -0
  49. metadata +74 -39
  50. checksums.yaml +0 -7
  51. data/spec/fixtures/vcr/Reviewed_Article/does_not_request_for_an_attachment_if_it_can_be_found_in_attributes.yml +0 -387
  52. data/spec/fixtures/vcr/Reviewed_Article/fetches_all_attachments_when_no_tag_is_asked_for.yml +0 -195
  53. data/spec/fixtures/vcr/Reviewed_Article/fetches_attachments_when_non-existent.yml +0 -137
  54. data/spec/fixtures/vcr/Reviewed_Article/fetches_when_an_tag_is_not_in_pre-loaded_set.yml +0 -75
  55. data/spec/fixtures/vcr/Reviewed_Article/sets_DEFAULT_ATTACHMENTS.yml +0 -75
@@ -0,0 +1,47 @@
1
+ require 'active_support/cache'
2
+
3
+ module Faraday
4
+ class Cache
5
+
6
+ def self.store
7
+ @store ||= ActiveSupport::Cache.lookup_store(:redis_store,
8
+ ENV['REVIEWED_CACHE_REDIS_URL'],
9
+ { expires_in: Integer(ENV['REVIEWED_CACHE_TIMEOUT'] || 90).minutes } )
10
+ end
11
+
12
+ def initialize(app)
13
+ @app = app
14
+ end
15
+
16
+ def call(env)
17
+ @url = env[:url]
18
+ @auth_header = env[:request_headers]['X-Reviewed-Authorization']
19
+
20
+ if serve_from_cache && self.class.store.exist?(cache_key)
21
+ Hashie::Mash.new(Marshal.load( self.class.store.read(cache_key) ))
22
+ else
23
+ @app.call(env).on_complete do |response|
24
+ if store_response
25
+ self.class.store.delete(cache_key)
26
+ self.class.store.write(cache_key, Marshal.dump(response))
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ private
33
+
34
+ def serve_from_cache
35
+ @url.query.blank? || !@url.query.match(/\b(skip|reset)-cache\b/)
36
+ end
37
+
38
+ def store_response
39
+ @url.query.blank? || !@url.query.match(/\bskip-cache\b/)
40
+ end
41
+
42
+ def cache_key
43
+ [@auth_header, @url.request_uri].join(':')
44
+ end
45
+
46
+ end
47
+ end
@@ -59,6 +59,7 @@ module Reviewed
59
59
 
60
60
  def connection
61
61
  @connection ||= ::Faraday.new(url: base_uri) do |faraday|
62
+ faraday.use Faraday::Cache if ENV['REVIEWED_CACHE_REDIS_URL']
62
63
  faraday.response :mashify
63
64
  faraday.response :errors
64
65
  faraday.response :json
@@ -78,12 +79,9 @@ module Reviewed
78
79
  end
79
80
  raise Reviewed::ApiError.new(msg: "API connection returned redirect or error: status=#{res.status}") if res.status > 204 and res.status != 404
80
81
  res
81
- rescue Faraday::Error::ClientError => e
82
- message = <<-EOS.gsub(/^[ ]*/, '')
83
- API Error. method: #{method} url: #{base_uri} path: #{path} params: #{params.to_s} api_key: #{self.api_key}
84
- Original exception message:
85
- #{e.message}
86
- EOS
82
+ rescue Errno::ETIMEDOUT, Faraday::Error::ClientError => e
83
+ message = %Q!API Error. method: #{method} url: #{base_uri} path: #{path} params: #{params.to_s} api_key: #{self.api_key}!
84
+ message << " Original exception message: #{e.message}"
87
85
  new_exception = Reviewed::ApiError.new(msg: message)
88
86
  new_exception.set_backtrace(e.backtrace) # TODO not seeing in Airbrake
89
87
  raise new_exception
@@ -6,6 +6,8 @@ module Reviewed
6
6
  @resource = opts[:resource]
7
7
  @scope = opts[:scope]
8
8
  @client = opts[:client] || Reviewed::Client.new
9
+ @skip_cache = false
10
+ @reset_cache = false
9
11
  end
10
12
 
11
13
  def path
@@ -33,13 +35,48 @@ module Reviewed
33
35
  end
34
36
 
35
37
  def object_from_response(method, url, params={})
36
- response = client.send(method, url, params)
38
+ response = client.send(method, url, params.merge(cache_control_params))
37
39
  resource.new(response.body)
38
40
  end
39
41
 
42
+ def cached?
43
+ !uncached?
44
+ end
45
+
46
+ def uncached?
47
+ skip_cache? || reset_cache?
48
+ end
49
+
50
+ def with_no_cache
51
+ @skip_cache = true
52
+ self
53
+ end
54
+
55
+ def with_new_cache
56
+ @reset_cache = true
57
+ self
58
+ end
59
+
40
60
  def collection_from_response(method, url, params={})
41
- response = client.send(method, url, params)
61
+ response = client.send(method, url, params.merge(cache_control_params))
42
62
  Reviewed::Collection.new(client, resource, response, params)
43
63
  end
64
+
65
+ def cache_control_params
66
+ params = {}
67
+ params.merge!({:"skip-cache" => true}) if skip_cache?
68
+ params.merge!({:"reset-cache" => true}) if reset_cache?
69
+ params
70
+ end
71
+
72
+ private
73
+
74
+ def skip_cache?
75
+ @skip_cache
76
+ end
77
+
78
+ def reset_cache?
79
+ @reset_cache
80
+ end
44
81
  end
45
82
  end
@@ -1,4 +1,4 @@
1
1
  module Reviewed
2
- VERSION = "0.5.0"
2
+ VERSION = "0.6.0"
3
3
  API_VERSION = 'v1'
4
4
  end
data/lib/reviewed.rb CHANGED
@@ -4,6 +4,7 @@ require 'faraday'
4
4
  require 'faraday_middleware'
5
5
  require 'faraday/api_key'
6
6
  require 'faraday/errors'
7
+ require 'faraday/cache'
7
8
  require 'active_support/inflector'
8
9
  require 'active_support/core_ext'
9
10
  require 'active_model'
data/reviewed.gemspec CHANGED
@@ -21,6 +21,7 @@ Gem::Specification.new do |gem|
21
21
  gem.add_dependency 'faraday_middleware', '>= 0.9.0'
22
22
  gem.add_dependency 'hashie', '~> 1.2'
23
23
  gem.add_dependency 'rack'
24
+ gem.add_dependency 'redis-rails'
24
25
 
25
26
  gem.add_development_dependency 'rake'
26
27
  gem.add_development_dependency 'rspec', '>= 2.10'
data/spec/article_spec.rb CHANGED
@@ -7,6 +7,7 @@ describe Reviewed::Article, vcr: true do
7
7
  end
8
8
 
9
9
  before(:each) do
10
+ Faraday::Cache.store.clear
10
11
  @article = client.articles.find('big-green-egg-medium-charcoal-grill-review', { with_attachments: true })
11
12
  end
12
13
 
@@ -63,11 +64,8 @@ describe Reviewed::Article, vcr: true do
63
64
  end
64
65
 
65
66
  it 'finds attachments by tag' do
66
- attachments = @article.attachments('old-hero')
67
- attachments.length.should == 1
68
- attachments.each do |attachment|
69
- attachment.tags.join(',').should match(/hero/i)
70
- end
67
+ attachments = @article.attachments('hero')
68
+ attachments.map(&:tags).flatten.should == ['hero']
71
69
  end
72
70
 
73
71
  it 'does not have any matching attachments' do
data/spec/client_spec.rb CHANGED
@@ -2,6 +2,10 @@ require 'spec_helper.rb'
2
2
 
3
3
  describe Reviewed::Client do
4
4
 
5
+ before do
6
+ Faraday::Cache.store.clear
7
+ end
8
+
5
9
  let(:client) { Reviewed::Client.new }
6
10
 
7
11
  describe 'config' do
@@ -12,7 +12,8 @@ describe Reviewed::Collection, vcr: true do
12
12
  end
13
13
 
14
14
  before(:each) do
15
- @collection = client.products.all # creates a collection
15
+ Faraday::Cache.store.clear
16
+ @collection = client.products.with_no_cache.all # creates a collection
16
17
  end
17
18
 
18
19
  describe 'collection data' do
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+
4
+ describe Faraday::Cache do
5
+
6
+ describe 'call' do
7
+
8
+ conn = Faraday.new do |builder|
9
+ builder.use Faraday::Cache
10
+ builder.adapter :test, Faraday::Adapter::Test::Stubs.new do |stub|
11
+ stub.get('/articles') { [200, {}, 'I like turtles'] }
12
+ stub.get('/products') { [200, {}, 'Weee products'] }
13
+ end
14
+ end
15
+
16
+ conn.headers = { "X-Reviewed-Authorization" => TEST_KEY }
17
+
18
+ it "caches anything that doesn't have skip-cache or reset-cache" do
19
+ mock_cache_val = mock
20
+ Marshal.stub(:dump).with(hash_including(:body => 'I like turtles')).and_return(mock_cache_val)
21
+ Faraday::Cache.store.should_receive(:write).with("#{TEST_KEY}:/articles", mock_cache_val)
22
+ conn.get('/articles')
23
+ end
24
+
25
+ it "can cache more than one thing" do
26
+ mock_cache_val = mock
27
+ Marshal.stub(:dump => mock_cache_val)
28
+ Faraday::Cache.store.should_receive(:write).with("#{TEST_KEY}:/articles", mock_cache_val)
29
+ Faraday::Cache.store.should_receive(:write).with("#{TEST_KEY}:/products", mock_cache_val)
30
+
31
+ conn.get('/articles')
32
+ conn.get('/products')
33
+ end
34
+
35
+ it "serves responses from the cache when fresh and does not call the app" do
36
+ marshalled_response = Marshal.dump(body: 'old musty response')
37
+ Faraday::Cache.store.stub(:exist? => true)
38
+ Faraday::Cache.store.should_receive(:read).with("#{TEST_KEY}:/articles").and_return(marshalled_response)
39
+ resp = conn.get '/articles'
40
+ resp[:body].should eq("old musty response")
41
+ end
42
+
43
+ describe "cache busting and skipping" do
44
+ it "does not cache responses with skip-cache as a query param" do
45
+ Faraday::Cache.store.should_not_receive(:read)
46
+ Faraday::Cache.store.should_not_receive(:write)
47
+ conn.get '/articles', {:"skip-cache" => true}
48
+ end
49
+
50
+ it "replaces cached content with app response when reset-cache is a query param" do
51
+ Faraday::Cache.store.should_not_receive(:read)
52
+ Faraday::Cache.store.should_receive(:write)
53
+ conn.get '/articles', {:"reset-cache" => true}
54
+ end
55
+ end
56
+
57
+ end
58
+
59
+ end