api-resource 0.4.1 → 0.4.2

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6690fd3a887d05228e41156d7b4896ccba029065
4
- data.tar.gz: e44f4accee730e338320003263919368b26ae5a2
3
+ metadata.gz: 5399345c2d2f04410d0c5d101a341c6118c4f2f3
4
+ data.tar.gz: b856b7ed659a37531a2c647f6f39d482e73c303e
5
5
  SHA512:
6
- metadata.gz: fdd55298b58b2dead869554975cec7f7fd081e6f11912f7858bea6d552456320549b0a58d87127a1e4b1252ecb7304305dda9040ed305ee45b7f1176213ec1ff
7
- data.tar.gz: 54eb3a96898c86de349ba47e704063747259277cd3bf4190e26d095a5d00be0c6ab6b0febdc1c4f1e3a61f2b3340c2744974fb2c36867414cfb9463a1393c907
6
+ metadata.gz: 25e9c12ee00a9cb4094dd3b4bd1db79269b21ed9906e0a1f1a55f27fc0a3ec62034dff2db46256f9fe1430cadaeea85640cdc760e6e5ae784cc72f7e6e727ca5
7
+ data.tar.gz: 40d994e49684c47d585120f109931cb4416caec48ca156f668768ce9ac6667d1d7346df940ca6d311c9a7e4ced0930689863ab1b7f5caad90690d29c3bc86f5b
@@ -7,6 +7,10 @@ require 'active_support/core_ext/hash/indifferent_access'
7
7
  require 'active_support/core_ext/object/to_param'
8
8
 
9
9
  module ApiResource
10
+
11
+ class ResourceError < StandardError
12
+ end
13
+
10
14
  class Resource
11
15
  include ActiveModel::Model
12
16
  include ActiveModel::Serializers::JSON
@@ -28,18 +32,30 @@ module ApiResource
28
32
  self.attributes = hash if hash
29
33
  end
30
34
 
35
+ def self.resource_path
36
+ @resource_path || resource_name
37
+ end
38
+
39
+ def self.resource_path=(path)
40
+ @resource_path = path
41
+ end
42
+
31
43
  def self.resource_name
32
- self.to_s.tableize
44
+ name.to_s.tableize
45
+ end
46
+
47
+ [:resource_name, :resource_path].each do |name|
48
+ define_method(name) { self.class.public_send(name) }
33
49
  end
34
50
 
35
51
  def self.find(id)
36
- result = client(:get, {}, resource_name, id)
52
+ result = client(:get, {}, resource_path, id)
37
53
  json = JSON.parse(result)
38
54
  self.new(json['data'] || json)
39
55
  end
40
56
 
41
57
  def self.all
42
- result = client(:get, {}, resource_name)
58
+ result = client(:get, {}, resource_path)
43
59
  create_resource_collection(result)
44
60
  end
45
61
 
@@ -59,23 +75,31 @@ module ApiResource
59
75
  end
60
76
 
61
77
  def self.where(options, verb=:get)
62
- result = client(verb, options, resource_name)
78
+ result = client(verb, options, resource_path)
63
79
  create_resource_collection(result)
64
80
  end
65
81
 
66
82
  def save!
67
- if self.id
68
- self.class.client(:put, attributes, self.class.resource_name)
83
+ submit_resource(resource_path, true)
84
+ self
85
+ end
86
+
87
+ def submit!(options={})
88
+ path = options.fetch(:path, resource_path)
89
+ type = options[:type]
90
+ json = submit_resource(path, false)
91
+ meta = json['meta']
92
+ type ||= meta && meta['type']
93
+ if type
94
+ returned_type = self.class.load_class(type)
95
+ returned_type.new(json['data'] || json)
69
96
  else
70
- result = self.class.client(:post, attributes, self.class.resource_name)
71
- json = JSON.parse(result)
72
- self.attributes = json['data'] || json
97
+ self
73
98
  end
