her 0.3.8 → 0.4
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 +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
|