class A4Tools::CachingClient

Attributes

cache_methods[RW]
query_methods[RW]
timeout[RW]

Public Class Methods

cache(name, params={}, &blk) click to toggle source
# File lib/clients/caching_client.rb, line 13
def cache(name, params={}, &blk)
  @cache_methods ||= {}
  @cache_methods[name] = {
    params:params,
    block:blk
  }
end
new(destination, username=nil, password=nil, timeout=300) click to toggle source
Calls superclass method A4Tools::AcresClient::new
# File lib/clients/caching_client.rb, line 30
def initialize(destination, username=nil, password=nil, timeout=300)
  super(destination, username, password)
  @timeout = timeout
  @query_cache = {}
  @cache = {}
  @update_thread = nil
  @query_methods = self.class.query_methods.clone rescue {}
end
query(name, params={}, &blk) click to toggle source
# File lib/clients/caching_client.rb, line 21
def query(name, params={}, &blk)
  @query_methods ||= {}
  @query_methods[name] = {
    params:params,
    block:blk
  }
end

Public Instance Methods

[](key) click to toggle source
# File lib/clients/caching_client.rb, line 74
def [](key)
  cache(key)
end
cache(key, sync=false) click to toggle source
# File lib/clients/caching_client.rb, line 78
def cache(key, sync=false)
  if sync then
    refresh unless fresh? # force caller to wait until we've refreshed
  else
    refresh_async unless fresh? # kick off an async update if we're stale, but still return what we have
  end
  
  @cache[key]
end
dirty() click to toggle source

Force a cache update next time someone makes a request

# File lib/clients/caching_client.rb, line 109
def dirty
  @cache_update_ts = nil
  @query_methods.each { |method, defn| defn[:update] = nil }
end
ensure_fresh() click to toggle source
# File lib/clients/caching_client.rb, line 114
def ensure_fresh
  refresh unless fresh?
end
fresh?() click to toggle source
# File lib/clients/caching_client.rb, line 118
def fresh?
  not timestamp_expired?(@cache_update_ts)
end
query(method, *args) click to toggle source
# File lib/clients/caching_client.rb, line 88
def query(method, *args)
  defn = @query_methods[method]
  raise UpdateError, "#{method}: Unsupported method" if defn.nil?

  if defn[:params][:authenticate] then
    raise(AuthenticationError, "Unable to authenticate as #{username}") unless authenticate_if_needed
  end

  @query_cache[method] ||= {}
  item = @query_cache[method] || {}
  return item[:cache] unless timestamp_expired?(item[:update])

  request = defn[:block].nil? ? nil : defn[:block].call(*args)
  result = update_from_method(method, request)
  item[:update] = Time.now
  item[:cache] = result
  @query_cache[method] = item
  result
end
refresh() click to toggle source
# File lib/clients/caching_client.rb, line 138
def refresh
  unless @update_thread.nil? then
    # We already have an update thread in flight; block on that
    @update_thread.join unless @update_thread == Thread.current
    return @cache
  end

  @update_thread = Thread.current
  @cache = send_update_request
  @cache_update_ts = Time.now
  @update_thread = nil
end
refresh_async() click to toggle source
# File lib/clients/caching_client.rb, line 126
def refresh_async
  # Create a new thread to get the update cache, unless one is already in flight
  if @update_thread.nil? then
    begin
      Thread.new { refresh }
    rescue AuthenticationError, UpdateError => exc
      signal(:error, exc.message)
    end
  end
  nil
end
send_update_request() click to toggle source
# File lib/clients/caching_client.rb, line 39
def send_update_request
  new_cache = {}
  return {} if self.class.cache_methods.nil?
  self.class.cache_methods.each do |method, defn|
    if defn[:params][:authenticate] then
      raise(AuthenticationError, "Unable to authenticate") unless authenticate_if_needed
    end

    request = defn[:block].nil? ? nil : defn[:block].call
    new_cache[method] = update_from_method(method, request)
  end
  new_cache
end
timestamp_expired?(ts) click to toggle source
# File lib/clients/caching_client.rb, line 122
def timestamp_expired?(ts)
  ts.nil? or (Time.now - ts) > @timeout
end
update_from_method(method, request, type=nil) click to toggle source
# File lib/clients/caching_client.rb, line 53
def update_from_method(method, request, type=nil)
  type ||= talk.expand_name(talk.method_named(method.to_s)[0][:request])
  inject_token(request, false, type)
  body = request.nil? ? empty_query(method) : wrapped_message(method.to_s, type, request)
  result = send_message(body)
  begin
    parsed = symbolify(JSON.parse(result.body))
    raise(UpdateError, "#{method}: #{parsed[:error][:code]} #{parsed[:error][:message]}") unless parsed[:error].nil?
    raise(UpdateError, "#{method}: Server did not include a response field") if parsed[:result].nil?
    toplevel = parsed[:result]
    if toplevel.has_key?(:body) and toplevel.has_key?(:className) then
      toplevel = toplevel[:body]
      toplevel[:__class] = parsed[:result][:className] # strip namedobjectwrapper
    end
    toplevel
  rescue JSON::ParserError
    truncated = result.body.length > 100 ? result.body[0..99] + "..." : result.body
    raise(UpdateError, "#{method}: Server returned non-JSON response: (#{result.body.length} bytes) '#{truncated}'")
  end
end