morpheus 0.3.7 → 0.3.8
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/.gitignore +3 -0
- data/lib/morpheus/client/associations.rb +4 -1
- data/lib/morpheus/mixins/conversion.rb +5 -0
- data/lib/morpheus/response_parser.rb +47 -39
- data/lib/morpheus/version.rb +1 -1
- data/morpheus.gemspec +8 -7
- data/spec/morpheus/mixins/conversion_spec.rb +10 -0
- data/spec/morpheus/response_parser_spec.rb +224 -6
- metadata +18 -17
- data/.rvmrc +0 -1
data/.gitignore
CHANGED
@@ -9,67 +9,55 @@ module Morpheus
|
|
9
9
|
end
|
10
10
|
|
11
11
|
def self.parse(owner, request, metadata)
|
12
|
-
|
13
|
-
ActiveSupport::Notifications.instrument('request.morpheus',
|
14
|
-
:url => request.url,
|
15
|
-
:params => request.params,
|
16
|
-
:method => request.method,
|
17
|
-
:class => owner,
|
18
|
-
:response => request.response
|
19
|
-
) do
|
20
|
-
@parsed_response = parser.parse
|
21
|
-
end
|
22
|
-
RequestCache.cache.clear unless request.method == :get
|
23
|
-
@parsed_response
|
12
|
+
new(owner, request, metadata).parse
|
24
13
|
end
|
25
14
|
|
26
15
|
def parse
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
16
|
+
clear_cache!
|
17
|
+
with_active_support_notifications do
|
18
|
+
case @response.code
|
19
|
+
when 200..299, 422
|
20
|
+
build_from_response
|
21
|
+
when 404
|
22
|
+
raise_not_found
|
23
|
+
when 500..599
|
24
|
+
raise ServerError
|
25
|
+
when 0
|
26
|
+
raise RemoteHostConnectionFailure
|
27
|
+
else
|
28
|
+
raise InvalidResponseCode, "Response had error code: #{@response.code}"
|
29
|
+
end
|
40
30
|
end
|
41
31
|
end
|
42
32
|
|
43
33
|
private
|
44
34
|
|
45
|
-
def
|
46
|
-
|
35
|
+
def with_active_support_notifications
|
36
|
+
ActiveSupport::Notifications.instrument('request.morpheus',
|
37
|
+
:url => @request.url,
|
38
|
+
:params => @request.params,
|
39
|
+
:method => @request.method,
|
40
|
+
:class => @owner,
|
41
|
+
:response => @request.response
|
42
|
+
) { yield }
|
47
43
|
end
|
48
44
|
|
49
|
-
def
|
50
|
-
|
45
|
+
def clear_cache!
|
46
|
+
RequestCache.cache.clear unless @request.method == :get
|
51
47
|
end
|
52
48
|
|
53
49
|
def build_from_response
|
54
50
|
if content
|
55
51
|
if Array === content
|
56
52
|
content.collect do |attributes|
|
57
|
-
|
53
|
+
build_object_with_attributes(attributes)
|
58
54
|
end
|
59
55
|
else
|
60
|
-
|
56
|
+
build_object_with_attributes(content)
|
61
57
|
end
|
62
58
|
end
|
63
59
|
end
|
64
60
|
|
65
|
-
def build_object_with_attributes(attributes)
|
66
|
-
if attributes.keys.include?('type')
|
67
|
-
attributes['type'].constantize.new(attributes)
|
68
|
-
else
|
69
|
-
@requesting_class.new.send(:merge_attributes, attributes)
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
61
|
def raise_not_found
|
74
62
|
case
|
75
63
|
when @metadata[:ids]
|
@@ -81,5 +69,25 @@ module Morpheus
|
|
81
69
|
end
|
82
70
|
end
|
83
71
|
|
72
|
+
def parsed_body
|
73
|
+
@parsed_body ||= Yajl::Parser.parse(@response.body)
|
74
|
+
end
|
75
|
+
|
76
|
+
def content
|
77
|
+
@content ||= parsed_body.try(:[], 'content')
|
78
|
+
end
|
79
|
+
|
80
|
+
def response_errors
|
81
|
+
@response_errors ||= parsed_body.try(:[], 'errors')
|
82
|
+
end
|
83
|
+
|
84
|
+
def build_object_with_attributes(attributes)
|
85
|
+
if attributes.keys.include?('type')
|
86
|
+
attributes['type'].constantize.new(attributes)
|
87
|
+
else
|
88
|
+
@requesting_class.new.send(:merge_attributes, attributes)
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
84
92
|
end
|
85
93
|
end
|
data/lib/morpheus/version.rb
CHANGED
data/morpheus.gemspec
CHANGED
@@ -1,5 +1,6 @@
|
|
1
1
|
# -*- encoding: utf-8 -*-
|
2
|
-
|
2
|
+
$:.unshift File.expand_path('../lib', __FILE__)
|
3
|
+
require 'morpheus/version'
|
3
4
|
|
4
5
|
Gem::Specification.new do |gem|
|
5
6
|
gem.authors = ['Ryan Moran']
|
@@ -17,13 +18,13 @@ Gem::Specification.new do |gem|
|
|
17
18
|
|
18
19
|
gem.add_dependency 'yajl-ruby', '~> 0.8.2'
|
19
20
|
gem.add_dependency 'typhoeus', '~> 0.2.4'
|
20
|
-
gem.add_dependency 'activemodel', '
|
21
|
-
gem.add_dependency 'activesupport', '
|
22
|
-
gem.add_dependency 'i18n', '
|
21
|
+
gem.add_dependency 'activemodel', '>= 3.0.0'
|
22
|
+
gem.add_dependency 'activesupport', '>= 3.0.0'
|
23
|
+
gem.add_dependency 'i18n', '>= 0.5.0'
|
23
24
|
|
24
|
-
gem.add_development_dependency 'rails', '
|
25
|
+
gem.add_development_dependency 'rails', '>= 3.0.0'
|
25
26
|
gem.add_development_dependency 'sqlite3', '~> 1.3.3'
|
26
|
-
gem.add_development_dependency 'rspec-rails', '~> 2.
|
27
|
+
gem.add_development_dependency 'rspec-rails', '~> 2.8.1'
|
27
28
|
gem.add_development_dependency 'rcov', '~> 0.9.11'
|
28
|
-
gem.add_development_dependency 'autotest-standalone', '~> 4.5.
|
29
|
+
gem.add_development_dependency 'autotest-standalone', '~> 4.5.9'
|
29
30
|
end
|
@@ -63,4 +63,14 @@ describe Morpheus::Conversion do
|
|
63
63
|
end
|
64
64
|
end
|
65
65
|
|
66
|
+
describe '#to_partial_path' do
|
67
|
+
before(:each) do
|
68
|
+
@dog = Dog.new
|
69
|
+
end
|
70
|
+
|
71
|
+
it 'returns a string representing the path to a partial for this model' do
|
72
|
+
@dog.to_partial_path.should eql('dogs/dog')
|
73
|
+
end
|
74
|
+
end
|
75
|
+
|
66
76
|
end
|
@@ -1,17 +1,235 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Morpheus::ResponseParser do
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
4
|
+
let(:owner) { Dog }
|
5
|
+
let(:response) { mock(:body => '{ "content": { "foo": "bar" }, "errors": [{ "foo": "cannot be \"bar\"" }] }') }
|
6
|
+
let(:request) { req = mock(:response => response, :url => '/dog/1', :params => { :id => 1 }); req.stub(:method).and_return(:get); req }
|
7
|
+
let(:metadata) { {} }
|
8
|
+
let(:parsed_response) { mock }
|
9
|
+
let(:parser) { Morpheus::ResponseParser.new(owner, request, metadata) }
|
8
10
|
|
9
11
|
describe '.parse' do
|
10
|
-
|
12
|
+
before do
|
13
|
+
parser
|
14
|
+
Morpheus::ResponseParser.stub(:new).and_return(parser)
|
15
|
+
parser.stub(:parse).and_return(parsed_response)
|
16
|
+
end
|
17
|
+
|
18
|
+
it 'creates a new ResponseParser' do
|
19
|
+
Morpheus::ResponseParser.should_receive(:new).with(owner, request, metadata).and_return(parser)
|
20
|
+
Morpheus::ResponseParser.parse(owner, request, metadata)
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'delegates parsing to the new ResponseParser' do
|
24
|
+
parser.should_receive(:parse)
|
25
|
+
Morpheus::ResponseParser.parse(owner, request, metadata)
|
26
|
+
end
|
27
|
+
|
28
|
+
it 'returns the parsed response' do
|
29
|
+
response = Morpheus::ResponseParser.parse(owner, request, metadata)
|
30
|
+
response.should eql(parsed_response)
|
31
|
+
end
|
11
32
|
end
|
12
33
|
|
13
34
|
describe '#parse' do
|
14
|
-
|
35
|
+
before do
|
36
|
+
parser.stub(:clear_cache!)
|
37
|
+
end
|
38
|
+
|
39
|
+
it 'clears the cache' do
|
40
|
+
parser.stub(:with_active_support_notifications)
|
41
|
+
parser.should_receive(:clear_cache!)
|
42
|
+
parser.parse
|
43
|
+
end
|
44
|
+
|
45
|
+
context 'when the response code is 200..299' do
|
46
|
+
before do
|
47
|
+
response.stub(:code).and_return(200)
|
48
|
+
end
|
49
|
+
|
50
|
+
it 'delegates to #build_from_response' do
|
51
|
+
parser.should_receive(:build_from_response)
|
52
|
+
parser.parse
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
context 'when the repsonse code is 404' do
|
57
|
+
before do
|
58
|
+
response.stub(:code).and_return(404)
|
59
|
+
end
|
60
|
+
|
61
|
+
it 'delegates to #raise_not_found' do
|
62
|
+
parser.should_receive(:raise_not_found)
|
63
|
+
parser.parse
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
context 'when the response code is 422' do
|
68
|
+
before do
|
69
|
+
response.stub(:code).and_return(422)
|
70
|
+
end
|
71
|
+
|
72
|
+
it 'delegates to #build_from_response' do
|
73
|
+
parser.should_receive(:build_from_response)
|
74
|
+
parser.parse
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
context 'when the response code is 500..599' do
|
79
|
+
before do
|
80
|
+
response.stub(:code).and_return(500)
|
81
|
+
end
|
82
|
+
|
83
|
+
it 'raises a ServerError exception' do
|
84
|
+
lambda { parser.parse }.should raise_error(Morpheus::ServerError)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
context 'when the response code is 0' do
|
89
|
+
before do
|
90
|
+
response.stub(:code).and_return(0)
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'raises a RemoteHostConnectionFailure exception' do
|
94
|
+
lambda { parser.parse }.should raise_error(Morpheus::RemoteHostConnectionFailure)
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
98
|
+
context 'when the response code is not one of the above' do
|
99
|
+
before do
|
100
|
+
response.stub(:code).and_return(701)
|
101
|
+
end
|
102
|
+
|
103
|
+
it 'raises an InvalidResponseCode exception' do
|
104
|
+
lambda { parser.parse }.should raise_error(Morpheus::InvalidResponseCode, 'Response had error code: 701')
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
describe '#clear_cache!' do
|
110
|
+
context 'when the request method is not GET' do
|
111
|
+
before do
|
112
|
+
request.stub(:method).and_return(:put)
|
113
|
+
end
|
114
|
+
|
115
|
+
it 'clears the RequestCache cache' do
|
116
|
+
Morpheus::RequestCache.cache.should_receive(:clear)
|
117
|
+
parser.send(:clear_cache!)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
context 'when the request method is GET' do
|
122
|
+
before do
|
123
|
+
request.stub(:method).and_return(:get)
|
124
|
+
end
|
125
|
+
|
126
|
+
it 'does not clear the RequestCache cache' do
|
127
|
+
Morpheus::RequestCache.cache.should_not_receive(:clear)
|
128
|
+
parser.stub(:clear_cache!)
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
132
|
+
|
133
|
+
describe '#build_from_response' do
|
134
|
+
context 'when there is content' do
|
135
|
+
context 'when the content is an Array' do
|
136
|
+
let(:content) { [{ 'foo' => 'bar' }, { 'baz' => 'qux' }] }
|
137
|
+
|
138
|
+
before do
|
139
|
+
parser.stub(:content).and_return(content)
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'delegates to #build_object_with_attributes for each element, collecting the results' do
|
143
|
+
parser.should_receive(:build_object_with_attributes).exactly(2).times
|
144
|
+
parser.send(:build_from_response)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
context 'when the content is not an Array' do
|
149
|
+
let(:content) { { 'foo' => 'bar' } }
|
150
|
+
|
151
|
+
it 'delegates to #build_object_with_attributes' do
|
152
|
+
parser.should_receive(:build_object_with_attributes).with(content)
|
153
|
+
parser.send(:build_from_response)
|
154
|
+
end
|
155
|
+
end
|
156
|
+
end
|
157
|
+
|
158
|
+
context 'when there is no content' do
|
159
|
+
before do
|
160
|
+
parser.stub(:content).and_return(nil)
|
161
|
+
end
|
162
|
+
|
163
|
+
it 'returns nil' do
|
164
|
+
parser.send(:build_from_response).should be_nil
|
165
|
+
end
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
describe '#raise_not_found' do
|
170
|
+
context 'when the metadata hash contains :ids' do
|
171
|
+
let(:metadata) { { :ids => [1,2,3] } }
|
172
|
+
|
173
|
+
it 'raises a ResourceNotFound exception with the appropriate message' do
|
174
|
+
lambda { parser.send(:raise_not_found) }.should raise_error(Morpheus::ResourceNotFound, "Couldn't find all Dogs with IDs (1, 2, 3)")
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
context 'when the metadata hash contains :id' do
|
179
|
+
let(:metadata) { { :id => 1 } }
|
180
|
+
|
181
|
+
it 'raises a ResourceNotFound exception with the appropriate message' do
|
182
|
+
lambda { parser.send(:raise_not_found) }.should raise_error(Morpheus::ResourceNotFound, "Couldn't find Dog with ID=1")
|
183
|
+
end
|
184
|
+
end
|
185
|
+
|
186
|
+
context 'when the metadata hash does not contain either :ids or :id' do
|
187
|
+
it 'raises a ResourceNotFound exception with the appropriate message' do
|
188
|
+
lambda { parser.send(:raise_not_found) }.should raise_error(Morpheus::ResourceNotFound, /No resource was found at/)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
end
|
192
|
+
|
193
|
+
describe '#parsed_body' do
|
194
|
+
it 'parses out the entire response body JSON' do
|
195
|
+
parser.send(:parsed_body).should eql({
|
196
|
+
'content' => { 'foo' => 'bar' },
|
197
|
+
'errors' => [{ 'foo' => 'cannot be "bar"' }]
|
198
|
+
})
|
199
|
+
end
|
200
|
+
end
|
201
|
+
|
202
|
+
describe '#content' do
|
203
|
+
it 'parses out the "content" attribute from the response body JSON' do
|
204
|
+
parser.send(:content).should eql({ 'foo' => 'bar' })
|
205
|
+
end
|
206
|
+
end
|
207
|
+
|
208
|
+
describe '#response_errors' do
|
209
|
+
it 'parses out the "errors" attribute from the response body JSON' do
|
210
|
+
parser.send(:response_errors).should eql([{ 'foo' => 'cannot be "bar"' }])
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
describe '#build_object_with_attributes' do
|
215
|
+
context 'when the attributes has a "type" key' do
|
216
|
+
let(:attributes) { { 'type' => 'Author', 'name' => 'Chuck Palahniuk' } }
|
217
|
+
|
218
|
+
it 'creates a new instance of the "type" with the attributes' do
|
219
|
+
Author.should_receive(:new).with(attributes)
|
220
|
+
parser.send(:build_object_with_attributes, attributes)
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
context 'when the attributes does not have a "type" key' do
|
225
|
+
let(:instance) { mock }
|
226
|
+
let(:attributes) { { 'name' => 'Fido' } }
|
227
|
+
it 'merges attributes into a new object of the owner class' do
|
228
|
+
Dog.should_receive(:new).and_return(instance)
|
229
|
+
instance.should_receive(:merge_attributes).with(attributes)
|
230
|
+
parser.send(:build_object_with_attributes, attributes)
|
231
|
+
end
|
232
|
+
end
|
15
233
|
end
|
16
234
|
|
17
235
|
end
|
metadata
CHANGED
@@ -1,13 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: morpheus
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
hash:
|
4
|
+
hash: 3
|
5
5
|
prerelease:
|
6
6
|
segments:
|
7
7
|
- 0
|
8
8
|
- 3
|
9
|
-
-
|
10
|
-
version: 0.3.
|
9
|
+
- 8
|
10
|
+
version: 0.3.8
|
11
11
|
platform: ruby
|
12
12
|
authors:
|
13
13
|
- Ryan Moran
|
@@ -15,7 +15,8 @@ autorequire:
|
|
15
15
|
bindir: bin
|
16
16
|
cert_chain: []
|
17
17
|
|
18
|
-
date: 2012-
|
18
|
+
date: 2012-02-09 00:00:00 -08:00
|
19
|
+
default_executable:
|
19
20
|
dependencies:
|
20
21
|
- !ruby/object:Gem::Dependency
|
21
22
|
name: yajl-ruby
|
@@ -55,7 +56,7 @@ dependencies:
|
|
55
56
|
requirement: &id003 !ruby/object:Gem::Requirement
|
56
57
|
none: false
|
57
58
|
requirements:
|
58
|
-
- - "
|
59
|
+
- - ">="
|
59
60
|
- !ruby/object:Gem::Version
|
60
61
|
hash: 7
|
61
62
|
segments:
|
@@ -71,7 +72,7 @@ dependencies:
|
|
71
72
|
requirement: &id004 !ruby/object:Gem::Requirement
|
72
73
|
none: false
|
73
74
|
requirements:
|
74
|
-
- - "
|
75
|
+
- - ">="
|
75
76
|
- !ruby/object:Gem::Version
|
76
77
|
hash: 7
|
77
78
|
segments:
|
@@ -87,7 +88,7 @@ dependencies:
|
|
87
88
|
requirement: &id005 !ruby/object:Gem::Requirement
|
88
89
|
none: false
|
89
90
|
requirements:
|
90
|
-
- - "
|
91
|
+
- - ">="
|
91
92
|
- !ruby/object:Gem::Version
|
92
93
|
hash: 11
|
93
94
|
segments:
|
@@ -103,7 +104,7 @@ dependencies:
|
|
103
104
|
requirement: &id006 !ruby/object:Gem::Requirement
|
104
105
|
none: false
|
105
106
|
requirements:
|
106
|
-
- - "
|
107
|
+
- - ">="
|
107
108
|
- !ruby/object:Gem::Version
|
108
109
|
hash: 7
|
109
110
|
segments:
|
@@ -137,12 +138,12 @@ dependencies:
|
|
137
138
|
requirements:
|
138
139
|
- - ~>
|
139
140
|
- !ruby/object:Gem::Version
|
140
|
-
hash:
|
141
|
+
hash: 45
|
141
142
|
segments:
|
142
143
|
- 2
|
143
|
-
-
|
144
|
-
-
|
145
|
-
version: 2.
|
144
|
+
- 8
|
145
|
+
- 1
|
146
|
+
version: 2.8.1
|
146
147
|
type: :development
|
147
148
|
version_requirements: *id008
|
148
149
|
- !ruby/object:Gem::Dependency
|
@@ -169,12 +170,12 @@ dependencies:
|
|
169
170
|
requirements:
|
170
171
|
- - ~>
|
171
172
|
- !ruby/object:Gem::Version
|
172
|
-
hash:
|
173
|
+
hash: 57
|
173
174
|
segments:
|
174
175
|
- 4
|
175
176
|
- 5
|
176
|
-
-
|
177
|
-
version: 4.5.
|
177
|
+
- 9
|
178
|
+
version: 4.5.9
|
178
179
|
type: :development
|
179
180
|
version_requirements: *id010
|
180
181
|
description: RESTful API Client
|
@@ -190,7 +191,6 @@ files:
|
|
190
191
|
- .autotest
|
191
192
|
- .gitignore
|
192
193
|
- .rspec
|
193
|
-
- .rvmrc
|
194
194
|
- Gemfile
|
195
195
|
- LICENSE.txt
|
196
196
|
- README.md
|
@@ -302,6 +302,7 @@ files:
|
|
302
302
|
- spec/shared/active_model_lint_test.rb
|
303
303
|
- spec/spec_helper.rb
|
304
304
|
- spec/support/configuration.rb
|
305
|
+
has_rdoc: true
|
305
306
|
homepage: ""
|
306
307
|
licenses: []
|
307
308
|
|
@@ -331,7 +332,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
331
332
|
requirements: []
|
332
333
|
|
333
334
|
rubyforge_project:
|
334
|
-
rubygems_version: 1.
|
335
|
+
rubygems_version: 1.6.2
|
335
336
|
signing_key:
|
336
337
|
specification_version: 3
|
337
338
|
summary: RESTful API Client
|
data/.rvmrc
DELETED
@@ -1 +0,0 @@
|
|
1
|
-
rvm use ree-1.8.7-2010.02@morpheus
|