Home > Enterprise >  Conditional use of rails cache fetch syntax when response body has error message
Conditional use of rails cache fetch syntax when response body has error message

Time:06-08

Setup

I'm aware of how to do a Rails.exist ? Rails.read : fetch & rails.write but rails has a nice Rails.cache.fetch syntax that will automatically check if the cache key exists and is valid, return that if true, else run through the block and save to the cache.

Examples

For example (long way)

def search(param1)
  if Rails.cache.exist?("key", namespace: "example")
    response = Rails.cache.read("key", namespace: "example")
  else
    conn = faraday_helper(url: "search/url")

    response = conn.post do |req|
      req.body = { key: param1 }
    end

    Rails.cache.write("key", response, namespace: "example", expires_in: 1.hour) if response.success?
  end

  response
end

Short hand using fetch syntax

Rails.cache.fetch("key", namespace: "example") do
  conn = faraday_helper(url: "search/url")

  response = conn.post do |req|
    req.body = { key: value }
  end

  response
end

This nice short hand does the same thing as the long way, except for the if response.success? which is what I'm interested in. If I make a call to this api, and the response is a 400 with a body of {"error": "invalid value for <key>"} the short way will cache that error response, which is no good. Edit for clarity: I do want that response body with the error, but I don't want to cache.

Question

Does anyone know of a way to pass a lambda or something to conditionally cache using the shorthand fetch syntax? I'd rather not have this method return nil when the cache fails because I want that error message in the response body, and deleting the cache if the response isn't a success seems to defeat the purpose of the entire thing (unless it's faster?)

CodePudding user response:

How I've done it using fetch syntax

  def search(param1:)
    bailed_resp = nil
    cached_resp = Rails.cache.fetch("key", namespace: "example", skip_nil: true, expires_in: 1.hour) do
      conn = faraday_helper(url: "search/url")

      response = conn.post do |req|
        req.body = { key: param1 }
      end

      # Save the response for later and bail on caching unless response.success is true
      bailed_resp = response
      break unless response.success?
      response
    end

    # If we bailed on the fetch, use the bailed response, otherwise use the new/fetched cache.
    cached_resp == nil ? ResponseWrappers::Service.new(bailed_resp) : ResponseWrappers::Service.new(cached_resp)
  end

Though this does work, I fail to see how it's any different than the long form syntax, which for reference:

def search(param1:)
  if Rails.cache.exist?("key", namespace: "example")
    response = Rails.cache.read("key", namespace: "example")
  else
    conn = faraday_helper(url: "search/url")

    response = conn.post do |req|
      req.body = { key: param1 }
    end

    Rails.cache.write("key", response, namespace: "example", expires_in: 1.hour) if response.success?
  end

  response
end

Is anyone able to give additional information on the differences between the two and/or if it's negligible?

CodePudding user response:

You can just break from this block

Rails.cache.fetch("key", namespace: "example") do
  conn = faraday_helper(url: "search/url")

  response = conn.post do |req|
    req.body = { key: value }
  end

  break unless response.success?

  response
end

In this case nothing will be written by this key for failure response

But if you try to repeat this code and response will be ok, it will be written

If you want to use this construction in some method and need this method to return response, you change it to:

def search
  Rails.cache.fetch("key", namespace: "example") do
    conn = faraday_helper(url: "search/url")

    response = conn.post do |req|
      req.body = { key: value }
    end

    return response unless response.success?

    response
  end
end

And process result outside the method

  • Related