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