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 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
 
@@ -33,7 +33,8 @@ module Her
33
33
  extend Her::Model::Hooks
34
34
 
35
35
  # Define default settings
36
- base_path = self.name.split("::").last.underscore.pluralize
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
@@ -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
- @data.dup
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 = parsed_data[: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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Her
2
- VERSION = "0.3.8"
2
+ VERSION = "0.4"
3
3
  end
@@ -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.3.8
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-14 00:00:00.000000000 Z
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: -2988245247515927159
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: -2988245247515927159
189
+ hash: -2845158545991818661
190
190
  requirements: []
191
191
  rubyforge_project:
192
- rubygems_version: 1.8.24
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