dry_ice 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.
data/.gitignore ADDED
@@ -0,0 +1,6 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ .DS_Store
6
+ vendor/
data/Gemfile ADDED
@@ -0,0 +1,12 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'guard'
7
+ gem 'rspec'
8
+ gem 'guard-rspec'
9
+ gem 'activesupport'
10
+ gem 'webmock'
11
+ gem 'msgpack'
12
+ end
data/History ADDED
@@ -0,0 +1,16 @@
1
+ == 0.0.4 2012-01-03
2
+ * minor enhancements
3
+ * Update gemspec to depend on HTTParty 0.8.X to fix YAML issues.
4
+
5
+ == 0.0.3
6
+ * minor enhancements
7
+ * Remove the output from setting a cache.
8
+
9
+ == 0.0.2
10
+ * minor enhancements
11
+ * Return a cached result if network fails. [Andreas Garnæs]
12
+
13
+ == 0.0.1
14
+
15
+ * major enhancements
16
+ * Initial release.
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ Copyright (c) 2013 Daniel Cooper
2
+ Copyright (c) 2011 Kristoffer Sachse
3
+
4
+ Permission is hereby granted, free of charge, to any person obtaining
5
+ a copy of this software and associated documentation files (the
6
+ "Software"), to deal in the Software without restriction, including
7
+ without limitation the rights to use, copy, modify, merge, publish,
8
+ distribute, sublicense, and/or sell copies of the Software, and to
9
+ permit persons to whom the Software is furnished to do so, subject to
10
+ the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be
13
+ included in all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # DryIce - Caching for HTTParty
2
+
3
+ ## Description
4
+
5
+ Cache responses in HTTParty models. Every response from a resource which has a non 0 max-age header will be cached for the appropriate amount of time in the cache you provide it.
6
+
7
+ Based off https://github.com/sachse/httparty-icebox
8
+
9
+ ## Installation
10
+
11
+ ### RubyGems
12
+
13
+ You can install the latest Film Buff gem using RubyGems
14
+
15
+ gem install dry_ice
16
+
17
+ ### GitHub
18
+
19
+ Alternatively you can check out the latest code directly from Github
20
+
21
+ git clone http://github.com/homeflow/dry_ice.git
22
+
23
+ ## Usage
24
+
25
+ Any class which includes the `HTTParty` module can include `HTTParty::DryIce` and get access to a cache class method.
26
+
27
+ class AnAPI
28
+
29
+ include HTTParty
30
+ include HTTParty::DryIce
31
+ base_uri 'example.com'
32
+
33
+ cache Rails.cache
34
+
35
+ end
36
+
37
+ The `cache` method accepts any instance which quacks like a [ActiveSupport cache](http://api.rubyonrails.org/classes/ActiveSupport/Cache/MemoryStore.html). That means you can use the built in Rails caches, something like [Redis Store](https://github.com/jodosha/redis-store) or your own custom rolled class as long as it responds to the methods:
38
+
39
+ - read
40
+ - write (taking an option :expires_in => seconds)
41
+ - exit?
42
+ - delete
43
+
44
+
45
+ ## Contribute
46
+
47
+ Fork the project, implement your changes in it's own branch, and send
48
+ a pull request to me. I'll gladly consider any help or ideas.
49
+
50
+ ### Contributors
51
+
52
+ - [Daniel Cooper](http://github.com/danielcooper) - For homeflow.co.uk
53
+
54
+ This project was based off the excellent httparty-icebox gem: https://github.com/sachse/httparty-icebox. It's contributors are listed below.
55
+
56
+ - [Karel Minarik](http://karmi.cz) (Original creator through [a gist](https://gist.github.com/209521/))
57
+ - [Martyn Loughran](https://github.com/mloughran) - Major parts of this code are based on the architecture of ApiCache.
58
+ - [David Heinemeier Hansson](https://github.com/dhh) - Other parts are inspired by the ActiveSupport::Cache in Ruby On Rails.
59
+ - [Amit Chakradeo](https://github.com/amit) - For pointing out response objects have to be stored marshalled on FS.
60
+ - Marlin Forbes - For pointing out the query parameters have to be included in the cache key.
61
+ - [ramieblatt](https://github.com/ramieblatt) - Original Memcached store.
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
data/dry_ice.gemspec ADDED
@@ -0,0 +1,20 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "dry_ice/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "dry_ice"
7
+ s.version = Httparty::DryIce::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Daniel Cooper"]
10
+ s.email = ["daniel@14lines.com"]
11
+ s.homepage = "https://github.com/homeflow/dry_ice"
12
+ s.summary = %q{Caching for HTTParty}
13
+ s.description = %q{Cache responses in HTTParty models}
14
+
15
+ s.add_dependency("httparty", "~> 0.9.0")
16
+ s.add_dependency("msgpack")
17
+
18
+ s.files = `git ls-files`.split("\n")
19
+ s.require_paths = ["lib"]
20
+ end
@@ -0,0 +1,5 @@
1
+ module Httparty
2
+ module DryIce
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
data/lib/dry_ice.rb ADDED
@@ -0,0 +1,149 @@
1
+ require 'logger'
2
+ require 'fileutils'
3
+ require 'tmpdir'
4
+ require 'pathname'
5
+ require 'digest/md5'
6
+
7
+ module HTTParty #:nodoc:
8
+ # == Caching for HTTParty
9
+ # See documentation in HTTParty::Icebox::ClassMethods.cache
10
+ #
11
+ module DryIce
12
+
13
+ module ClassMethods
14
+
15
+ # Enable caching and set cache options
16
+ # Returns memoized cache object
17
+ #
18
+ # Following options are available, default values are in []:
19
+ #
20
+ # +store+:: Storage mechanism for cached data (memory, filesystem, your own) [memory]
21
+ # +timeout+:: Cache expiration in seconds [60]
22
+ # +logger+:: Path to logfile or logger instance [nil, silent]
23
+ #
24
+ # Any additional options are passed to the Cache constructor
25
+ #
26
+ # Usage:
27
+ #
28
+ # # Enable caching in HTTParty, in memory, for 1 minute
29
+ # cache # Use default values
30
+ #
31
+ # # Enable caching in HTTParty, on filesystem (/tmp), for 10 minutes
32
+ # cache :store => 'file', :timeout => 600, :location => '/tmp/'
33
+ #
34
+ # # Use your own cache store (see +AbstractStore+ class below)
35
+ # cache :store => 'memcached', :timeout => 600, :server => '192.168.1.1:1001'
36
+ #
37
+ def cache(cache)
38
+ return @cache = nil unless cache
39
+ raise "cache instance must respond_to #read, #write and #delete" unless cache.respond_to?(:read) && cache.respond_to?(:write) && cache.respond_to?(:delete)
40
+ @cache = IceCache.new(cache)
41
+ end
42
+
43
+ def get_cache
44
+ @cache || false
45
+ end
46
+
47
+ end
48
+
49
+ # When included, extend class with +cache+ method
50
+ # and redefine +get+ method to use cache
51
+ #
52
+ def self.included(receiver) #:nodoc:
53
+ receiver.extend ClassMethods
54
+ receiver.class_eval do
55
+
56
+ # Get reponse from network
57
+ #
58
+ # TODO: Why alias :new :old is not working here? Returns NoMethodError
59
+ #
60
+ def self.get_without_caching(path, options={})
61
+ perform_request Net::HTTP::Get, path, options
62
+ end
63
+
64
+ # Get response from cache, if available
65
+ #
66
+ def self.get_with_caching(path, options={})
67
+ return get_without_caching(path, options) unless get_cache
68
+ key = path.downcase # this makes a copy of path
69
+ key << options[:query].to_s if defined? options[:query]
70
+ if res = get_cache.read(key)
71
+ return res
72
+ else
73
+ response = get_without_caching(path, options)
74
+ if cache_for = self.cache_response?(response)
75
+ get_cache.write(key,response, :expires_in => cache_for)
76
+ return response
77
+ else
78
+ return response
79
+ end
80
+ end
81
+ end
82
+
83
+ #returns falsy if the response should not be cached - otherwise returns the timeout in seconds to cache for
84
+ def self.cache_response?(response)
85
+ return false if !response.body
86
+ return false unless response.code.to_s == "200"
87
+ timeout = response.headers['cache-control'] && response.headers['cache-control'][/max-age=(\d+)/, 1].to_i()
88
+ return false unless timeout && timeout != 0
89
+ return timeout
90
+ end
91
+
92
+ # Redefine original HTTParty +get+ method to use cache
93
+ #
94
+ def self.get(path, options={})
95
+ self.get_with_caching(path, options)
96
+ end
97
+
98
+ end
99
+ end
100
+
101
+ class IceCache
102
+
103
+ require 'msgpack'
104
+
105
+ def initialize(cache)
106
+ @cache = cache
107
+ end
108
+
109
+ def write(name, value, options = {})
110
+ @cache.write(name, serialize_response(value), options)
111
+ end
112
+
113
+
114
+ def serialize_response(response)
115
+ headers = response.headers.dup
116
+ body = response.body.dup
117
+ [headers,body].to_msgpack
118
+ end
119
+
120
+ def build_response(serialized_response)
121
+ res = MessagePack.unpack(serialized_response)
122
+ CachedHTTPartyResponse.new(res[0], res[1])
123
+ end
124
+
125
+ def read(*args)
126
+ found = @cache.read(*args)
127
+ build_response(found) if found
128
+ end
129
+
130
+ def exist?(*args)
131
+ @cache.exist?(*args)
132
+ end
133
+
134
+
135
+ end
136
+
137
+ class CachedHTTPartyResponse
138
+
139
+ attr_accessor :headers, :body
140
+
141
+ def initialize(headers, body)
142
+ @headers, @body = headers, body
143
+ end
144
+
145
+ end
146
+
147
+ end
148
+ end
149
+
@@ -0,0 +1,81 @@
1
+ require 'spec_helper.rb'
2
+ require 'active_support'
3
+
4
+
5
+ describe HTTParty::DryIce do
6
+
7
+ context "when being set up" do
8
+
9
+ it "accepts a cache instance that quacks like a ActiveSupport cache" do
10
+
11
+ expect do
12
+ class MockApi
13
+ cache(Object.new)
14
+ end
15
+ end.to raise_error
16
+
17
+ expect do
18
+ class MockApi
19
+ cache(ActiveSupport::Cache::NullStore.new)
20
+ end
21
+ end.not_to raise_error
22
+
23
+ end
24
+ end
25
+
26
+
27
+ context "when performing a request" do
28
+
29
+
30
+ it "should not use read to check existance" do
31
+
32
+ class MockApi
33
+ cache(ActiveSupport::Cache::NullStore.new)
34
+ end
35
+
36
+ ActiveSupport::Cache::NullStore.any_instance.should_receive(:read)
37
+
38
+ stub_request(:get, "http://example.com/").to_return(:status => 200, :body => "test", :headers => {})
39
+
40
+ MockApi.get('/')
41
+
42
+ end
43
+
44
+
45
+ it "does not cache requests without a cache header" do
46
+
47
+ class MockApi
48
+ cache(nil)
49
+ end
50
+
51
+ stub_request(:get, "http://example.com/").to_return(:status => 200, :body => "test", :headers => {:cache_control => 'max-age=10'})
52
+
53
+ response = MockApi.get('/')
54
+
55
+ expect(MockApi.cache_response?(response)).to be 10
56
+
57
+ end
58
+
59
+
60
+
61
+ end
62
+ end
63
+
64
+
65
+ describe HTTParty::DryIce::IceCache do
66
+
67
+
68
+ it "should be able to marshel and store a HTTParty request" do
69
+ stub_request(:get, "http://example.com/").to_return(:status => 200, :body => "hello world", :headers => {:cache_control => 'max-age=0'})
70
+ response = MockApi.get('/')
71
+ cache = HTTParty::DryIce::IceCache.new(ActiveSupport::Cache::NullStore.new)
72
+
73
+ serialised = cache.serialize_response(response)
74
+ res = cache.build_response(serialised)
75
+
76
+ res.body.should == response.body
77
+ res.headers.should == response.headers
78
+
79
+ end
80
+
81
+ end
@@ -0,0 +1,14 @@
1
+ require File.dirname(__FILE__) + '/../lib/dry_ice.rb'
2
+ require 'rspec'
3
+ require 'httparty'
4
+ require 'json'
5
+ require 'webmock/rspec'
6
+
7
+ class MockApi
8
+
9
+ include HTTParty
10
+ include HTTParty::DryIce
11
+ base_uri 'example.com'
12
+
13
+
14
+ end
metadata ADDED
@@ -0,0 +1,88 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: dry_ice
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Daniel Cooper
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-08-09 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: httparty
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ~>
20
+ - !ruby/object:Gem::Version
21
+ version: 0.9.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ version: 0.9.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: msgpack
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '0'
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: '0'
46
+ description: Cache responses in HTTParty models
47
+ email:
48
+ - daniel@14lines.com
49
+ executables: []
50
+ extensions: []
51
+ extra_rdoc_files: []
52
+ files:
53
+ - .gitignore
54
+ - Gemfile
55
+ - History
56
+ - LICENSE
57
+ - README.md
58
+ - Rakefile
59
+ - dry_ice.gemspec
60
+ - lib/dry_ice.rb
61
+ - lib/dry_ice/version.rb
62
+ - spec/dry_ice_spec.rb
63
+ - spec/spec_helper.rb
64
+ homepage: https://github.com/homeflow/dry_ice
65
+ licenses: []
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ none: false
72
+ requirements:
73
+ - - ! '>='
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ required_rubygems_version: !ruby/object:Gem::Requirement
77
+ none: false
78
+ requirements:
79
+ - - ! '>='
80
+ - !ruby/object:Gem::Version
81
+ version: '0'
82
+ requirements: []
83
+ rubyforge_project:
84
+ rubygems_version: 1.8.24
85
+ signing_key:
86
+ specification_version: 3
87
+ summary: Caching for HTTParty
88
+ test_files: []