her 0.3.8 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +53 -0
- data/lib/her/model.rb +2 -1
- data/lib/her/model/orm.rb +35 -7
- data/lib/her/model/paths.rb +7 -0
- data/lib/her/version.rb +1 -1
- data/spec/model/orm_spec.rb +87 -0
- metadata +5 -5
data/README.md
CHANGED
@@ -372,6 +372,58 @@ The available hooks are:
|
|
372
372
|
* `after_destroy`
|
373
373
|
* `after_find`
|
374
374
|
|
375
|
+
### JSON attributes-wrapping
|
376
|
+
|
377
|
+
Her supports *sending* and *parsing* JSON data wrapped in a root element (to be compatible with Rails’ `include_root_in_json` setting), like so:
|
378
|
+
|
379
|
+
#### Sending
|
380
|
+
|
381
|
+
If you want to send all data to your API wrapped in a *root* element based on the model name.
|
382
|
+
|
383
|
+
```ruby
|
384
|
+
class User
|
385
|
+
include Her::Model
|
386
|
+
include_root_in_json true
|
387
|
+
end
|
388
|
+
|
389
|
+
class Article
|
390
|
+
include Her::Model
|
391
|
+
include_root_in_json :post
|
392
|
+
end
|
393
|
+
|
394
|
+
User.create(:fullname => "Tobias Fünke")
|
395
|
+
# POST { "user": { "fullname": "Tobias Fünke" } } to /users
|
396
|
+
|
397
|
+
Article.create(:title => "Hello world.")
|
398
|
+
# POST { "post": { "title": "Hello world." } } to /articles
|
399
|
+
```
|
400
|
+
|
401
|
+
#### Parsing
|
402
|
+
|
403
|
+
If the API returns data wrapped in a *root* element based on the model name.
|
404
|
+
|
405
|
+
```ruby
|
406
|
+
class User
|
407
|
+
include Her::Model
|
408
|
+
parse_root_in_json true
|
409
|
+
end
|
410
|
+
|
411
|
+
class Article
|
412
|
+
include Her::Model
|
413
|
+
parse_root_in_json :post
|
414
|
+
end
|
415
|
+
|
416
|
+
# POST /users returns { "user": { "fullname": "Tobias Fünke" } }
|
417
|
+
user = User.create(:fullname => "Tobias Fünke")
|
418
|
+
user.fullname # => "Tobias Fünke"
|
419
|
+
|
420
|
+
# POST /articles returns { "post": { "title": "Hello world." } }
|
421
|
+
article = Article.create(:title => "Hello world.")
|
422
|
+
article.title # => "Hello world."
|
423
|
+
```
|
424
|
+
|
425
|
+
Of course, you can use both `include_root_in_json` and `parse_root_in_json` at the same time.
|
426
|
+
|
375
427
|
### Custom requests
|
376
428
|
|
377
429
|
You can easily define custom requests for your models using `custom_get`, `custom_post`, etc.
|
@@ -662,6 +714,7 @@ These fine folks helped with Her:
|
|
662
714
|
* [@pencil](https://github.com/pencil)
|
663
715
|
* [@joanniclaborde](https://github.com/joanniclaborde)
|
664
716
|
* [@seanreads](https://github.com/seanreads)
|
717
|
+
* [@jonkarna](https://github.com/jonkarna)
|
665
718
|
|
666
719
|
## License
|
667
720
|
|
data/lib/her/model.rb
CHANGED
@@ -33,7 +33,8 @@ module Her
|
|
33
33
|
extend Her::Model::Hooks
|
34
34
|
|
35
35
|
# Define default settings
|
36
|
-
|
36
|
+
root_element self.name.split("::").last.underscore
|
37
|
+
base_path = root_element.pluralize
|
37
38
|
collection_path "#{base_path}"
|
38
39
|
resource_path "#{base_path}/:id"
|
39
40
|
uses_api Her::API.default_api
|
data/lib/her/model/orm.rb
CHANGED
@@ -24,7 +24,7 @@ module Her
|
|
24
24
|
# @private
|
25
25
|
def self.initialize_collection(klass, parsed_data={})
|
26
26
|
collection_data = parsed_data[:data].map do |item_data|
|
27
|
-
resource = klass.new(item_data)
|
27
|
+
resource = klass.new(klass.parse(item_data))
|
28
28
|
klass.wrap_in_hooks(resource, :find)
|
29
29
|
resource
|
30
30
|
end
|
@@ -150,7 +150,7 @@ module Her
|
|
150
150
|
|
151
151
|
self.class.wrap_in_hooks(resource, *hooks) do |resource, klass|
|
152
152
|
klass.request(params.merge(:_method => method, :_path => "#{request_path}")) do |parsed_data|
|
153
|
-
self.data = parsed_data[:data] if parsed_data[:data].any?
|
153
|
+
self.data = self.class.parse(parsed_data[:data]) if parsed_data[:data].any?
|
154
154
|
self.metadata = parsed_data[:metadata]
|
155
155
|
self.errors = parsed_data[:errors]
|
156
156
|
|
@@ -171,7 +171,7 @@ module Her
|
|
171
171
|
resource = self
|
172
172
|
self.class.wrap_in_hooks(resource, :destroy) do |resource, klass|
|
173
173
|
klass.request(:_method => :delete, :_path => "#{request_path}") do |parsed_data|
|
174
|
-
self.data = parsed_data[:data]
|
174
|
+
self.data = self.class.parse(parsed_data[:data])
|
175
175
|
self.metadata = parsed_data[:metadata]
|
176
176
|
self.errors = parsed_data[:errors]
|
177
177
|
end
|
@@ -185,7 +185,11 @@ module Her
|
|
185
185
|
# @user.to_params
|
186
186
|
# # => { :id => 1, :name => 'John Smith' }
|
187
187
|
def to_params
|
188
|
-
|
188
|
+
if self.class.include_root_in_json
|
189
|
+
{ (self.class.include_root_in_json == true ? self.class.root_element : self.class.include_root_in_json) => @data.dup }
|
190
|
+
else
|
191
|
+
@data.dup
|
192
|
+
end
|
189
193
|
end
|
190
194
|
|
191
195
|
module ClassMethods
|
@@ -196,6 +200,17 @@ module Her
|
|
196
200
|
Her::Model::ORM.initialize_collection(self, parsed_data)
|
197
201
|
end
|
198
202
|
|
203
|
+
# Parse data before assigning it to a resource
|
204
|
+
#
|
205
|
+
# @param [Hash] data
|
206
|
+
def parse(data)
|
207
|
+
if parse_root_in_json
|
208
|
+
parse_root_in_json == true ? data[root_element.to_sym] : data[parse_root_in_json]
|
209
|
+
else
|
210
|
+
data
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
199
214
|
# Fetch specific resource(s) by their ID
|
200
215
|
#
|
201
216
|
# @example
|
@@ -210,7 +225,7 @@ module Her
|
|
210
225
|
results = ids.flatten.compact.uniq.map do |id|
|
211
226
|
resource = nil
|
212
227
|
request(params.merge(:_method => :get, :_path => "#{build_request_path(params.merge(:id => id))}")) do |parsed_data|
|
213
|
-
resource = new(parsed_data[:data].merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
228
|
+
resource = new(parse(parsed_data[:data]).merge :_metadata => parsed_data[:data], :_errors => parsed_data[:errors])
|
214
229
|
wrap_in_hooks(resource, :find)
|
215
230
|
end
|
216
231
|
resource
|
@@ -243,8 +258,9 @@ module Her
|
|
243
258
|
wrap_in_hooks(resource, :create, :save) do |resource, klass|
|
244
259
|
params = resource.to_params
|
245
260
|
request(params.merge(:_method => :post, :_path => "#{build_request_path(params)}")) do |parsed_data|
|
261
|
+
data = parse(parsed_data[:data])
|
246
262
|
resource.instance_eval do
|
247
|
-
@data =
|
263
|
+
@data = data
|
248
264
|
@metadata = parsed_data[:metadata]
|
249
265
|
@errors = parsed_data[:errors]
|
250
266
|
end
|
@@ -271,7 +287,7 @@ module Her
|
|
271
287
|
# # Called via DELETE "/users/1"
|
272
288
|
def destroy_existing(id, params={})
|
273
289
|
request(params.merge(:_method => :delete, :_path => "#{build_request_path(params.merge(:id => id))}")) do |parsed_data|
|
274
|
-
new(parsed_data[:data])
|
290
|
+
new(parse(parsed_data[:data]))
|
275
291
|
end
|
276
292
|
end
|
277
293
|
|
@@ -282,6 +298,18 @@ module Her
|
|
282
298
|
memo
|
283
299
|
end
|
284
300
|
end
|
301
|
+
|
302
|
+
# Return or change the value of `include_root_in_json`
|
303
|
+
def include_root_in_json(value=nil)
|
304
|
+
return @include_root_in_json if value.nil?
|
305
|
+
@include_root_in_json = value
|
306
|
+
end
|
307
|
+
|
308
|
+
# Return or change the value of `parse_root_in`
|
309
|
+
def parse_root_in_json(value=nil)
|
310
|
+
return @parse_root_in_json if value.nil?
|
311
|
+
@parse_root_in_json = value
|
312
|
+
end
|
285
313
|
end
|
286
314
|
end
|
287
315
|
end
|
data/lib/her/model/paths.rb
CHANGED
@@ -16,6 +16,7 @@ module Her
|
|
16
16
|
end
|
17
17
|
|
18
18
|
module ClassMethods
|
19
|
+
|
19
20
|
# Defines a custom collection path for the resource
|
20
21
|
#
|
21
22
|
# @example
|
@@ -69,6 +70,12 @@ module Her
|
|
69
70
|
parameters.delete($1.to_sym) || parameters.delete("_#{$1}".to_sym) || raise(Her::Errors::PathError.new("Missing :_#{$1} parameter to build the request path (#{path})."))
|
70
71
|
end
|
71
72
|
end
|
73
|
+
|
74
|
+
# Return or change the value of `root_element`
|
75
|
+
def root_element(value=nil)
|
76
|
+
return @root_element if value.nil?
|
77
|
+
@root_element = value
|
78
|
+
end
|
72
79
|
end
|
73
80
|
end
|
74
81
|
end
|
data/lib/her/version.rb
CHANGED
data/spec/model/orm_spec.rb
CHANGED
@@ -462,4 +462,91 @@ describe Her::Model::ORM do
|
|
462
462
|
hash.should == { user => false }
|
463
463
|
end
|
464
464
|
end
|
465
|
+
|
466
|
+
context "when include_root_in_json is true" do
|
467
|
+
context "when include_root_in_json is true" do
|
468
|
+
before do
|
469
|
+
spawn_model "Foo::User" do
|
470
|
+
include_root_in_json true
|
471
|
+
end
|
472
|
+
end
|
473
|
+
|
474
|
+
it "wraps params in the element name" do
|
475
|
+
@new_user = Foo::User.new(:fullname => "Tobias Fünke")
|
476
|
+
@new_user.to_params.should == { 'user' => { :fullname => "Tobias Fünke" } }
|
477
|
+
end
|
478
|
+
end
|
479
|
+
|
480
|
+
context "when include_root_in_json is set to another value" do
|
481
|
+
before do
|
482
|
+
spawn_model "Foo::User" do
|
483
|
+
include_root_in_json :person
|
484
|
+
end
|
485
|
+
end
|
486
|
+
|
487
|
+
it "wraps params in the specified value" do
|
488
|
+
@new_user = Foo::User.new(:fullname => "Tobias Fünke")
|
489
|
+
@new_user.to_params.should == { :person => { :fullname => "Tobias Fünke" } }
|
490
|
+
end
|
491
|
+
end
|
492
|
+
end
|
493
|
+
|
494
|
+
context "when parse_root_in_json is set" do
|
495
|
+
before do
|
496
|
+
Her::API.setup :url => "https://api.example.com" do |builder|
|
497
|
+
builder.use Her::Middleware::FirstLevelParseJSON
|
498
|
+
builder.use Faraday::Request::UrlEncoded
|
499
|
+
end
|
500
|
+
end
|
501
|
+
|
502
|
+
context "when parse_root_in_json is true" do
|
503
|
+
before do
|
504
|
+
Her::API.default_api.connection.adapter :test do |stub|
|
505
|
+
stub.post("/users") { |env| [200, {}, { :user => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
|
506
|
+
stub.get("/users") { |env| [200, {}, [{ :user => { :id => 1, :fullname => "Lindsay Fünke" } }].to_json] }
|
507
|
+
stub.get("/users/1") { |env| [200, {}, { :user => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
|
508
|
+
stub.put("/users/1") { |env| [200, {}, { :user => { :id => 1, :fullname => "Tobias Fünke Jr." } }.to_json] }
|
509
|
+
end
|
510
|
+
|
511
|
+
spawn_model("Foo::User") { parse_root_in_json true }
|
512
|
+
end
|
513
|
+
|
514
|
+
it "parse the data from the JSON root element after .create" do
|
515
|
+
@new_user = Foo::User.create(:fullname => "Lindsay Fünke")
|
516
|
+
@new_user.fullname.should == "Lindsay Fünke"
|
517
|
+
end
|
518
|
+
|
519
|
+
it "parse the data from the JSON root element after .all" do
|
520
|
+
@users = Foo::User.all
|
521
|
+
@users.first.fullname.should == "Lindsay Fünke"
|
522
|
+
end
|
523
|
+
|
524
|
+
it "parse the data from the JSON root element after .find" do
|
525
|
+
@user = Foo::User.find(1)
|
526
|
+
@user.fullname.should == "Lindsay Fünke"
|
527
|
+
end
|
528
|
+
|
529
|
+
it "parse the data from the JSON root element after .save" do
|
530
|
+
@user = Foo::User.find(1)
|
531
|
+
@user.fullname = "Tobias Fünke"
|
532
|
+
@user.save
|
533
|
+
@user.fullname.should == "Tobias Fünke Jr."
|
534
|
+
end
|
535
|
+
end
|
536
|
+
|
537
|
+
context "when parse_root_in_json is set to a symbol" do
|
538
|
+
before do
|
539
|
+
Her::API.default_api.connection.adapter :test do |stub|
|
540
|
+
stub.post("/users") { |env| [200, {}, { :person => { :id => 1, :fullname => "Lindsay Fünke" } }.to_json] }
|
541
|
+
end
|
542
|
+
|
543
|
+
spawn_model("Foo::User") { parse_root_in_json :person }
|
544
|
+
end
|
545
|
+
|
546
|
+
it "parse the data with the symbol" do
|
547
|
+
@new_user = Foo::User.create(:fullname => "Lindsay Fünke")
|
548
|
+
@new_user.fullname.should == "Lindsay Fünke"
|
549
|
+
end
|
550
|
+
end
|
551
|
+
end
|
465
552
|
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.
|
4
|
+
version: '0.4'
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2013-01-
|
12
|
+
date: 2013-01-19 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rake
|
@@ -177,7 +177,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
|
|
177
177
|
version: '0'
|
178
178
|
segments:
|
179
179
|
- 0
|
180
|
-
hash: -
|
180
|
+
hash: -2845158545991818661
|
181
181
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
182
182
|
none: false
|
183
183
|
requirements:
|
@@ -186,10 +186,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
186
186
|
version: '0'
|
187
187
|
segments:
|
188
188
|
- 0
|
189
|
-
hash: -
|
189
|
+
hash: -2845158545991818661
|
190
190
|
requirements: []
|
191
191
|
rubyforge_project:
|
192
|
-
rubygems_version: 1.8.
|
192
|
+
rubygems_version: 1.8.23
|
193
193
|
signing_key:
|
194
194
|
specification_version: 3
|
195
195
|
summary: A simple Representational State Transfer-based Hypertext Transfer Protocol-powered
|