74
- self
75
99
  end
76
100
 
77
101
  def destroy!
78
- self.class.client(:delete, id, self.class.resource_name) if self.id
102
+ self.class.client(:delete, id, resource_path) if self.id
79
103
  self
80
104
  end
81
105
 
@@ -85,18 +109,43 @@ module ApiResource
85
109
 
86
110
  protected
87
111
 
112
+ def submit_resource(path, save_attributes=false)
113
+ raise ResourceError unless valid?
114
+ verb = respond_to?(:id) && id ? :put : :post
115
+ begin
116
+ result = self.class.client(verb, attributes, path)
117
+ rescue RestClient::ExceptionWithResponse => e
118
+ result = e.http_body
119
+ raise ResourceError, 'Error executing request'
120
+ ensure
121
+ json = parse_and_check_error(result)
122
+ self.attributes = json['data'] || json if verb == :post && save_attributes
123
+ end
124
+ json
125
+ end
126
+
127
+ def parse_and_check_error(result)
128
+ return nil if result.blank?
129
+ json = JSON.parse(result)
130
+ errors_hash = json['errors']
131
+ if errors_hash.is_a?(Hash)
132
+ errors_hash.each { |attr, ary| ary.each { |error| errors.add(attr, error) } }
133
+ nil
134
+ else
135
+ json
136
+ end
137
+ end
138
+
88
139
  def attributes=(hash)
89
140
  hash.each do |key, value|
90
141
  if respond_to? "#{key}="
91
142
  send("#{key}=", value)
92
143
  else
93
- # check for embedded resource
144
+ # define method for associated embedded resource(s)
94
145
  if value.is_a?(Hash)
95
- meta = value['meta']
96
- data = value['data'] || value
146
+ meta, data = value['meta'], value['data'] || value
97
147
  elsif value.is_a?(Array)
98
- meta = nil
99
- data = value
148
+ meta, data = nil, value
100
149
  else
101
150
  next
102
151
  end
@@ -124,7 +173,7 @@ module ApiResource
124
173
  end
125
174
 
126
175
  def self.with_parent_resource(parent_resource_id, parent_resource_name)
127
- result = client(:get, {}, parent_resource_name.pluralize, parent_resource_id, self.resource_name)
176
+ result = client(:get, {}, parent_resource_name.pluralize, parent_resource_id, resource_path)
128
177
  create_resource_collection(result)
129
178
  end
130
179
 
@@ -139,7 +188,7 @@ module ApiResource
139
188
  end
140
189
 
141
190
  def self.client(verb, params, *paths)
142
- raise StandardError.new('Empty base_url') if base_url.blank?
191
+ raise RuntimeError, 'Empty base_url' if base_url.blank?
143
192
  url = base_url
144
193
  url += '/' unless url.end_with?('/')
145
194
  url += paths.join('/')
@@ -1,3 +1,3 @@
1
1
  module ApiResource
2
- VERSION = '0.4.1'
2
+ VERSION = '0.4.2'
3
3
  end
@@ -30,7 +30,7 @@ RSpec.describe ApiResource::Resource do
30
30
  def req(verb, path, resource, status=200)
31
31
  stub_request(verb, "#{BlogResource.base_url}#{path}").
32
32
  with(headers: { 'Accept' => 'application/json' }).
33
- to_return(status: status, body: resource === String ? resource : resource.to_json)
33
+ to_return(status: status, body: resource.is_a?(String) || resource.blank? ? resource : resource.to_json)
34
34
  end
35
35
 
36
36
  before do
@@ -191,7 +191,7 @@ RSpec.describe ApiResource::Resource do
191
191
  expect(blog.id).to be_nil
192
192
  expected_value = { data: { id: 325, title: 'hello' } }
193
193
 
194
- stub = req(:post, '/blogs', expected_value, 201)
194
+ stub = req(:post, '/blogs', expected_value, 201)
195
195
  returned = blog.save!
