Module: Grape::App::Helpers::Caching

Defined in:
lib/grape/app/helpers/caching.rb

Overview

Instance Method Summary collapse

Instance Method Details

#cache_control(max_age: nil, no_cache: false, public: false, must_revalidate: false, stale_while_revalidate: nil, stale_if_error: nil, extras: nil) ⇒ Object



88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
# File 'lib/grape/app/helpers/caching.rb', line 88

def cache_control(max_age: nil, no_cache: false, public: false, must_revalidate: false, stale_while_revalidate: nil, stale_if_error: nil, extras: nil)
  extras = extras.map {|k, v| "#{k}=#{v}" } if extras.is_a?(Hash)
  opts   = []

  if no_cache
    opts << 'public' if public
    opts << 'no-cache'
  else
    opts << "max-age=#{max_age.to_i}" if max_age
    opts << (public ? 'public' : 'private')
    opts << 'must-revalidate' if must_revalidate
    opts << "stale-while-revalidate=#{stale_while_revalidate.to_i}" if stale_while_revalidate
    opts << "stale-if-error=#{stale_if_error.to_i}" if stale_if_error
  end
  opts.concat(extras) if extras

  header 'Cache-Control', opts.join(', ')
end

#expires_in(seconds, public: false, must_revalidate: false, stale_while_revalidate: nil, stale_if_error: nil, extras: {}) ⇒ Object

Sets an HTTP 1.1 Cache-Control header. Defaults to ‘private`.

This method will overwrite an existing Cache-Control header. See www.w3.org/Protocols/rfc2616/rfc2616-sec14.html for more possibilities.

The method will also ensure an HTTP Date header for client compatibility.

Examples:


expires_in 20.minutes
expires_in 3.hours, public: true
expires_in 3.hours, public: true, must_revalidate: true


68
69
70
71
72
73
74
75
76
77
78
79
# File 'lib/grape/app/helpers/caching.rb', line 68

def expires_in(seconds, public: false, must_revalidate: false, stale_while_revalidate: nil, stale_if_error: nil, extras: {})
  header 'Date', Time.now.httpdate

  cache_control(
    max_age: seconds,
    public: public,
    must_revalidate: must_revalidate,
    stale_while_revalidate: stale_while_revalidate,
    stale_if_error: stale_if_error,
    extras: extras,
  )
end

#expires_now(public: false) ⇒ Object

Sets an HTTP 1.1 Cache-Control header of ‘no-cache`. This means the resource will be marked as stale, so clients must always revalidate. Intermediate/browser caches may still store the asset.



84
85
86
# File 'lib/grape/app/helpers/caching.rb', line 84

def expires_now(public: false)
  cache_control(no_cache: true, public: public)
end

#fresh_when(object = nil, etag: nil, last_modified: nil, last_modified_field: :updated_at, **cache_control) ⇒ Object

Sets the ‘etag`, or `last_modified`, or both on the response and renders a “304 Not Modified” response if the request is already fresh.

Examples:


get '/articles/:id' do
  article = Article.find(params[:id])
  fresh_when(article, public: true)

  article
end


20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/grape/app/helpers/caching.rb', line 20

def fresh_when(object = nil, etag: nil, last_modified: nil, last_modified_field: :updated_at, **cache_control)
  etag ||= object
  last_modified = object.try(last_modified_field) || object.try(:maximum, last_modified_field) if last_modified.nil?
  etag = ActiveSupport::Digest.hexdigest(ActiveSupport::Cache.expand_cache_key(etag))
  header 'ETag', etag
  header 'Last-Modified', last_modified.httpdate if last_modified
  cache_control(**cache_control) unless cache_control.empty?

  if_modified_since = headers['if-modified-since']
  if_modified_since = Time.rfc2822(if_modified_since) rescue nil if if_modified_since # rubocop:disable Style/RescueModifier
  if_none_match     = headers['if-none-match']
  return unless if_modified_since || if_none_match

  fresh = true
  fresh &&= last_modified && if_modified_since >= last_modified if if_modified_since
  fresh &&= if_none_match == etag if if_none_match
  error! 'Not Modified', 304 if fresh
end

#stale?(object = nil, **freshness_opts) ⇒ Boolean

Sets the ‘etag` and/or `last_modified` on the response and checks it against the client request. If the request doesn’t match the options provided, the request is considered stale and should be generated from scratch. Otherwise, it’s fresh and we don’t need to generate anything and reply with ‘304 Not Modified`.

@example:

get '/articles/:id' do
  article = Article.find(params[:id])
  stats = article.really_expensive_call if stale?(article)
end

Returns:

  • (Boolean)


51
52
53
54
# File 'lib/grape/app/helpers/caching.rb', line 51

def stale?(object = nil, **freshness_opts)
  fresh_when(object, **freshness_opts)
  true
end