her 0.1.6 → 0.1.7

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -86,25 +86,27 @@ By default, Her handles JSON data. It expects the data to be formatted in a cert
86
86
  }
87
87
  ```
88
88
 
89
- 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:
89
+ 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:
90
90
 
91
91
  ```ruby
92
- Her::API.setup :base_uri => "https://api.example.com"
93
- Her::API.parse_with |response|
94
- json = JSON.parse(response.body, :symbolize_names => true)
95
- errors = json.delete(:errors)
96
- {
97
- :data => json,
98
- :errors => errors || [],
99
- :metadata => {}
100
- }
92
+ class MyCustomParser < Faraday::Response::Middleware
93
+ def on_complete(env)
94
+ json = JSON.parse(env[:body], :symbolize_names => true)
95
+ errors = json.delete(:errors) || []
96
+ metadata = json.delete(:metadata) || []
97
+ env[:body] = {
98
+ :data => json,
99
+ :errors => errors,
100
+ :metadata => metadata,
101
+ }
102
+ end
103
+ end
104
+ Her::API.setup :base_uri => "https://api.example.com", :middleware => [MyCustomParser] + Her::API.default_middleware
101
105
  end
102
106
 
103
107
  # User.find(1) will now expect "https://api.example.com/users/1" to return something like '{ "id": 1, "name": "Tobias Fünke" }'
104
108
  ```
105
109
 
106
- This feature is not stable and might change in the future, probably by using a middleware through [Faraday](https://github.com/technoweenie/faraday).
107
-
108
110
  ## Relationships
109
111
 
110
112
  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.
@@ -179,6 +181,9 @@ class User
179
181
  self.internal_id = 42 # Will be passed in the HTTP request
180
182
  end
181
183
  end
184
+
185
+ @user = User.create(:fullname => "Tobias Fünke")
186
+ # POST /users&fullname=Tobias+Fünke&internal_id=42
182
187
  ```
183
188
 
184
189
  In the future, adding hooks to all models will be possible, as well as defining and triggering your own hooks (eg. for your custom requests).
data/lib/her.rb CHANGED
@@ -5,6 +5,7 @@ require "active_support"
5
5
  require "active_support/inflector"
6
6
 
7
7
  module Her
8
- autoload :Model, "her/model"
9
- autoload :API, "her/api"
8
+ autoload :Model, "her/model"
9
+ autoload :API, "her/api"
10
+ autoload :Middleware, "her/middleware"
10
11
  end
@@ -7,7 +7,7 @@ module Her
7
7
  # $my_api.setup :base_uri => "https://api.example.com"
8
8
  class API
9
9
  # @private
10
- attr_reader :base_uri, :parse_with
10
+ attr_reader :base_uri, :middleware
11
11
 
12
12
  # Setup a default API connection
13
13
  def self.setup(attrs={}) # {{{
@@ -20,20 +20,32 @@ module Her
20
20
  defined?(@@default_api) ? @@default_api : nil
21
21
  end # }}}
22
22
 