196
196
  expect(returned).to eq(blog)
197
197
  check_returned_object(Blog, expected_value, blog)
@@ -199,9 +199,9 @@ RSpec.describe ApiResource::Resource do
199
199
  end
200
200
  it 'PUTs data when id is not nil' do
201
201
  blog_id = 525
202
- blog = Blog.new(id: blog_id, title: 'hello')
202
+ blog = Blog.new(id: blog_id, title: 'hello')
203
203
 
204
- stub = req(:put, '/blogs', '', 204)
204
+ stub = req(:put, '/blogs', nil, 204)
205
205
  returned = blog.save!
206
206
  expect(returned).to eq(blog)
207
207
  expect(blog.id).to eq(blog_id)
@@ -211,18 +211,60 @@ RSpec.describe ApiResource::Resource do
211
211
 
212
212
  context '#destroy!' do
213
213
  it 'DELETEs data when id is not nil' do
214
- blog = Blog.new(id: 323, title: 'hello')
215
- stub = req(:delete, '/blogs', '', 204)
214
+ blog = Blog.new(id: 323, title: 'hello')
215
+ stub = req(:delete, '/blogs', '', 204)
216
216
  returned = blog.destroy!
217
217
  expect(returned).to eq(blog)
218
218
  expect(stub).to have_been_requested
219
219
  end
220
220
  it 'not DELETE data when id is nil' do
221
- blog = Blog.new(id: nil, title: 'hello')
222
- stub = req(:delete, '/blogs', '', 204)
221
+ blog = Blog.new(id: nil, title: 'hello')
222
+ stub = req(:delete, '/blogs', '', 204)
223
223
  returned = blog.destroy!
224
224
  expect(returned).to eq(blog)
225
225
  expect(stub).to_not have_been_requested
226
226
  end
227
227
  end
228
+
229
+ context '#submit!' do
230
+
231
+ class SignUp < BlogResource
232
+ self.resource_path = 'sign_up'
233
+ attr_accessor :email, :password, :password_confirmation, :name
234
+ validates :name, presence: true
235
+ end
236
+
237
+ class User < BlogResource
238
+ attr_accessor :id, :email, :name
239
+ end
240
+
241
+ let (:sign_up) { SignUp.new(email: 'user@example.com', password: 'pass', password_confirmation: 'pass', name: 'Joe') }
242
+
243
+ before do
244
+
245
+ end
246
+ it 'uses the resource_path and uses the type in meta' do
247
+ expected_value = { data: { id: 3, email: 'user@example.com', name: 'Jane' }, meta: { type: 'user' } }
248
+ stub = req(:post, '/sign_up', expected_value, 200)
249
+ returned = sign_up.submit!
250
+ check_returned_object(User, expected_value, returned)
251
+ expect(stub).to have_been_requested
252
+ end
253
+
254
+ it 'sets the returned errors' do
255
+ expected_value = { errors: { password_confrimation: ["can't be blank"], email: ["can't be blank", 'is invalid'] } }
256
+ stub = req(:post, '/sign_up', expected_value, 422)
257
+ expect(proc { sign_up.submit! }).to raise_error(ApiResource::ResourceError)
258
+ expect(sign_up.errors.messages).to match(expected_value[:errors])
259
+ expect(stub).to have_been_requested
260
+ end
261
+
262
+ it 'no request if not valid' do
263
+ sign_up.name = nil
264
+ stub = req(:post, '/sign_up', nil, 422)
265
+ expect(proc { sign_up.submit! }).to raise_error(ApiResource::ResourceError)
266
+ expect(sign_up.errors[:name]).to be_truthy
267
+ expect(stub).to_not have_been_requested
268
+ end
269
+ end
228
270
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api-resource
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.1
4
+ version: 0.4.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Chaker Nakhli
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-20 00:00:00.000000000 Z
11
+ date: 2015-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: simple-hmac