grape_cache 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0aff7faeba22650387a9a0f10bdc1fada4349666
4
+ data.tar.gz: cead8b6f22a977484ecfbd8dae9a9776b50ca5f5
5
+ SHA512:
6
+ metadata.gz: e07e1a92ed8a8d513dbef1cc678ea871767e03851d12dd2a40ce93c5e96e38344eb5d2926b897f7a0fb04d55837c452e75f961e528b5a7fca3b292198793a017
7
+ data.tar.gz: 0d678a2010d84859870d6da9995c97b7210aef3753990c1a705c3f8d9b2c39886ba02f4bd431ed40f1a5258bfd7da069686ce8873f04b2bb78f1b80eb943dbe4
data/.travis.yml ADDED
@@ -0,0 +1,5 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.3.0
4
+ script:
5
+ - bundle exec rspec
data/Dockerfile ADDED
@@ -0,0 +1,17 @@
1
+ # Ruby image
2
+ FROM ruby
3
+
4
+ # Install bundler
5
+ RUN gem install bundler --no-ri --no-rdoc
6
+
7
+ # Make app folder
8
+ RUN mkdir app/
9
+
10
+ # Set as workdir
11
+ WORKDIR app/
12
+
13
+ # Add the full source
14
+ ADD . .
15
+
16
+ # Install!
17
+ RUN bundle install
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ # Source
2
+ source 'https://rubygems.org'
3
+
4
+ # Some test dependencies
5
+ gem 'rack-test'
6
+
7
+ # Add the Gem dependencies
8
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,86 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ grape_cache (0.0.0)
5
+ grape (~> 0.16)
6
+ multi_json
7
+ oj
8
+
9
+ GEM
10
+ remote: https://rubygems.org/
11
+ specs:
12
+ activesupport (4.2.6)
13
+ i18n (~> 0.7)
14
+ json (~> 1.7, >= 1.7.7)
15
+ minitest (~> 5.1)
16
+ thread_safe (~> 0.3, >= 0.3.4)
17
+ tzinfo (~> 1.1)
18
+ axiom-types (0.1.1)
19
+ descendants_tracker (~> 0.0.4)
20
+ ice_nine (~> 0.11.0)
21
+ thread_safe (~> 0.3, >= 0.3.1)
22
+ builder (3.2.2)
23
+ coercible (1.0.0)
24
+ descendants_tracker (~> 0.0.1)
25
+ descendants_tracker (0.0.4)
26
+ thread_safe (~> 0.3, >= 0.3.1)
27
+ diff-lcs (1.2.5)
28
+ enumerable-lazy (0.0.1)
29
+ equalizer (0.0.11)
30
+ grape (0.16.2)
31
+ activesupport
32
+ builder
33
+ hashie (>= 2.1.0)
34
+ multi_json (>= 1.3.2)
35
+ multi_xml (>= 0.5.2)
36
+ mustermann19 (~> 0.4.3)
37
+ rack (>= 1.3.0)
38
+ rack-accept
39
+ virtus (>= 1.0.0)
40
+ hashie (3.4.4)
41
+ i18n (0.7.0)
42
+ ice_nine (0.11.2)
43
+ json (1.8.3)
44
+ minitest (5.9.0)
45
+ multi_json (1.12.1)
46
+ multi_xml (0.5.5)
47
+ mustermann19 (0.4.3)
48
+ enumerable-lazy
49
+ oj (2.15.0)
50
+ rack (1.6.4)
51
+ rack-accept (0.4.5)
52
+ rack (>= 0.4)
53
+ rack-test (0.6.3)
54
+ rack (>= 1.0)
55
+ rspec (3.4.0)
56
+ rspec-core (~> 3.4.0)
57
+ rspec-expectations (~> 3.4.0)
58
+ rspec-mocks (~> 3.4.0)
59
+ rspec-core (3.4.4)
60
+ rspec-support (~> 3.4.0)
61
+ rspec-expectations (3.4.0)
62
+ diff-lcs (>= 1.2.0, < 2.0)
63
+ rspec-support (~> 3.4.0)
64
+ rspec-mocks (3.4.1)
65
+ diff-lcs (>= 1.2.0, < 2.0)
66
+ rspec-support (~> 3.4.0)
67
+ rspec-support (3.4.1)
68
+ thread_safe (0.3.5)
69
+ tzinfo (1.2.2)
70
+ thread_safe (~> 0.1)
71
+ virtus (1.0.5)
72
+ axiom-types (~> 0.1)
73
+ coercible (~> 1.0)
74
+ descendants_tracker (~> 0.0, >= 0.0.3)
75
+ equalizer (~> 0.0, >= 0.0.9)
76
+
77
+ PLATFORMS
78
+ ruby
79
+
80
+ DEPENDENCIES
81
+ grape_cache!
82
+ rack-test
83
+ rspec (~> 3.0)
84
+
85
+ BUNDLED WITH
86
+ 1.12.4
data/README.md ADDED
@@ -0,0 +1,50 @@
1
+ # Grape Cache [![Build Status](https://travis-ci.org/gabrielcorado/grape_cache.svg?branch=develop)](https://travis-ci.org/gabrielcorado/grape_cache) [![Gem Version](https://badge.fury.io/rb/grape_cache.svg)](https://badge.fury.io/rb/grape_cache)
2
+ ```
3
+ +-----------+ Cached?
4
+ | Request +------------------+
5
+ +-----------+ |
6
+ |
7
+ |
8
+ v
9
+ +-----------+ No +-----+-----+
10
+ | API Call +<-----------+ Cache |
11
+ +-----+-----+ +-----+-----+
12
+ | |
13
+ | |
14
+ v |
15
+ +-----+-----+ Yes |
16
+ | Response +<-----------------+
17
+ +-----------+
18
+ ```
19
+
20
+ ## How it works
21
+ The module generates a key based on the Grape Route(METHOD + PATH) and the current params
22
+ (HTTP BODY + QUERY STRINGS) and set the result of the API Call to this key.
23
+ When another request with exacly same attributes that will generate the same key again,
24
+ it does not call the API again, it just return the last response.
25
+
26
+ ## Usage
27
+ ```ruby
28
+ # Add the Middleware into your API class
29
+ # In the middleware definition you have to specify the
30
+ # store used by the middleware, also you could pass the
31
+ # params for this store like `:expires_in`
32
+ class Application < Grape::API
33
+ use GrapeCache, store: Rails.cache, options: { expires_in: 1.hour }
34
+ end
35
+
36
+ # After this just simply set your routes with a cache flag
37
+ get '/hello/cache', cache: true do
38
+ { hello: 'From cache' }
39
+ end
40
+
41
+ # To use it with a shared cache env you could use the flag `:namespace`
42
+ # to define it. In this case I'll use the Apartment as example.
43
+ use GrapeCache, store: Rails.cache, namespace: -> { Apartment::Tenant.current }
44
+ ```
45
+
46
+ ## Development
47
+ * Building the docker container: `docker build -t grape-cache .`
48
+ * Running the tests:
49
+ * With volume: `docker run --rm -it -v (PWD):/app grape-cache bundle exec rspec`
50
+ * Without volume: `docker run --rm -it grape-cache bundle exec rspec`
@@ -0,0 +1,32 @@
1
+ #
2
+ require './lib/grape_cache/version'
3
+
4
+ #
5
+ Gem::Specification.new do |s|
6
+ #
7
+ s.name = 'grape_cache'
8
+ s.version = GrapeCache.version
9
+
10
+ #
11
+ s.summary = ''
12
+ s.description = ''
13
+
14
+ #
15
+ s.author = 'Gabriel Corado'
16
+ s.email = 'gabrielcorado@mail.com'
17
+ s.homepage = 'http://github.com/gabrielcorado/venduitz'
18
+
19
+ #
20
+ s.files = `git ls-files`.strip.split("\n")
21
+ s.executables = Dir["bin/*"].map { |f| File.basename(f) }
22
+
23
+ # Dependencies
24
+ # s.add_dependency 'concurrent-ruby', '~> 1.0'
25
+ # s.add_dependency 'docker-api', '~> 1.26'
26
+ s.add_dependency 'multi_json'
27
+ s.add_dependency 'oj'
28
+ s.add_dependency 'grape', '~> 0.16'
29
+
30
+ # Development depencies
31
+ s.add_development_dependency 'rspec', '~> 3.0'
32
+ end
@@ -0,0 +1,140 @@
1
+ #
2
+ module GrapeCache
3
+ # Middleware Class
4
+ class Middleware < Grape::Middleware::Base
5
+ # Overwriting the method call! from Grape::Middleware::Base
6
+ # Reason: Make the :before call to return the whole stack
7
+ def call!(env)
8
+ @env = env
9
+ # Just here is moving!
10
+ before_res = before
11
+ return before_res unless before_res.nil?
12
+ # end the overwrite
13
+ begin
14
+ @app_response = @app.call(@env)
15
+ ensure
16
+ begin
17
+ after_response = after
18
+ rescue StandardError => e
19
+ warn "caught error of type #{e.class} in after callback inside #{self.class.name} : #{e.message}"
20
+ raise e
21
+ end
22
+ end
23
+
24
+ response = after_response || @app_response
25
+ merge_headers response
26
+ response
27
+ end
28
+
29
+ #
30
+ def after
31
+ # Check if cache is enabled in this route
32
+ return unless cache?
33
+
34
+ # Cache its output
35
+ store.write key(@params), response, @options.fetch(:options, {}).merge(endpoint.options.fetch(:options, {}))
36
+
37
+ # Do not change the return
38
+ return
39
+ end
40
+
41
+ # Generate the cache if its necessary
42
+ def before
43
+ # Check if cache is enabled in this route
44
+ return unless cache?
45
+
46
+ # Generate the cache key
47
+ k = key(params)
48
+
49
+ # Check the current key exists in the cache
50
+ return unless store.exist?(k)
51
+
52
+ # Get to the response it
53
+ res = MultiJson.load store.read(k)
54
+
55
+ # Return in the Rack Formatt
56
+ [res['status'], res['headers'], res['body']]
57
+ end
58
+
59
+ private
60
+
61
+ # Check if the current route has cache
62
+ def cache?
63
+ endpoint.options.fetch(:route_options, {}).fetch(:cache, false)
64
+ end
65
+
66
+ # Cache store
67
+ def store
68
+ # Check if Store was defined
69
+ raise 'Cache Store have to be defined' if @options[:store].nil?
70
+
71
+ # Return it
72
+ @options[:store]
73
+ end
74
+
75
+ # Cache key
76
+ def key(opts)
77
+ # Get the namespace
78
+ ns = namespace
79
+
80
+ # Default prefix: `grape_cache`
81
+ res = "grape_cache/#{digest(route)}/#{digest(opts)}"
82
+
83
+ # Add the namespace (if its necessary)
84
+ "#{ns}/#{res}" if ns.nil?
85
+
86
+ # Return the result
87
+ res
88
+ end
89
+
90
+ # Get the namespace
91
+ def namespace
92
+ # Check if its nil
93
+ return nil if @options[:namespace].nil?
94
+
95
+ # Call it if it's a Proc
96
+ @options[:namespace].proc? ? @options[:namespace].call : @options[:namespace]
97
+ end
98
+
99
+ # Route
100
+ def route
101
+ "#{endpoint.route.options[:method]} #{endpoint.route.pattern.path}"
102
+ end
103
+
104
+ # Transform the reponse into JSON
105
+ def response
106
+ # Dump it to JSON
107
+ MultiJson.dump({
108
+ status: @app_response.status,
109
+ headers: @app_response.headers,
110
+ body: @app_response.body
111
+ })
112
+ end
113
+
114
+ # Digest the params
115
+ def digest(params)
116
+ # Dump the params
117
+ dump = Marshal::dump(params)
118
+
119
+ # Disgest the params
120
+ Digest::MD5.hexdigest dump
121
+ end
122
+
123
+ # Endpoint params
124
+ # Get the input passed and the QueryString
125
+ def params
126
+ # endpoint.declared(endpoint.params)
127
+ @params = "#{env['QUERY_STRING']}_#{env[Grape::Env::RACK_INPUT].read}"
128
+ end
129
+
130
+ # Request
131
+ def request
132
+ @request ||= Rack::Request.new(env)
133
+ end
134
+
135
+ # Access the endpoint
136
+ def endpoint
137
+ env['api.endpoint']
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,7 @@
1
+ #
2
+ module GrapeCache
3
+ # Gem version
4
+ def self.version
5
+ '0.0.0'.freeze
6
+ end
7
+ end
@@ -0,0 +1,13 @@
1
+ # Initialize the module
2
+ module GrapeCache
3
+ end
4
+
5
+ # Dependencies
6
+ require 'grape'
7
+ require 'grape/middleware/formatter'
8
+ require 'digest/md5'
9
+ require 'oj'
10
+ require 'multi_json'
11
+
12
+ # Modules
13
+ require 'grape_cache/middleware'
@@ -0,0 +1,86 @@
1
+ # Helper
2
+ require 'spec_helper'
3
+
4
+ # Create a Grape API application
5
+ class Application < Grape::API
6
+ # Add the Middleware
7
+ # insert_after Grape::Middleware::Formatter, GrapeCache::Middleware, store: CacheStore.new
8
+ use GrapeCache::Middleware, store: CacheStore.new
9
+
10
+ # Set the format
11
+ format :json
12
+
13
+ # Simple Route
14
+ params do
15
+ optional :break, default: true
16
+ end
17
+ get '/simple', cache: true do
18
+ { hello: 'Route', time: Time.now }
19
+ end
20
+
21
+ #
22
+ resource :sub do
23
+ get '/simple', cache: true do
24
+ { hello: 'Sub', time: Time.now }
25
+ end
26
+ end
27
+
28
+ # Post request
29
+ post '/post', cache: true do
30
+ { hello: 'Sub', time: Time.now }
31
+ end
32
+ end
33
+
34
+ # Class test
35
+ describe GrapeCache::Middleware do
36
+ # Create a Grape app
37
+ let(:app) { Application.new }
38
+
39
+ #
40
+ it 'should generate the cache for a simple text view' do
41
+ # Execute the route
42
+ get '/simple'
43
+
44
+ # Get it infos
45
+ first = MultiJson.load last_response.body
46
+ expect(first['time']).to eq(Time.now.to_s)
47
+
48
+ # Sleep...
49
+ sleep 2
50
+
51
+ # Sub Route
52
+ get '/sub/simple', hello: 'Test'
53
+ expect(MultiJson.load(last_response.body)['time']).to eq(Time.now.to_s)
54
+
55
+ # Sleep...
56
+ sleep 2
57
+
58
+ # Cached response
59
+ get '/simple'
60
+ expect(MultiJson.load(last_response.body)['time']).to eq(first['time'])
61
+
62
+ # Sleep...
63
+ sleep 2
64
+
65
+ # Test a Post request
66
+ post '/post', {hello: 'Test'}.to_json, 'CONTENT_TYPE' => 'application/json'
67
+ first_post = MultiJson.load(last_response.body)
68
+ expect(first_post['time']).to eq(Time.now.to_s)
69
+
70
+ # Sleep...
71
+ sleep 2
72
+
73
+ # Test a Post request
74
+ post '/post', {hello: 'Test'}.to_json, 'CONTENT_TYPE' => 'application/json'
75
+ expect(MultiJson.load(last_response.body)['time']).to eq(first_post['time'])
76
+
77
+ # Sleep...
78
+ sleep 2
79
+
80
+ # Execute the route
81
+ get '/simple', break: true
82
+
83
+ # Get it infos
84
+ expect(MultiJson.load(last_response.body)['time']).to eq(Time.now.to_s)
85
+ end
86
+ end
@@ -0,0 +1,37 @@
1
+ # Spec dependencies
2
+ require 'grape_cache'
3
+ require 'grape'
4
+ require 'rack/test'
5
+
6
+ # Configure the RSpec
7
+ RSpec.configure do |config|
8
+ config.order = 'random'
9
+ config.seed = '12345'
10
+ config.include Rack::Test::Methods
11
+ end
12
+
13
+ # Define a Store
14
+ class CacheStore
15
+ # Storage
16
+ attr_reader :storage
17
+
18
+ # Initialize
19
+ def initialize
20
+ @storage = {}
21
+ end
22
+
23
+ #
24
+ def write(key, value, options)
25
+ @storage[key] = value
26
+ end
27
+
28
+ #
29
+ def read(key)
30
+ @storage[key]
31
+ end
32
+
33
+ # Exist?
34
+ def exist?(key)
35
+ @storage.key?(key)
36
+ end
37
+ end
metadata ADDED
@@ -0,0 +1,109 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: grape_cache
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0
5
+ platform: ruby
6
+ authors:
7
+ - Gabriel Corado
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: multi_json
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: oj
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: grape
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.16'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.16'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ description: ''
70
+ email: gabrielcorado@mail.com
71
+ executables: []
72
+ extensions: []
73
+ extra_rdoc_files: []
74
+ files:
75
+ - ".travis.yml"
76
+ - Dockerfile
77
+ - Gemfile
78
+ - Gemfile.lock
79
+ - README.md
80
+ - grape_cache.gemspec
81
+ - lib/grape_cache.rb
82
+ - lib/grape_cache/middleware.rb
83
+ - lib/grape_cache/version.rb
84
+ - spec/grape_cache/middleware_spec.rb
85
+ - spec/spec_helper.rb
86
+ homepage: http://github.com/gabrielcorado/venduitz
87
+ licenses: []
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubyforge_project:
105
+ rubygems_version: 2.4.5.1
106
+ signing_key:
107
+ specification_version: 4
108
+ summary: ''
109
+ test_files: []