23
+ # @private
24
+ def self.default_middleware # {{{
25
+ [Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
26
+ end # }}}
27
+
23
28
  # Setup the API connection
29
+ #
30
+ # @example
31
+ # module MyAPI
32
+ # class ParseResponse
33
+ # def on_complete(env)
34
+ # json = JSON.parse(env[:body], :symbolize_names => true)
35
+ # {
36
+ # :data => json,
37
+ # :errors => json[:errors] || [],
38
+ # :metadata => json[:metadata] || {},
39
+ # }
40
+ # end
41
+ # end
42
+ # end
43
+ # Her::API.setup :base_url => "https://api.example.com", :middleware => [MyAPI::ParseResponse, Faraday::Request::UrlEncoded, Faraday::Adapter::NetHttp]
24
44
  def setup(attrs={}) # {{{
25
45
  @base_uri = attrs[:base_uri]
26
- @parse_with = lambda do |response|
27
- json = JSON.parse(response.body, :symbolize_names => true)
28
- {
29
- :data => json[:data],
30
- :errors => json[:errors],
31
- :metadata => json[:metadata],
32
- }
33
- end
46
+ middleware = @middleware = attrs[:middleware] || [Her::Middleware::DefaultParseJSON] + Her::API.default_middleware
34
47
  @connection = Faraday.new(:url => @base_uri) do |builder|
35
- builder.request :url_encoded
36
- builder.adapter :net_http
48
+ middleware.each { |m| builder.use(m) }
37
49
  end
38
50
  end # }}}
39
51
 
@@ -42,25 +54,12 @@ module Her
42
54
  # and a metadata Hash.
43
55
  #
44
56
  # @example
45
- # $my_api.parse_with do |response|
46
- # json = JSON.parse(response.body)
47
- # { :resource => json[:data], :errors => json[:errors], :metadata => json[:metdata] }
48
- # end
49
- def parse_with(&block) # {{{
50
- @custom_parsing_block = true
51
- @parse_with = block
52
- end # }}}
53
-
54
- # Return whether a custom parsing block has been defined
55
- def custom_parsing_block? # {{{
56
- @custom_parsing_block
57
- end # }}}
58
57
 
59
58
  # Make an HTTP request to the API
60
59
  def request(attrs={}) # {{{
61
60
  method = attrs.delete(:_method)
62
61
  path = attrs.delete(:_path)
63
- @connection.send method do |request|
62
+ response = @connection.send method do |request|
64
63
  if method == :get
65
64
  # For GET requests, treat additional parameters as querystring data
66
65
  request.url path, attrs
@@ -70,11 +69,7 @@ module Her
70
69
  request.body = attrs
71
70
  end
72
71
  end
73
- end # }}}
74
-
75
- # Parse the HTTP response
76
- def parse(response) # {{{
77
- @parse_with.call(response)
72
+ response.env[:body]
78
73
  end # }}}
79
74
  end
80
75
  end
@@ -0,0 +1,5 @@
1
+ module Her
2
+ module Middleware
3
+ autoload :DefaultParseJSON, "her/middleware/default_parse_json"
4
+ end
5
+ end
@@ -0,0 +1,18 @@
1
+ module Her
2
+ module Middleware
3
+ class DefaultParseJSON < Faraday::Response::Middleware
4
+ def parse(body)
5
+ json = JSON.parse(body, :symbolize_names => true)
6
+ return {
7
+ :data => json[:data],
8
+ :errors => json[:errors],
9
+ :metadata => json[:metadata],
10
+ }
11
+ end
12
+
13
+ def on_complete(env)
14
+ env[:body] = parse env[:body]
15
+ end
16
+ end
17
+ end
18
+ end
@@ -34,8 +34,7 @@ module Her
34
34
  # Main request wrapper around Her::API. Used to make custom request to the API.
35
35
  # @private
36
36
  def request(attrs={}, &block) # {{{
37
- response = @her_api.request(attrs)
38
- yield @her_api.parse(response)
37
+ yield @her_api.request(attrs)
39
38
  end # }}}
40
39
 
41
40
  # Make a GET request and return the parsed JSON response (not mapped to objects)
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.1.6"
2
+ VERSION = "0.1.7"
3
3
  end
@@ -17,43 +17,69 @@ describe Her::API do
17
17
  @api.base_uri.should == "https://api.example.com"
18
18
  end # }}}
19
19
 
20
- it "sets a custom parsing block" do # {{{
20
+ it "sets middleware" do # {{{
21
+ class Foo < Faraday::Response::Middleware; end;
22
+ class Bar < Faraday::Response::Middleware; end;
23
+
21
24
  @api = Her::API.new
22
- @api.setup :base_uri => "https://api.example.com"
23
- @api.parse_with do |response|
24
- response.body
25
- end
26
- @api.custom_parsing_block?.should be_true
25
+ @api.setup :base_uri => "https://api.example.com", :middleware => [Foo, Bar]
26
+ @api.middleware.should == [Foo, Bar]
27
27
  end # }}}
28
28
  end
29
29
 
30
30
  describe "#request" do
31
- before do # {{{
32
- @api = Her::API.new
33
- @api.setup :base_uri => "https://api.example.com"
31
+ it "makes HTTP requests" do # {{{
34
32
  FakeWeb.register_uri(:get, "https://api.example.com/foo", :body => "Foo, it is.")
35
- end # }}}
36
33
 
37
- it "makes HTTP requests" do # {{{
38
- response = @api.request(:_method => :get, :_path => "/foo")
39
- response.body.should == "Foo, it is."
40
- end # }}}
41
- end
34
+ class Foo < Faraday::Response::Middleware
35
+ def on_complete(env)
36
+ env[:body] = { :data => env[:body] }
37
+ end
38
+ end
42
39
 
43
- describe "#parse" do
44
- before do # {{{
45
40
  @api = Her::API.new
46
- @api.setup :base_uri => "https://api.example.com"
47
- 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)
41
+ @api.setup :base_uri => "https://api.example.com", :middleware => []
42
+ @api.request(:_method => :get, :_path => "/foo") do |parsed_data|
43
+ parsed_data[:data] == "Foo, it is."
44
+ end
48
45
  end # }}}
