Skip to content
Browse files

Add support for custom Faraday middleware

  • Loading branch information...
1 parent ee90237 commit 3d1da8d595be171b0a8a941051c436dcf81de9d6 @remiprev committed Apr 15, 2012
Showing with 114 additions and 68 deletions.
  1. +14 −12 README.md
  2. +3 −2 lib/her.rb
  3. +25 −30 lib/her/api.rb
  4. +5 −0 lib/her/middleware.rb
  5. +18 −0 lib/her/middleware/default_parse_json.rb
  6. +1 −2 lib/her/model/http.rb
  7. +48 −22 spec/api_spec.rb
View
26 README.md
@@ -86,25 +86,27 @@ By default, Her handles JSON data. It expects the data to be formatted in a cert
}
```
-However, you can define your own parsing method, with `Her::API.parse_with`. The `parse_with` method takes a block which will be executed each time data from an HTTP response needs to be parsed. The block is expected to return a hash with three keys: `data`, `errors` and `metadata`. The following code enables parsing JSON data and treating this data as first-level properties:
+However, you can define your own parsing method, using a Faraday response middleware. The middleware is expected to return a hash with three keys: `data`, `errors` and `metadata`. The following code enables parsing JSON data and treating this data as first-level properties:
```ruby
-Her::API.setup :base_uri => "https://api.example.com"
-Her::API.parse_with |response|
- json = JSON.parse(response.body, :symbolize_names => true)
- errors = json.delete(:errors)
- {
- :data => json,
- :errors => errors || [],
- :metadata => {}
- }
+class MyCustomParser < Faraday::Response::Middleware
+ def on_complete(env)
+ json = JSON.parse(env[:body], :symbolize_names => true)
+ errors = json.delete(:errors) || []
+ metadata = json.delete(:metadata) || []
+ env[:body] = {
+ :data => json,
+ :errors => errors,
+ :metadata => metadata,
+ }
+ end
+end
+Her::API.setup :base_uri => "https://api.example.com", :middleware => [MyCustomParser] + Her::API.default_middleware
end
# User.find(1) will now expect "https://api.example.com/users/1" to return something like '{ "id": 1, "name": "Tobias Fünke" }'
```
-This feature is not stable and might change in the future, probably by using a middleware through [Faraday](https://github.com/technoweenie/faraday).
-
## 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
5 lib/her.rb
@@ -5,6 +5,7 @@
require "active_support/inflector"
module Her
- autoload :Model, "her/model"
- autoload :API, "her/api"
+ autoload :Model, "her/model"
+ autoload :API, "her/api"
+ autoload :Middleware, "her/middleware"
end
View
55 lib/her/api.rb
@@ -7,7 +7,7 @@ module Her
# $my_api.setup :base_uri => "https://api.example.com"
class API
# @private
- attr_reader :base_uri, :parse_with
+ attr_reader :base_uri, :middleware
# Setup a default API connection
def self.setup(attrs={}) # {{{
@@ -20,20 +20,32 @@ def self.default_api(attrs={}) # {{{
defined?(@@default_api) ? @@default_api : nil
end # }}}
+ # @private
+ def self.default_middleware # {{{
+ [Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
+ end # }}}
+
# Setup the API connection
+ #
+ # @example
+ # module MyAPI
+ # class ParseResponse
+ # def on_complete(env)
+ # json = JSON.parse(env[:body], :symbolize_names => true)
+ # {
+ # :data => json,
+ # :errors => json[:errors] || [],
+ # :metadata => json[:metadata] || {},
+ # }
+ # end
+ # end
+ # end
+ # Her::API.setup :base_url => "https://api.example.com", :middleware => [MyAPI::ParseResponse, Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
def setup(attrs={}) # {{{
@base_uri = attrs[:base_uri]
- @parse_with = lambda do |response|
- json = JSON.parse(response.body, :symbolize_names => true)
- {
- :data => json[:data],
- :errors => json[:errors],
- :metadata => json[:metadata],
- }
- end
+ middleware = @middleware = attrs[:middleware] || [Her::Middleware::DefaultParseJSON] + Her::API.default_middleware
@connection = Faraday.new(:url => @base_uri) do |builder|
- builder.request :url_encoded
- builder.adapter :net_http
+ middleware.each { |m| builder.use(m) }
end
end # }}}
@@ -42,25 +54,12 @@ def setup(attrs={}) # {{{
# and a metadata Hash.
#
# @example
- # $my_api.parse_with do |response|
- # json = JSON.parse(response.body)
- # { :resource => json[:data], :errors => json[:errors], :metadata => json[:metdata] }
- # end
- def parse_with(&block) # {{{
- @custom_parsing_block = true
- @parse_with = block
- end # }}}
-
- # Return whether a custom parsing block has been defined
- def custom_parsing_block? # {{{
- @custom_parsing_block
- end # }}}
# Make an HTTP request to the API
def request(attrs={}) # {{{
method = attrs.delete(:_method)
path = attrs.delete(:_path)
- @connection.send method do |request|
+ response = @connection.send method do |request|
if method == :get
# For GET requests, treat additional parameters as querystring data
request.url path, attrs
@@ -70,11 +69,7 @@ def request(attrs={}) # {{{
request.body = attrs
end
end
- end # }}}
-
- # Parse the HTTP response
- def parse(response) # {{{
- @parse_with.call(response)
+ response.env[:body]
end # }}}
end
end
View
5 lib/her/middleware.rb
@@ -0,0 +1,5 @@
+module Her
+ module Middleware
+ autoload :DefaultParseJSON, "her/middleware/default_parse_json"
+ end
+end
View
18 lib/her/middleware/default_parse_json.rb
@@ -0,0 +1,18 @@
+module Her
+ module Middleware
+ class DefaultParseJSON < Faraday::Response::Middleware
+ def parse(body)
+ json = JSON.parse(body, :symbolize_names => true)
+ return {
+ :data => json[:data],
+ :errors => json[:errors],
+ :metadata => json[:metadata],
+ }
+ end
+
+ def on_complete(env)
+ env[:body] = parse env[:body]
+ end
+ end
+ end
+end
View
3 lib/her/model/http.rb
@@ -34,8 +34,7 @@ def item_path(path=nil) # {{{
# Main request wrapper around Her::API. Used to make custom request to the API.
# @private
def request(attrs={}, &block) # {{{
- response = @her_api.request(attrs)
- yield @her_api.parse(response)
+ yield @her_api.request(attrs)
end # }}}
# Make a GET request and return the parsed JSON response (not mapped to objects)
View
70 spec/api_spec.rb
@@ -17,43 +17,69 @@
@api.base_uri.should == "https://api.example.com"
end # }}}
- it "sets a custom parsing block" do # {{{
+ it "sets 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"
- @api.parse_with do |response|
- response.body
- end
- @api.custom_parsing_block?.should be_true
+ @api.setup :base_uri => "https://api.example.com", :middleware => [Foo, Bar]
+ @api.middleware.should == [Foo, Bar]
end # }}}
end
describe "#request" do
- before do # {{{
- @api = Her::API.new
- @api.setup :base_uri => "https://api.example.com"
+ it "makes HTTP requests" do # {{{
FakeWeb.register_uri(:get, "https://api.example.com/foo", :body => "Foo, it is.")
- end # }}}
- it "makes HTTP requests" do # {{{
- response = @api.request(:_method => :get, :_path => "/foo")
- response.body.should == "Foo, it is."
- end # }}}
- end
+ class Foo < Faraday::Response::Middleware
+ def on_complete(env)
+ env[:body] = { :data => env[:body] }
+ end
+ end
- describe "#parse" do
- before do # {{{
@api = Her::API.new
- @api.setup :base_uri => "https://api.example.com"
- FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :name => "George Michael Bluth" }, :errors => ["This is a single error"], :metadata => { :page => 1, :per_page => 10 }}.to_json)
+ @api.setup :base_uri => "https://api.example.com", :middleware => []
+ @api.request(:_method => :get, :_path => "/foo") do |parsed_data|
+ parsed_data[:data] == "Foo, it is."
+ end
end # }}}
- it "parses a request" do # {{{
- @api.parse @api.request(:_method => :get, :_path => "users/1") do |parsed_data|
- parsed_data[:resource].should == { :id => 1, :name => "George Michael Bluth" }
+ it "parses a request with the default parser" do # {{{
+ FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :data => { :id => 1, :name => "George Michael Bluth" }, :errors => ["This is a single error"], :metadata => { :page => 1, :per_page => 10 }}.to_json)
+
+ @api = Her::API.new
+ @api.setup :base_uri => "https://api.example.com"
+ @api.request(:_method => :get, :_path => "users/1") do |parsed_data|
+ parsed_data[:data].should == { :id => 1, :name => "George Michael Bluth" }
parsed_data[:errors].should == ["This is a single error"]
parsed_data[:metadata].should == { :page => 1, :per_page => 10 }
end
end # }}}
+
+ it "parses a request with a custom parser" do # {{{
+ FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :id => 1, :name => "George Michael Bluth" }.to_json)
+
+ class CustomParser < Faraday::Response::Middleware
+ def on_complete(env)
+ json = JSON.parse(env[:body], :symbolize_names => true)
+ errors = json.delete(:errors) || []
+ metadata = json.delete(:metadata) || {}
+ env[:body] = {
+ :data => json,
+ :errors => errors,
+ :metadata => metadata,
+ }
+ end
+ end
+
+ @api = Her::API.new
+ @api.setup :base_uri => "https://api.example.com", :middleware => [CustomParser] + Her::API.default_middleware
+ @api.request(:_method => :get, :_path => "users/1") do |parsed_data|
+ parsed_data[:data].should == { :id => 1, :name => "George Michael Bluth" }
+ parsed_data[:errors].should == []
+ parsed_data[:metadata].should == {}
+ end
+ end # }}}
end
end
end

0 comments on commit 3d1da8d

Please sign in to comment.
Something went wrong with that request. Please try again.