her 0.1.6 → 0.1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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