lib/and-son/client.rb in and-son-0.6.1 vs lib/and-son/client.rb in and-son-0.7.0

- old
+ new

@@ -1,145 +1,113 @@ -require 'benchmark' -require 'logger' -require 'ostruct' -require 'sanford-protocol' -require 'and-son/connection' -require 'and-son/response' +require 'and-son/call_runner' require 'and-son/stored_responses' module AndSon - module CallRunnerMethods + module Client - # define methods here to allow configuring call runner params. be sure to - # use `tap` to return whatever instance `self.call_runner` returns so you - # can method-chain. `self.call_runner` returns a new runner instance if - # called on a client, but returns the chained instance if called on a runner - - def timeout(seconds) - self.call_runner.tap{|r| r.timeout_value = seconds.to_f } + def self.new(host, port) + if !ENV['ANDSON_TEST_MODE'] + AndSonClient.new(host, port) + else + TestClient.new(host, port) + end end - def params(hash = nil) - if !hash.kind_of?(Hash) - raise ArgumentError, "expected params to be a Hash instead of a #{hash.class}" + def self.included(klass) + klass.class_eval do + include CallRunner::InstanceMethods + include InstanceMethods end - self.call_runner.tap{|r| r.params_value.merge!(self.stringify_keys(hash)) } end - def logger(passed_logger) - self.call_runner.tap{|r| r.logger_value = passed_logger } - end + module InstanceMethods - protected + attr_reader :host, :port - def stringify_keys(hash) - hash.inject({}){|h, (k, v)| h.merge({ k.to_s => v }) } + def initialize(host, port) + @host, @port = host, port + end + end end - class Client - include CallRunnerMethods + class AndSonClient + include Client - DEFAULT_TIMEOUT = 60 #seconds - - attr_reader :host, :port, :responses - - def initialize(host, port) - @host, @port = host, port - @responses = AndSon::StoredResponses.new - end - # proxy the call method to the call runner def call(*args, &block); self.call_runner.call(*args, &block); end def call_runner - # always start with this default CallRunner - CallRunner.new({ - :host => host, - :port => port, - :timeout_value => (ENV['ANDSON_TIMEOUT'] || DEFAULT_TIMEOUT).to_f, - :params_value => {}, - :logger_value => NullLogger.new, - :responses => @responses, - }) + AndSon::CallRunner.new(host, port) end + + def hash + self.call_runner.hash + end + + def ==(other) + other.kind_of?(self.class) ? self.hash == other.hash : super + end + alias :eql? :== + end - class CallRunner < OpenStruct - # { :host, :port, :timeout_value, :params_value, :logger_value, :responses } - include CallRunnerMethods + class TestClient + include Client - # chain runner methods by returning itself - def call_runner; self; end + attr_accessor :timeout_value, :params_value, :logger_value + attr_reader :calls, :responses + def initialize(host, port) + super + @params_value = {} + @calls = [] + @responses = AndSon::StoredResponses.new + end + def call(name, params = nil) params ||= {} - if !params.kind_of?(Hash) - raise ArgumentError, "expected params to be a Hash instead of a #{params.class}" - end - client_response = nil - benchmark = Benchmark.measure do - client_response = self.responses.find(name, params) if ENV['ANDSON_TEST_MODE'] - client_response ||= self.call!(name, params) - end - - summary_line = SummaryLine.new({ - 'time' => RoundedTime.new(benchmark.real), - 'status' => client_response.protocol_response.code, - 'host' => "#{self.host}:#{self.port}", - 'service' => name, - 'params' => params - }) - self.logger_value.info("[AndSon] #{summary_line}") - + response = self.responses.get(name, params) + self.calls << Call.new(name, params, response.protocol_response) if block_given? - yield client_response.protocol_response + yield response.protocol_response else - client_response.data + response.data end end - def call!(name, params) - call_params = self.params_value.merge(params) - AndSon::Connection.new(host, port).open do |connection| - connection.write(Sanford::Protocol::Request.new(name, call_params).to_hash) - connection.close_write - if !connection.peek(timeout_value).empty? - AndSon::Response.parse(connection.read(timeout_value)) - else - raise AndSon::ConnectionClosedError.new - end - end + def call_runner; self; end + + def add_response(*args, &block) + self.responses.add(*args, &block) end - end - - class ConnectionClosedError < RuntimeError - def initialize - super "The server closed the connection, no response was written." + def remove_response(*args) + self.responses.remove(*args) end - end - class NullLogger - ::Logger::Severity.constants.each do |name| - define_method(name.downcase){|*args| } # no-op + def reset + self.calls.clear + self.responses.remove_all end - end - module SummaryLine - def self.new(line_attrs) - attr_keys = %w{time status host service params} - attr_keys.map{ |k| "#{k}=#{line_attrs[k].inspect}" }.join(' ') + def hash + [ self.host, + self.port, + self.timeout_value, + self.params_value, + self.logger_value + ].hash end - end - module RoundedTime - ROUND_PRECISION = 2 - ROUND_MODIFIER = 10 ** ROUND_PRECISION - def self.new(time_in_seconds) - (time_in_seconds * 1000 * ROUND_MODIFIER).to_i / ROUND_MODIFIER.to_f + def ==(other) + other.kind_of?(self.class) ? self.hash == other.hash : super end + alias :eql? :== + + Call = Struct.new(:request_name, :request_params, :response) + end end