49
46
 
50
- it "parses a request" do # {{{
51
- @api.parse @api.request(:_method => :get, :_path => "users/1") do |parsed_data|
52
- parsed_data[:resource].should == { :id => 1, :name => "George Michael Bluth" }
47
+ it "parses a request with the default parser" do # {{{
48
+ 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)
49
+
50
+ @api = Her::API.new
51
+ @api.setup :base_uri => "https://api.example.com"
52
+ @api.request(:_method => :get, :_path => "users/1") do |parsed_data|
53
+ parsed_data[:data].should == { :id => 1, :name => "George Michael Bluth" }
53
54
  parsed_data[:errors].should == ["This is a single error"]
54
55
  parsed_data[:metadata].should == { :page => 1, :per_page => 10 }
55
56
  end
56
57
  end # }}}
58
+
59
+ it "parses a request with a custom parser" do # {{{
60
+ FakeWeb.register_uri(:get, "https://api.example.com/users/1", :body => { :id => 1, :name => "George Michael Bluth" }.to_json)
61
+
62
+ class CustomParser < Faraday::Response::Middleware
63
+ def on_complete(env)
64
+ json = JSON.parse(env[:body], :symbolize_names => true)
65
+ errors = json.delete(:errors) || []
66
+ metadata = json.delete(:metadata) || {}
67
+ env[:body] = {
68
+ :data => json,
69
+ :errors => errors,
70
+ :metadata => metadata,
71
+ }
72
+ end
73
+ end
74
+
75
+ @api = Her::API.new
76
+ @api.setup :base_uri => "https://api.example.com", :middleware => [CustomParser] + Her::API.default_middleware
77
+ @api.request(:_method => :get, :_path => "users/1") do |parsed_data|
78
+ parsed_data[:data].should == { :id => 1, :name => "George Michael Bluth" }
79
+ parsed_data[:errors].should == []
80
+ parsed_data[:metadata].should == {}
81
+ end
82
+ end # }}}
57
83
  end
58
84
  end
59
85
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: her
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.6
4
+ version: 0.1.7
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -171,6 +171,8 @@ files:
171
171
  - her.gemspec
172
172
  - lib/her.rb
173
173
  - lib/her/api.rb
174
+ - lib/her/middleware.rb
175
+ - lib/her/middleware/default_parse_json.rb
174
176
  - lib/her/model.rb
175
177
  - lib/her/model/base.rb
176
178
  - lib/her/model/hooks.rb
@@ -198,7 +200,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
198
200
  version: '0'
199
201
  segments:
200
202
  - 0
201
- hash: 1685847524195482169
203
+ hash: -2767021996149695300
202
204
  required_rubygems_version: !ruby/object:Gem::Requirement
203
205
  none: false
204
206
  requirements:
@@ -207,7 +209,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
207
209
  version: '0'
208
210
  segments:
209
211
  - 0
210
- hash: 1685847524195482169
212
+ hash: -2767021996149695300
211
213
  requirements: []
212
214
  rubyforge_project:
213
215
  rubygems_version: 1.8.18