Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Client dependency injection + Chainable is not chainable #7

Merged
merged 20 commits into from Feb 13, 2012

Conversation

blambeau
Copy link
Contributor

Hi!

My aim is here is to allow injection the Client class to use in an easy way when including the Chainable module.
The reason of each commit is explained in the git comment.

By the way, the last commit shows that the Chainable module is not really chainable as implemented so far. I don't know how to fix this exactly. One idea that comes in mind is to mimic Rack with a chain of middlewares responding to request(verb,uri,options). I've implemented that idea in an experimental branch in my repo, but I'm not really happy with it so far.

Any idea?

Chainable and Client interfaces were almost the same and their
differences seem purely accidental. Having the same interface
everywhere seem easier and will allow client injection later.
The Client class already handles Net::HTTP bridging and most of
the semantics of the options. It makes sense to frees it from also
having to provide http helpers, which are available through Chainable.

Removing the helper methods does not hurt (provided Client is part of
the private API?). Client#request become *the method* for which injection
will take place later.
The :response option handling is already spread between Chainable
and Client. It seems it makes sense to avoid unnecessary secret
spreading.

By the way, a Response object is also returned on HEAD requests made
through Chainable#request directly.
@blambeau
Copy link
Contributor Author

Another plan to fix the Chainable stuff could be as follows:

  1. Client is itself Chainable and takes an options Hash at initialize time.
  2. The Chainable DSL systematically creates a new Client instance, merging previous options with new options related the requested feature.

This plan is probably cleaner that mimicing Rack middlewares (?)

The Options class encapsulates the management of options. For now,
it implements the following ones: :response, :form, :headers,
:before, :after.
No Hash-like write-support is added so far, but it could be later.
The merge operator takes care of merging :callbacks and :headers
in a POLS way. For now, :form is not merged as a Hash and the value
at right is always used.
Previously Chainable implemented a different heuristics than Client
for :response. Chainable used :object on HEAD and :parsed_body on
other verbs. Client always used :object.

This commit removes this difference and introduces :auto as option
value. It is the default one, it is implemented in Client directly
and it corresponds to the Chainable behavior above. This means that
{:response => :object} is necessary on Client to get its previous
behavior (broken private API, therefore).

By the way, we could restore the previous behavior simply by setting
:object as default in Options and forcing something else through
default_options in Chainable and/or Http. It seemed simpler to get the
auto behavior immediately in Client, though.
This commit fixes the chainable behavior. It works by simply
instantiating a Client instance and branching on default options.

Chainable#default_headers and default_callbacks will be restored
in a future patch. default_client has been removed, as monkey
patching branch allows achieving the same goal as Client injection
before.
Parameters and EventCallback are no longer used. This commit also
restore the default_headers and default_callbacks accessors. The
presence of the writers is much arguable IMHO.
@blambeau
Copy link
Contributor Author

I've added a couple of commits that fix the chainable stuff.

This makes the patch quite big to review, sorry. The commit comments should help. Otherwise, have a look at the final result, the code is quite clear now. The whole stuff occurs in the Options class.

@leonbreedt
Copy link
Contributor

Looks nice, what you did with Options looks pretty close to what I was thinking (removal of EventCallbacks class, using just the one class to store configuration of the chain up to that point).

It also should fix the bug that was in my branch, which caused custom headers added by a #with before #on to be lost.

@tarcieri
Copy link
Member

Hi there, sorry I haven't responded to this yet, I've kind of been going balls to the wall on nio4r and Celluloid::IO. I'll look over this and let you know.

One thing I did notice was d0a80c6. I would like to make the options hash the default means of dependency injection (which generally follows the whole idea of the options hash as the universal abstraction), not that there's anything suspect about that commit per se.

I'd like for people to be able to specify :socket => TCPSocket, as I'd like for people to be able to specify :socket => Celluloid::IO::TCPSocket to be able to do "evented" HTTP client stuff.

That will involve a rewrite of the client library with http_parser though.

tarcieri added a commit that referenced this pull request Feb 13, 2012
Client dependency injection + Chainable is not chainable
@tarcieri tarcieri merged commit e28042e into httprb:master Feb 13, 2012
@tarcieri
Copy link
Member

Overall this looks pretty good, so I'm merging it

@blambeau
Copy link
Contributor Author

Thanks for merging this and for adding me to the committers.

I'll probably use pull requests in the future though, at least for possibly controversial features and because they provide a good place for discussing possible directions.

Not having an HTTP::Request abstraction prevents us from properly releasing 0.2.0 (say). I'll try to take some time to get into it.

@tarcieri
Copy link
Member

Ideally what I'd like to have is an Http::Request that can be shared between client and server, effectively just data objects, and abstracting the concerns for creating these objects elsewhere (the server I'm writing has a RequestParser that builds the actual Request object).

I'd like to factor the server code I'm writing into the Http library as well so it can be a single unified client/server library built on top of http_parser.rb: https://github.com/tmm1/http_parser.rb

I'd like for the server components to be an extraction from a quasi-real-world server though.

You're welcome to spike out Http::Request though, just be prepared for some changed to it when I merge in the server components.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

3 participants