added registry module, where one can register by a tag; introducing transcoder, modules which can encode and decode; using them for the body/form/json bodies

This commit is contained in:
HoneyryderChuck 2017-12-06 19:49:26 +00:00
parent e7b943ba39
commit fbdd7e2dd8
7 changed files with 187 additions and 13 deletions

View File

@ -1,8 +1,11 @@
# frozen_string_literal: true
require "httpx/version"
require "httpx/errors"
require "httpx/callbacks"
require "httpx/registry"
require "httpx/transcoder"
require "httpx/options"
require "httpx/timeout"
require "httpx/connection"

84
lib/httpx/registry.rb Normal file
View File

@ -0,0 +1,84 @@
# frozen_string_literal: true
module HTTPX
# Adds a general-purpose registry API to a class. It is designed to be a
# configuration-level API, i.e. the registry is global to the class and
# should be set on **boot time**.
#
# It is used internally to associate tags with handlers.
#
# ## Register/Fetch
#
# One is strongly advised to register handlers when creating the class.
#
# There is an instance-level method to retrieve from the registry based
# on the tag:
#
# class Server
# include HTTPX::Registry
#
# register "tcp", TCPHandler
# register "ssl", SSLHandlers
# ...
#
#
# def handle(uri)
# scheme = uri.scheme
# handler = registry(scheme) #=> TCPHandler
# handler.handle
# end
# end
#
module Registry
# Base Registry Error
Error = Class.new(Error)
def self.extended(klass)
super
klass.extend(ClassMethods)
end
def self.included(klass)
super
klass.extend(ClassMethods)
klass.__send__(:include, InstanceMethods)
end
# Class Methods
module ClassMethods
# @param [Object] tag the handler identifier in the registry
# @return [Symbol, String, Object] the corresponding handler (if Symbol or String,
# will assume it referes to an autoloaded module, and will load-and-return it).
#
def registry(tag = nil)
@registry ||= {}
return @registry if tag.nil?
handler = @registry.fetch(tag)
raise(Error, "#{tag} is not registered in #{self}") unless handler
case handler
when Symbol, String
const_get(handler)
else
handler
end
end
# @param [Object] tag the identifier for the handler in the registry
# @return [Symbol, String, Object] the handler (if Symbol or String, it is
# assumed to be an autoloaded module, to be loaded later)
#
def register(tag, handler)
registry[tag] = handler
end
end
# Instance Methods
module InstanceMethods
# delegates to HTTPX::Registry#registry
def registry(tag)
self.class.registry(tag)
end
end
end
end

View File

@ -1,8 +1,5 @@
# frozen_string_literal: true
require "http/form_data"
require "json"
module HTTPX
class Request
METHODS = [
@ -72,18 +69,14 @@ module HTTPX
@headers = headers
@body = case
when options.body
options.body
Transcoder.registry("body").encode(options.body)
when options.form
form = HTTP::FormData.create(options.form)
@headers["content-type"] = form.content_type
@headers["content-length"] = form.content_length
form
Transcoder.registry("form").encode(options.form)
when options.json
body = JSON.dump(options.json)
@headers["content-type"] = "application/json; charset=#{body.encoding.name.downcase}"
@headers["content-length"] = body.bytesize
body
Transcoder.registry("json").encode(options.json)
end
@headers["content-type"] ||= @body.content_type
@headers["content-length"] ||= @body.content_length
end
def each(&block)

11
lib/httpx/transcoder.rb Normal file
View File

@ -0,0 +1,11 @@
# frozen_string_literal: true
module HTTPX
module Transcoder
extend Registry
end
end
require "httpx/transcoder/body"
require "httpx/transcoder/form"
require "httpx/transcoder/json"

View File

@ -0,0 +1,36 @@
# frozen_string_literal: true
module HTTPX::Transcoder
module Body
module_function
class Encoder
def initialize(body)
@raw = body
end
def to_str
@raw
end
def content_length
if @raw.respond_to?(:bytesize)
@raw.bytesize
elsif @raw.respond_to?(:size)
@raw.size
else
raise Error, "cannot determine size of body: #{@raw.inspect}"
end
end
def content_type
"application/octet-stream"
end
end
def encode(body)
Encoder.new(body)
end
end
register "body", Body
end

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
require "http/form_data"
module HTTPX::Transcoder
module Form
module_function
def encode(form)
HTTP::FormData.create(form)
end
end
register "form", Form
end

View File

@ -0,0 +1,33 @@
# frozen_string_literal: true
require "json"
module HTTPX::Transcoder
module JSON
module_function
class Encoder
def initialize(json)
@raw = ::JSON.dump(json)
@charset = @raw.encoding.name.downcase
end
def content_type
"application/json; charset=#{@charset}"
end
def content_length
@raw.bytesize
end
def to_str
@raw
end
end
def encode(json)
Encoder.new(json)
end
end
register "json", JSON
end