class Travis::Client::Session

def access_token=(token)

def access_token=(token)
  clear_cache!
  @access_token = token
  headers['Authorization'] = "token #{token}"
  headers.delete('Authorization') unless token
end

def adapter_version(adapter)

def adapter_version(adapter)
  version = Object.const_get(adapter).const_get("VERSION")
  [*version].join('.')
rescue Exception
  "unknown"
end

def agent_info=(info)

def agent_info=(info)
  @agent_info = [info].flatten.freeze
  set_user_agent
end

def cached(*keys)

def cached(*keys)
  last  = keys.pop
  cache = keys.inject(@cache) { |store, key| store[key] ||= {} }
  cache[last] ||= yield
end

def clear_cache

def clear_cache
  reset_entities
  clear_find_cache
  self
end

def clear_cache!

def clear_cache!
  reset_entities
  @cache.clear
  self
end

def clear_find_cache

def clear_find_cache
  subcaches do |subcache|
    subcache.delete_if { |k, v| k != :id }
  end
end

def config

def config
  @config ||= get_raw('/config')['config'] || {}
end

def connection=(connection)

def connection=(connection)
  clear_cache!
  connection.headers.merge! headers
  @config     = nil
  @connection = connection
  @headers    = connection.headers
end

def create_entity(type, data)

def create_entity(type, data)
  data   = { type.id_field => data } if type.id? data
  id     = type.cast_id(data.fetch(type.id_field)) unless type.weak?
  entity = id ? cached(type, :id, id) { type.new(self, id) } : type.new(self, nil)
  entity.update_attributes(data)
  entity
end

def delete(*args)

def delete(*args)
  load delete_raw(*args)
end

def delete_raw(*args)

def delete_raw(*args)
  raw(:delete, *args)
end

def error_message(e)

def error_message(e)
  message = e.response[:body].to_str rescue e.message
  JSON.parse(message).fetch('error').fetch('message') rescue message
end

def faraday_adapter=(adapter)

def faraday_adapter=(adapter)
  @faraday_adapter = adapter
  self.uri &&= uri
  set_user_agent
end

def fetch_many(entity, params = {})

def fetch_many(entity, params = {})
  get("/#{entity.many}/", params)[entity.many]
end

def fetch_one(entity, id = nil)

def fetch_one(entity, id = nil)
  get("/#{entity.many}/#{id}")[entity.one]
end

def find_many(entity, args = {})

def find_many(entity, args = {})
  raise Travis::Client::Error, "cannot fetch #{entity}" unless entity.respond_to?(:many) and entity.many
  cached(entity, :many, args) { fetch_many(entity, args) }
end

def find_one(entity, id = nil)

def find_one(entity, id = nil)
  raise Travis::Client::Error, "cannot fetch #{entity}" unless entity.respond_to?(:many) and entity.many
  return create_entity(entity, entity.id_field => id) if entity.id? id
  cached(entity, :by, id) { fetch_one(entity, id) }
end

def find_one_or_many(entity, args = nil)

def find_one_or_many(entity, args = nil)
  raise Travis::Client::Error, "cannot fetch #{entity}" unless entity.respond_to?(:many) and entity.many
  cached(entity, :one_or_many, args) do
    path       = "/#{entity.many}"
    path, args = "#{path}/#{args}", {} unless args.is_a? Hash
    result     = get(path, args)
    one        = result[entity.one]
    if result.include? entity.many
      Array(one) + Array(result[entity.many])
    else
      one
    end
  end
end

def get(*args)

def get(*args)
  load get_raw(*args)
end

def get_raw(*args)

def get_raw(*args)
  raw(:get, *args)
end

def headers=(headers)

def headers=(headers)
  clear_cache!
  connection.headers = headers if connection
  @headers = headers
end

def initialize(options = Travis::Client::ORG_URI)

