Permalink
Browse files

Improve the way we handle middleware; expose Faraday’s connection bui…

…lder
  • Loading branch information...
1 parent b93a382 commit b73abec787cdd5762027e16f043bb657cb8c999b @remiprev committed Apr 30, 2012
Showing with 71 additions and 71 deletions.
  1. +47 −8 README.md
  2. +17 −6 lib/her/api.rb
  3. +2 −0 lib/her/middleware.rb
  4. +1 −1 lib/her/version.rb
  5. +4 −56 spec/api_spec.rb
View
@@ -60,7 +60,7 @@ Since Her relies on [Faraday](https://github.com/technoweenie/faraday) to send H
### Authentication
-Her doesn’t support any kind of authentication. However, it’s very easy to implement one with a request middleware. Using the `add_middleware` key, we add it to the default list of middleware.
+Her doesn’t support any kind of authentication. However, it’s very easy to implement one with a request middleware. Using the builder block, we add it to the default list of middleware.
```ruby
class MyAuthentication < Faraday::Middleware
@@ -70,7 +70,9 @@ class MyAuthentication < Faraday::Middleware
end
end
-Her::API.setup :base_uri => "https://api.example.com", :add_middleware => [MyAuthentication]
+Her::API.setup :base_uri => "https://api.example.com" do |builder|
+ builder.use MyAuthentication
+end
```
Now, each HTTP request made by Her will have the `X-API-Token` header.
@@ -89,7 +91,7 @@ By default, Her handles JSON data. It expects the resource/collection data to be
[{ "id" : 1, "name" : "Tobias Fünke" }]
```
-However, you can define your own parsing method, using a response middleware. The middleware is expected to set `env[:body]` to a hash with three keys: `data`, `errors` and `metadata`. The following code enables parsing JSON data and treating the result as first-level properties. Using the `parse_middleware` key, we then replace the default parser.
+However, you can define your own parsing method, using a response middleware. The middleware is expected to set `env[:body]` to a hash with three keys: `data`, `errors` and `metadata`. The following code enables parsing JSON data and treating the result as first-level properties. Using the builder block, we then replace the default parser.
```ruby
class MyCustomParser < Faraday::Response::Middleware
@@ -103,7 +105,10 @@ class MyCustomParser < Faraday::Response::Middleware
end
end
-Her::API.setup :base_uri => "https://api.example.com", :parse_middleware => MyCustomParser
+Her::API.setup :base_uri => "https://api.example.com" do |builder|
+ builder.delete Her::Middleware::DefaultParseJSON
+ builder.use MyCustomParser
+end
# User.find(1) will now expect "https://api.example.com/users/1" to return something like '{ "data" => { "id": 1, "name": "Tobias Fünke" }, "errors" => [] }'
```
@@ -130,10 +135,9 @@ TWITTER_CREDENTIALS = {
:token_secret => ""
}
-Her::API.setup({
- :base_uri => "https://api.twitter.com/1/",
- :add_middleware => [FaradayMiddleware::OAuth => TWITTER_CREDENTIALS]
-})
+Her::API.setup :base_uri => "https://api.twitter.com/1/" do |builder|
+ builder.use FaradayMiddleware::OAuth, TWITTER_CREDENTIALS
+end
class Tweet
include Her::Model
@@ -142,6 +146,41 @@ end
@tweets = Tweet.get("/statuses/home_timeline.json")
```
+### Caching
+
+Again, using the `faraday_middleware` makes it very easy to cache requests and responses:
+
+In your Gemfile:
+
+```ruby
+gem "her"
+gem "faraday_middleware"
+```
+
+In your Ruby code:
+
+```ruby
+class MyCache
+ def write(key, value); end
+ def read(key); end
+ def fetch(key, &block); end
+end
+
+# A cache system must respond to `#write`, `#read` and `#fetch`.
+$cache = MyCache.new
+
+Her::API.setup :base_uri => "https://api.example.com" do |builder|
+ builder.use FaradayMiddleware::Caching, $cache
+end
+
+class User
+ include Her::Model
+end
+
+@user = User.find(1)
+@user = User.find(1) # This request will be fetched from the cache
+```
+
## Relationships
You can define `has_many`, `has_one` and `belongs_to` relationships in your models. The relationship data is handled in two different ways. When parsing a resource from JSON data, if there’s a relationship data included, it will be used to create new Ruby objects.
View
@@ -8,16 +8,20 @@ class API
# Setup a default API connection. Accepted arguments and options are the same as {API#setup}.
def self.setup(attrs={}) # {{{
@@default_api = new
- @@default_api.setup(attrs)
+ connection = @@default_api.setup(attrs)
+ yield connection.builder if block_given?
+ connection
end # }}}
# Setup the API connection.
#
# @param [Hash] attrs the options to create a message with
# @option attrs [String] :base_uri The main HTTP API root (eg. `https://api.example.com`)
- # @option attrs [Array, Class] :middleware A list of the only middleware Her will use
- # @option attrs [Array, Class] :add_middleware A list of middleware to add to Her’s default middleware
- # @option attrs [Class] :parse_middleware A middleware that will replace {Her::Middleware::FirstLevelParseJSON} to parse the received JSON
+ # @option attrs [Array, Class] :middleware **Deprecated** A list of the only middleware Her will use
+ # @option attrs [Array, Class] :add_middleware **Deprecated** A list of middleware to add to Her’s default middleware
+ # @option attrs [Class] :parse_middleware **Deprecated** A middleware that will replace {Her::Middleware::FirstLevelParseJSON} to parse the received JSON
+ #
+ # @return Faraday::Connection
#
# @example Setting up the default API connection
# Her::API.setup :base_uri => "https://api.example"
@@ -29,7 +33,9 @@ def self.setup(attrs={}) # {{{
# @all.call(env)
# end
# end
- # Her::API.setup :base_uri => "https://api.example.com", :add_middleware => [MyAuthentication]
+ # Her::API.setup :base_uri => "https://api.example.com" do |builder|
+ # builder.use MyAuthentication
+ # end
#
# @example A custom parse middleware
# class MyCustomParser < Faraday::Response::Middleware
@@ -40,7 +46,10 @@ def self.setup(attrs={}) # {{{
# env[:body] = { :data => json, :errors => errors, :metadata => metadata }
# end
# end
- # Her::API.setup :base_uri => "https://api.example.com", :parse_middleware => MyCustomParser
+ # Her::API.setup :base_uri => "https://api.example.com" do |builder|
+ # builder.delete Her::Middleware::DefaultParseJSON
+ # builder.use MyCustomParser
+ # end
def setup(attrs={}) # {{{
@base_uri = attrs[:base_uri]
@middleware = Her::API.default_middleware
@@ -62,6 +71,8 @@ def setup(attrs={}) # {{{
end
end
end
+ yield @connection.builder if block_given?
+ @connection
end # }}}
# Define a custom parsing procedure. The procedure is passed the response object and is
View
@@ -2,5 +2,7 @@ module Her
module Middleware
autoload :FirstLevelParseJSON, "her/middleware/first_level_parse_json"
autoload :SecondLevelParseJSON, "her/middleware/second_level_parse_json"
+
+ DefaultParseJSON = FirstLevelParseJSON
end
end
View
@@ -1,3 +1,3 @@
module Her
- VERSION = "0.2"
+ VERSION = "0.2.1"
end
View
@@ -16,41 +16,6 @@
@api.setup :base_uri => "https://api.example.com"
@api.base_uri.should == "https://api.example.com"
end # }}}
-
- it "sets additional middleware" do # {{{
- class Foo < Faraday::Response::Middleware; end;
- class Bar < Faraday::Response::Middleware; end;
- class Baz < Faraday::Response::Middleware; end;
-
- @api = Her::API.new
- @api.setup :base_uri => "https://api.example.com", :add_middleware => [Foo, Bar, Baz => { :hello => :world }]
- @api.middleware.should == [Foo, Bar, { Baz => { :hello => :world } }, Her::Middleware::FirstLevelParseJSON, Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
-
- @api = Her::API.new
- @api.setup :base_uri => "https://api.example.com", :add_middleware => Foo
- @api.middleware.should == [Foo, Her::Middleware::FirstLevelParseJSON, Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
-
- @api = Her::API.new
- @api.setup :base_uri => "https://api.example.com", :add_middleware => [{Baz => { :hello => :world }}]
- @api.middleware.should == [{ Baz => { :hello => :world } }, Her::Middleware::FirstLevelParseJSON, Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
- end # }}}
-
- it "overrides middleware" do # {{{
- class Foo < Faraday::Response::Middleware; end;
- class Bar < Faraday::Response::Middleware; end;
-
- @api = Her::API.new
- @api.setup :base_uri => "https://api.example.com", :middleware => [Foo, Bar]
- @api.middleware.should == [Foo, Bar]
- end # }}}
-
- it "sets a parse middleware" do # {{{
- class Foo < Faraday::Response::Middleware; end;
-
- @api = Her::API.new
- @api.setup :base_uri => "https://api.example.com", :parse_middleware => Foo
- @api.middleware.should == [Foo, Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
- end # }}}
end
describe "#request" do
@@ -97,32 +62,15 @@ def on_complete(env)
end
@api = Her::API.new
- @api.setup :base_uri => "https://api.example.com", :parse_middleware => CustomParser
+ @api.setup :base_uri => "https://api.example.com" do |connection|
+ connection.delete Her::Middleware::DefaultParseJSON
+ connection.use CustomParser
+ end
parsed_data = @api.request(:_method => :get, :_path => "users/1")
parsed_data[:data].should == { :id => 1, :name => "George Michael Bluth" }
parsed_data[:errors].should == []
parsed_data[:metadata].should == {}
end # }}}
-
- it "uses middleware with custom arguments" do # {{{
- FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => MultiJson.dump(:id => 1, :name => "George Michael Bluth"))
-
- class FakeOAuth < Faraday::Middleware
- def initialize(app, options={})
- super(app)
- @options = options
- end
-
- def call(env)
- env[:request_headers]["X-Hello"] = @options[:foo]
- @app.call(env)
- end
- end
-
- @api = Her::API.new
- @api.setup :base_uri => "https://api.example.com", :add_middleware => [FakeOAuth => { :foo => "World" }]
- parsed_data = @api.request(:_method => :get, :_path => "users/1")
- end # }}}
end
end
end

0 comments on commit b73abec

Please sign in to comment.