def initialize(options = Travis::Client::ORG_URI)
  @headers         = {}
  @cache           = {}
  @instruments     = []
  @agent_info      = []
  @config          = nil
  @faraday_adapter = defined?(Typhoeus) ? :typhoeus : :net_http
  @ssl             = SSL_OPTIONS
  options = { :uri => options } unless options.respond_to? :each_pair
  options.each_pair { |key, value| public_send("#{key}=", value) }
  raise ArgumentError, "neither :uri nor :connection specified" unless connection
  headers['Accept'] = 'application/json; version=2'
  set_user_agent
end

def inspect

def inspect
  "#<#{self.class}: #{uri}>"
end

def instrument(&block)

def instrument(&block)
  instruments << block
end

def instrumented(name, *args)

def instrumented(name, *args)
  name   = [name, *args.map(&:inspect)].join(" ") if args.any?
  result = nil
  chain  = instruments + [proc { |n,l| result = yield }]
  lift   = proc { chain.shift.call(name, lift) }
  lift.call
  result
end

def load(data)

def load(data)
  result = {}
  (data || {}).each_pair do |key, value|
    type = Entity.subclass_for(key)
    if value.respond_to? :to_ary
      result[key] = value.to_ary.map { |e| create_entity(type, e) }
    else
      result[key] = create_entity(type, value)
    end
  end
  result
end

def post_raw(*args)

def post_raw(*args)
  raw(:post, *args)
end

def private_channels?

def private_channels?
  access_token and user.channels != ['common']
end

def put_raw(*args)

def put_raw(*args)
  raw(:put, *args)
end

def raw(verb, url, *args)

def raw(verb, url, *args)
  url    = url.sub(/^\//, '')
  result = instrumented(verb.to_s.upcase, url, *args) do
    connection.public_send(verb, url, *args) do |request|
      next if request.path !~ /^https?:/ or request.path.start_with? api_endpoint
      request.headers.delete("Authorization")
    end
  end
  case result.status
  when 0             then raise Travis::Client::Error, 'SSL error: could not verify peer'
  when 200..299      then JSON.parse(result.body) rescue result.body
  when 301, 303      then raw(:get, result.headers['Location'])
  when 302, 307, 308 then raw(verb, result.headers['Location'])
  when 404           then raise Travis::Client::NotFound, result.body
  when 400..499      then raise Travis::Client::Error,    result.status
  when 500..599      then raise Travis::Client::Error,    "server error (#{result.status})"
  else raise Travis::Client::Error, "unhandled status code #{result.status}"
  end
end

def reload(entity)

def reload(entity)
  reset(entity)
  result = fetch_one(entity.class, entity.id)
  entity.update_attributes(result.attributes) if result.attributes != entity.attributes
  result
end

def reset(entity)

def reset(entity)
  entity.attributes.clear
  entity
end

def reset_entities

def reset_entities
  subcaches do |subcache|
    subcache[:id].each_value { |e| e.attributes.clear } if subcache.include? :id
  end
end

def session

def session
  self
end

def set_user_agent

def set_user_agent
  adapter = Array === faraday_adapter ? faraday_adapter.first : faraday_adapter
  adapter = adapter.to_s.capitalize.gsub(/_http_(.)/) { "::HTTP::#{$1.upcase}" }.gsub(/_http/, '::HTTP')
  headers['User-Agent'] = "Travis/#{Travis::VERSION} (#{Travis::Tools::System.description(agent_info)}) Faraday/#{Faraday::VERSION} #{adapter}/#{adapter_version(adapter)}"
end

def ssl=(options)

def ssl=(options)
  @ssl     = options.dup.freeze
  self.uri = uri if uri
end

def subcaches

def subcaches
  @cache.each_value do |subcache|
    yield subcache if subcache.is_a? Hash
  end
end

def uri

def uri
  connection.url_prefix.to_s if connection
end

def uri=(uri)

def uri=(uri)
  clear_cache!
  self.connection = Faraday.new(:url => uri, :ssl => ssl) do |faraday|
    faraday.request  :url_encoded
    faraday.adapter(*faraday_adapter)
  end
end