flexirest 1.3.35 → 1.4.0

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: e6b067ad0cc1312a876a41486f10d6368b3b34e0
4
- data.tar.gz: e2dd9f61d27a472b42a05d7798b752fcf5682da0
3
+ metadata.gz: 523560b561f09e067d5ccb4e00442091e821fc41
4
+ data.tar.gz: 6dbe9d04e9bb44e66c40c31c4b0e24cceea3d895
5
5
  SHA512:
6
- metadata.gz: 22af0afd6a7bbd09b14a7e816cd87cce96a966eac6495d3a6e78ee47532e000772261ebac173c9ebda037b1b306dd75a9aa06df7e2dbd48690591d17ca729bf1
7
- data.tar.gz: 6bf3a1d2cae10e37486bca474a91d3cdf74a44d7183b3dd2620a0abee736955e147030b2f6b740074b977115c790419679933b657f78a3da1f8c051d2d8d2892
6
+ metadata.gz: 6cef508630a6f7c15c9e8836a48ecbe6b4fd6ccd7a6c2f55cb9bb0e7a1bdb4bf0621159c0dcc31f65c8b9a3a5346d3304624bfccd23b2d51151760159872a8c3
7
+ data.tar.gz: 5152a2255c1719686cd48c64618c5cfc7ef56ecfc231ce1d18f58c7d0427da2348dc3c731c7621dce246dce8b3fae8f17f90e9456b22a5b1517e9d9c6a3b0830
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.4.0
4
+
5
+ Feature:
6
+
7
+ - New JSON API support - thanks to Mike Voets for all his hard work!
8
+
3
9
  ## 1.3.35
4
10
 
5
11
  Bugfix:
data/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  [![Average time to resolve an issue](http://isitmaintained.com/badge/resolution/flexirest/flexirest.svg)](http://isitmaintained.com/project/flexirest/flexirest "Average time to resolve an issue")
8
8
  [![Percentage of issues still open](http://isitmaintained.com/badge/open/flexirest/flexirest.svg)](http://isitmaintained.com/project/flexirest/flexirest "Percentage of issues still open")
9
9
 
10
- This gem is for accessing REST services in an ActiveRecord style. ActiveResource already exists for this, but it doesn't work where the resource naming doesn't follow Rails conventions, it doesn't have in-built caching and it's not as flexible in general.
10
+ This gem is for accessing REST services in an ActiveRecord style. ActiveResource already exists for this, but it doesn't work where the resource naming doesn't follow Rails conventions, it doesn't have in-built caching and it's not as flexible in general.
11
11
 
12
12
  If you are a previous user of ActiveRestClient, there's some more information on [why I created this fork and how to upgrade](https://github.com/flexirest/flexirest/blob/master/Migrating-from-ActiveRestClient.md).
13
13
 
@@ -19,40 +19,42 @@ If you are a previous user of ActiveRestClient, there's some more information on
19
19
  - [Get all people](#get-all-people)
20
20
  - [Ruby on Rails Integration](#ruby-on-rails-integration)
21
21
  - [Advanced Features](#advanced-features)
22
- - [Faraday Configuration](#faraday-configuration)
23
- - [Associations](#associations)
24
- - [Association Type 1 - Loading Other Classes](#association-type-1-loading-other-classes)
25
- - [Association Type 2 - Lazy Loading From Other URLs](#association-type-2-lazy-loading-from-other-urls)
26
- - [Association Type 3 - HAL Auto-loaded Resources](#association-type-3-hal-auto-loaded-resources)
27
- - [Association Type 4 - Nested Resources](#association-type-4-nested-resources)
28
- - [Caching](#caching)
29
- - [Using callbacks](#using-callbacks)
30
- - [Lazy Loading](#lazy-loading)
31
- - [Authentication](#authentication)
32
- - [Basic](#basic)
33
- - [Api-Auth](#api-auth)
34
- - [Body Types](#body-types)
35
- - [Parallel Requests](#parallel-requests)
36
- - [Faking Calls](#faking-calls)
37
- - [Per-request Timeouts](#per-request-timeouts)
38
- - [Per-request Params Encoding](#per-request-params-encoding)
39
- - [Automatic Conversion of Fields to Date/DateTime](#automatic-conversion-of-fields-to-datedatetime)
40
- - [Raw Requests](#raw-requests)
41
- - [Plain Requests](#plain-requests)
42
- - [Proxying APIs](#proxying-apis)
43
- - [Translating APIs](#translating-apis)
44
- - [Default Parameters](#default-parameters)
45
- - [Root element removal](#root-element-removal)
46
- - [Required Parameters](#required-parameters)
47
- - [Updating Only Changed/Dirty](#updating-only-changed-dirty)
48
- - [HTTP/Parse Error Handling](#httpparse-error-handling)
49
- - [Validation](#validation)
50
- - [Permitting nil values](#permitting-nil-values)
22
+ - [Faraday Configuration](#faraday-configuration)
23
+ - [Associations](#associations)
24
+ - [Association Type 1 - Loading Other Classes](#association-type-1-loading-other-classes)
25
+ - [Association Type 2 - Lazy Loading From Other URLs](#association-type-2-lazy-loading-from-other-urls)
26
+ - [Association Type 3 - HAL Auto-loaded Resources](#association-type-3-hal-auto-loaded-resources)
27
+ - [Association Type 4 - Nested Resources](#association-type-4-nested-resources)
28
+ - [Association Type 5 - JSON API Auto-loaded Resources](#association-type-5-json-api-auto-loaded-resources)
29
+ - [Combined Example](#combined-example)
30
+ - [Caching](#caching)
31
+ - [Using callbacks](#using-callbacks)
32
+ - [Lazy Loading](#lazy-loading)
33
+ - [Authentication](#authentication)
34
+ - [Basic](#basic)
35
+ - [Api-Auth](#api-auth)
36
+ - [Body Types](#body-types)
37
+ - [Parallel Requests](#parallel-requests)
38
+ - [Faking Calls](#faking-calls)
39
+ - [Per-request Timeouts](#per-request-timeouts)
40
+ - [Per-request Params Encoding](#per-request-params-encoding)
41
+ - [Automatic Conversion of Fields to Date/DateTime](#automatic-conversion-of-fields-to-datedatetime)
42
+ - [Raw Requests](#raw-requests)
43
+ - [Plain Requests](#plain-requests)
44
+ - [JSON API](#json-api)
45
+ - [Proxying APIs](#proxying-apis)
46
+ - [Translating APIs](#translating-apis)
47
+ - [Default Parameters](#default-parameters)
48
+ - [Root element removal](#root-element-removal)
49
+ - [Required Parameters](#required-parameters)
50
+ - [Updating Only Changed/Dirty](#updating-only-changed-dirty)
51
+ - [HTTP/Parse Error Handling](#httpparse-error-handling)
52
+ - [Validation](#validation)
53
+ - [Permitting nil values](#permitting-nil-values)
51
54
  - [Debugging](#debugging)
52
55
  - [XML Responses](#xml-responses)
53
56
  - [Contributing](#contributing)
54
57
 
55
-
56
58
  ## Installation
57
59
 
58
60
  Add this line to your application's Gemfile:
@@ -63,11 +65,15 @@ gem 'flexirest'
63
65
 
64
66
  And then execute:
65
67
 
66
- $ bundle
68
+ ```
69
+ $ bundle
70
+ ```
67
71
 
68
72
  Or install it yourself as:
69
73
 
70
- $ gem install flexirest
74
+ ```
75
+ $ gem install flexirest
76
+ ```
71
77
 
72
78
  ## Usage
73
79
 
@@ -91,7 +97,7 @@ class Person < Flexirest::Base
91
97
  end
92
98
  ```
93
99
 
94
- Note I've specified the base_url in the class above. This is useful where you want to be explicit or use different APIs for some classes and be explicit. If you have one server that's generally used, you can set it once with a simple line in a `config/initializer/{something}.rb` file:
100
+ Note I've specified the base_url in the class above. This is useful where you want to be explicit or use different APIs for some classes and be explicit. If you have one server that's generally used, you can set it once with a simple line in a `config/initializer/{something}.rb` file:
95
101
 
96
102
  ```ruby
97
103
  Flexirest::Base.base_url = "https://www.example.com/api/v1"
@@ -132,7 +138,7 @@ If an API returns an array of results and you have [will_paginate](https://rubyg
132
138
  end
133
139
  ```
134
140
 
135
- Note, you can assign to any attribute, whether it exists or not before and read from any attribute (which will return nil if not found). If you pass a string or a number to a method it will assume that it's for the "id" field. Any other field values must be passed as a hash and you can't mix passing a string/number and a hash.
141
+ Note, you can assign to any attribute, whether it exists or not before and read from any attribute (which will return nil if not found). If you pass a string or a number to a method it will assume that it's for the "id" field. Any other field values must be passed as a hash and you can't mix passing a string/number and a hash.
136
142
 
137
143
  ```ruby
138
144
  @person = Person.find(1234) # valid
@@ -142,7 +148,7 @@ Note, you can assign to any attribute, whether it exists or not before and read
142
148
  @person = Person.find(1234, :name => "Billy") # invalid
143
149
  ```
144
150
 
145
- You can also call any mapped method as an instance variable which will pass the current attribute set in as parameters (either GET or POST depending on the mapped method type). If the method returns a single instance it will assign the attributes of the calling object and return itself. If the method returns a list of instances, it will only return the list. So, we could rewrite the create call above as:
151
+ You can also call any mapped method as an instance variable which will pass the current attribute set in as parameters (either GET or POST depending on the mapped method type). If the method returns a single instance it will assign the attributes of the calling object and return itself. If the method returns a list of instances, it will only return the list. So, we could rewrite the create call above as:
146
152
 
147
153
  ```ruby
148
154
  @person = Person.new
@@ -154,7 +160,7 @@ puts @person.id
154
160
 
155
161
  The response of the #create call set the attributes at that point (any manually set attributes before that point are removed).
156
162
 
157
- If you have attributes beginning with a number, Ruby doesn't like this. So, you can use hash style notation to read/write the attributes:
163
+ If you have attributes beginning with a number, Ruby doesn't like this. So, you can use hash style notation to read/write the attributes:
158
164
 
159
165
  ```ruby
160
166
  @tv = Tv.find(model:"UE55U8000") # { "properties" : {"3d" : false} }
@@ -162,7 +168,7 @@ puts @tv.properties["3d"]
162
168
  @tv.properties["3d"] = true
163
169
  ```
164
170
 
165
- If you want to debug the response, using inspect on the response object may well be useful. However, if you want a simpler output, then you can call `#to_json` on the response object:
171
+ If you want to debug the response, using inspect on the response object may well be useful. However, if you want a simpler output, then you can call `#to_json` on the response object:
166
172
 
167
173
  ```ruby
168
174
  @person = Person.find(email:"something@example.com")
@@ -187,7 +193,7 @@ Flexirest::Base.adapter = :patron
187
193
 
188
194
  In versions before 1.2.0 the adapter was hardcoded to `:patron`, so if you want to ensure it still uses Patron, you should set this setting.
189
195
 
190
- If you want more control you can pass a **complete** configuration block ("complete" means that the block does not *override* [the default configuration](https://github.com/flexirest/flexirest/blob/5b1953d89e26c02ca74f74464ccb7cd4c9439dcc/lib/flexirest/configuration.rb#L184-L201), but rather *replaces* it). For available config variables look into the Faraday documentation.
196
+ If you want more control you can pass a **complete** configuration block ("complete" means that the block does not _override_ [the default configuration](https://github.com/flexirest/flexirest/blob/5b1953d89e26c02ca74f74464ccb7cd4c9439dcc/lib/flexirest/configuration.rb#L184-L201), but rather _replaces_ it). For available config variables look into the Faraday documentation.
191
197
 
192
198
  ```ruby
193
199
  Flexirest::Base.faraday_config do |faraday|
@@ -196,13 +202,14 @@ Flexirest::Base.faraday_config do |faraday|
196
202
  faraday.headers['User-Agent'] = "Flexirest/#{Flexirest::VERSION}"
197
203
  end
198
204
  ```
205
+
199
206
  ### Associations
200
207
 
201
- There are two types of association. One assumes when you call a method you actually want it to call the method on a separate class (as that class has other methods that are useful). The other is lazy loading related classes from a separate URL.
208
+ There are two types of association. One assumes when you call a method you actually want it to call the method on a separate class (as that class has other methods that are useful). The other is lazy loading related classes from a separate URL.
202
209
 
203
210
  #### Association Type 1 - Loading Other Classes
204
211
 
205
- If the call would return a single instance or a list of instances that should be considered another object, you can also specify this when mapping the method using the `:has_one` or `:has_many` options respectively. It doesn't call anything on that object except for instantiate it, but it does let you have objects of a different class to the one you initially called.
212
+ If the call would return a single instance or a list of instances that should be considered another object, you can also specify this when mapping the method using the `:has_one` or `:has_many` options respectively. It doesn't call anything on that object except for instantiate it, but it does let you have objects of a different class to the one you initially called.
206
213
 
207
214
  ```ruby
208
215
  class Expense < Flexirest::Base
@@ -226,7 +233,7 @@ puts @person.expenses.reduce {|e| e.inc_vat}
226
233
  puts @person.address.full_string
227
234
  ```
228
235
 
229
- You can also use `has_one`/`has_many` on the class level to allow chaining of classes. You can specify the class name or allow the system to automatically convert it to the singular class. For example:
236
+ You can also use `has_one`/`has_many` on the class level to allow chaining of classes. You can specify the class name or allow the system to automatically convert it to the singular class. For example:
230
237
 
231
238
  ```ruby
232
239
  class Expense < Flexirest::Base
@@ -283,7 +290,7 @@ When the `:array` option includes an attribute, it is assumed the values were re
283
290
 
284
291
  #### Association Type 2 - Lazy Loading From Other URLs
285
292
 
286
- When mapping the method, passing a list of attributes will cause any requests for those attributes to mapped to the URLs given in their responses. The response for the attribute may be one of the following:
293
+ When mapping the method, passing a list of attributes will cause any requests for those attributes to mapped to the URLs given in their responses. The response for the attribute may be one of the following:
287
294
 
288
295
  ```ruby
289
296
  "attribute" : "URL"
@@ -293,9 +300,9 @@ When mapping the method, passing a list of attributes will cause any requests fo
293
300
  "attribute" : { "something" : "URL"}
294
301
  ```
295
302
 
296
- The difference between the last 3 examples is that a key of `url` or `href` signifies it's a single object that is lazy loaded from the value specified. Any other keys assume that it's a nested set of URLs (like in the array situation, but accessible via the keys - e.g. object.attribute.something in the above example).
303
+ The difference between the last 3 examples is that a key of `url` or `href` signifies it's a single object that is lazy loaded from the value specified. Any other keys assume that it's a nested set of URLs (like in the array situation, but accessible via the keys - e.g. object.attribute.something in the above example).
297
304
 
298
- It is required that the URL is a complete URL including a protocol starting with "http". To configure this use code like:
305
+ It is required that the URL is a complete URL including a protocol starting with "http". To configure this use code like:
299
306
 
300
307
  ```ruby
301
308
  class Person < Flexirest::Base
@@ -316,9 +323,9 @@ And use it like this:
316
323
 
317
324
  #### Association Type 3 - HAL Auto-loaded Resources
318
325
 
319
- You don't need to define lazy attributes if they are defined using [HAL](http://stateless.co/hal_specification.html) (with an optional embedded representation). If your resource has an `_links` item (and optionally an `_embedded` item) then it will automatically treat the linked resources (with the `_embedded` cache) as if they were defined using `:lazy` as per type 2 above.
326
+ You don't need to define lazy attributes if they are defined using [HAL](http://stateless.co/hal_specification.html) (with an optional embedded representation). If your resource has an `_links` item (and optionally an `_embedded` item) then it will automatically treat the linked resources (with the `_embedded` cache) as if they were defined using `:lazy` as per type 2 above.
320
327
 
321
- If you need to, you can access properties of the HAL association. By default just using the HAL association gets the embedded resource (or requests the remote resource if not available in the `_embedded` list).
328
+ If you need to, you can access properties of the HAL association. By default just using the HAL association gets the embedded resource (or requests the remote resource if not available in the `_embedded` list).
322
329
 
323
330
  ```ruby
324
331
  @person = Person.find(1)
@@ -350,9 +357,31 @@ Ad.all(magazine_id: 1)
350
357
  Ad.create(magazine_id: 1, title: "My Add Title")
351
358
  ```
352
359
 
360
+ #### Association Type 5 - JSON API Auto-loaded Resources
361
+
362
+ If attributes are defined using [JSON API](http://jsonapi.org), you don't need to define lazy attributes. If your resource has a `links` object with a `related` item, it will automatically treat the linked resources as if they were defined using `:lazy`.
363
+
364
+ You need to activate JSON API by specifying the `json_api` proxy:
365
+
366
+ ```ruby
367
+ class Article < Flexirest::Base
368
+ proxy :json_api
369
+ end
370
+ ```
371
+
372
+ If you want to embed linked resources directly in the response (i.e. request a JSON API compound document), use the `includes` class method. The linked resource is accessed in the same was as if it was lazily loaded, but without the extra request:
373
+
374
+ ```ruby
375
+ # Makes a call to /articles with parameters: include=images
376
+ Article.includes(:images).all
377
+
378
+ # For nested resources, the include parameter becomes: include=images.tags,images.photographer
379
+ Article.includes(:images => [:tags, :photographer]).all
380
+ ```
381
+
353
382
  #### Combined Example
354
383
 
355
- OK, so let's say you have an API for getting articles. Each article has a property called `title` (which is a string) and a property `images` which includes a list of URIs. Following this URI would take you to a image API that returns the image's `filename` and `filesize`. We'll also assume this is a HAL compliant API. We would declare our two models (one for articles and one for images) like the following:
384
+ OK, so let's say you have an API for getting articles. Each article has a property called `title` (which is a string) and a property `images` which includes a list of URIs. Following this URI would take you to a image API that returns the image's `filename` and `filesize`. We'll also assume this is a HAL compliant API. We would declare our two models (one for articles and one for images) like the following:
356
385
 
357
386
  ```ruby
358
387
  class Article < Flexirest::Base
@@ -401,7 +430,7 @@ When it comes time to use it, you would do something like this:
401
430
  end
402
431
  ```
403
432
 
404
- At this point, only the HTTP call to '/articles/1' has been made. When you actually start using properties of the images list/image object then it makes a call to the URL given in the images list and you can use the properties as if it was a nested JSON object in the original response instead of just a URL:
433
+ At this point, only the HTTP call to '/articles/1' has been made. When you actually start using properties of the images list/image object then it makes a call to the URL given in the images list and you can use the properties as if it was a nested JSON object in the original response instead of just a URL:
405
434
 
406
435
  ```ruby
407
436
  @image = @article.images.first
@@ -411,7 +440,7 @@ puts @image.filesize
411
440
  # => 123456
412
441
  ```
413
442
 
414
- You can also treat `@image` looks like an Image class (and you should 100% treat it as one) it's technically a lazy loading proxy. So, if you cache the views for your application should only make HTTP API requests when actually necessary.
443
+ You can also treat `@image` looks like an Image class (and you should 100% treat it as one) it's technically a lazy loading proxy. So, if you cache the views for your application should only make HTTP API requests when actually necessary.
415
444
 
416
445
  ```ruby
417
446
  puts @image.nice_size
@@ -442,7 +471,7 @@ Flexirest::Base.cache_store = Redis::Store.new("redis://localhost:6379/0/cache")
442
471
 
443
472
  ### Using callbacks
444
473
 
445
- You can use callbacks to alter get/post parameters, the URL or set the post body (doing so overrides normal parameter insertion in to the body) before a request or to adjust the response after a request. This can either be a block or a named method (like ActionController's `before_callback`/`before_action` methods).
474
+ You can use callbacks to alter get/post parameters, the URL or set the post body (doing so overrides normal parameter insertion in to the body) before a request or to adjust the response after a request. This can either be a block or a named method (like ActionController's `before_callback`/`before_action` methods).
446
475
 
447
476
  The callback is passed the name of the method (e.g. `:save`) and an object (a request object for `before_request` and a response object for `after_request`). The request object has four public attributes `post_params` (a Hash of the POST parameters), `get_params` (a Hash of the GET parameters), `headers` and `url` (a `String` containing the full URL without GET parameters appended).
448
477
 
@@ -549,7 +578,6 @@ end
549
578
 
550
579
  This will return an array of the named method for each object or the response from the block and will have loaded the objects in to the resource.
551
580
 
552
-
553
581
  ### Authentication
554
582
 
555
583
  #### Basic
@@ -578,6 +606,7 @@ Flexirest::Base.api_auth_credentials(@access_id, @secret_key)
578
606
  ```
579
607
 
580
608
  You can also specify different credentials for different models just like configuring base_url
609
+
581
610
  ```ruby
582
611
  class Person < Flexirest::Base
583
612
  api_auth_credentials('123456', 'abcdef')
@@ -596,7 +625,7 @@ end
596
625
 
597
626
  ### Body Types
598
627
 
599
- By default Flexirest puts the body in to normal CGI parameters in K=V&K2=V2 format. However, if you want to use JSON for your PUT/POST requests, you can use either (the other option, the default, is `:form_encoded`):
628
+ By default Flexirest puts the body in to normal CGI parameters in K=V&K2=V2 format. However, if you want to use JSON for your PUT/POST requests, you can use either (the other option, the default, is `:form_encoded`):
600
629
 
601
630
  ```ruby
602
631
  class Person < Flexirest::Base
@@ -626,12 +655,14 @@ end
626
655
  ### Parallel Requests
627
656
 
628
657
  Sometimes you know you will need to make a bunch of requests and you don't want to wait for one to finish to start the next. When using parallel requests there is the potential to finish many requests all at the same time taking only as long as the single longest request. To use parallel requests you will need to set Flexirest to use a Faraday adapter that supports parallel requests [(such as Typhoeus)](https://github.com/lostisland/faraday/wiki/Parallel-requests).
658
+
629
659
  ```ruby
630
660
  # Set adapter to Typhoeus to use parallel requests
631
661
  Flexirest::Base.adapter = :typhoeus
632
662
  ```
633
663
 
634
664
  Now you just need to get ahold of the connection that is going to make the requests by specifying the same host that the models will be using. When inside the `in_parallel` block call request methods as usual and access the results after the `in_parallel` block ends.
665
+
635
666
  ```ruby
636
667
  Flexirest::ConnectionManager.in_parallel('https://www.example.com') do
637
668
  @person = Person.find(1234)
@@ -647,7 +678,7 @@ puts @employers.size #=> 7
647
678
 
648
679
  ### Faking Calls
649
680
 
650
- There are times when an API hasn't been developed yet, so you want to fake the API call response. To do this, you can simply pass a `fake` option when mapping the call containing the response.
681
+ There are times when an API hasn't been developed yet, so you want to fake the API call response. To do this, you can simply pass a `fake` option when mapping the call containing the response.
651
682
 
652
683
  ```ruby
653
684
  class Person < Flexirest::Base
@@ -655,7 +686,7 @@ class Person < Flexirest::Base
655
686
  end
656
687
  ```
657
688
 
658
- You may want to run a proc when faking data (to put information from the parameters in to the response or return different responses depending on the parameters). To do this just pass a proc to :fake:
689
+ You may want to run a proc when faking data (to put information from the parameters in to the response or return different responses depending on the parameters). To do this just pass a proc to :fake:
659
690
 
660
691
  ```ruby
661
692
  class Person < Flexirest::Base
@@ -665,7 +696,7 @@ end
665
696
 
666
697
  ### Per-request Timeouts
667
698
 
668
- There are times when an API is generally quick, but one call is very intensive. You don't want to set a global timeout in the Faraday configuration block, you just want to increase the timeout for this single call. To do this, you can simply pass a `timeout` option when mapping the call containing the response (in seconds).
699
+ There are times when an API is generally quick, but one call is very intensive. You don't want to set a global timeout in the Faraday configuration block, you just want to increase the timeout for this single call. To do this, you can simply pass a `timeout` option when mapping the call containing the response (in seconds).
669
700
 
670
701
  ```ruby
671
702
  class Person < Flexirest::Base
@@ -703,7 +734,7 @@ would output the following url
703
734
 
704
735
  ### Automatic Conversion of Fields to Date/DateTime
705
736
 
706
- By default Flexirest will attempt to convert all fields to a Date or DateTime object if it's a string and the value matches a pair of regular expressions. However, on large responses this can be computationally expensive. You can disable this automatic conversion completely with:
737
+ By default Flexirest will attempt to convert all fields to a Date or DateTime object if it's a string and the value matches a pair of regular expressions. However, on large responses this can be computationally expensive. You can disable this automatic conversion completely with:
707
738
 
708
739
  ```ruby
709
740
  Flexirest::Base.disable_automatic_date_parsing = true
@@ -734,7 +765,7 @@ This system respects `disable_automatic_date_parsing`, and will default to mappi
734
765
 
735
766
  ### Raw Requests
736
767
 
737
- Sometimes you have have a URL that you just want to force through, but have the response handled in the same way as normal objects or you want to have the callbacks run (say for authentication). The easiest way to do that is to call `_request` on the class:
768
+ Sometimes you have have a URL that you just want to force through, but have the response handled in the same way as normal objects or you want to have the callbacks run (say for authentication). The easiest way to do that is to call `_request` on the class:
738
769
 
739
770
  ```ruby
740
771
  class Person < Flexirest::Base
@@ -746,13 +777,13 @@ people = Person._request('http://api.example.com/v1/people') # Defaults to get w
746
777
  Person._request('http://api.example.com/v1/people', :post, {id:1234,name:"John"}) # Post with parameters
747
778
  ```
748
779
 
749
- If you want to use a lazy loaded request instead (so it will create an object that will only call the API if you use it), you can use `_lazy_request` instead of `_request`. If you want you can create a construct that creates and object that lazy loads itself from a given method (rather than a URL):
780
+ If you want to use a lazy loaded request instead (so it will create an object that will only call the API if you use it), you can use `_lazy_request` instead of `_request`. If you want you can create a construct that creates and object that lazy loads itself from a given method (rather than a URL):
750
781
 
751
782
  ```ruby
752
783
  @person = Person._lazy_request(Person._request_for(:find, 1234))
753
784
  ```
754
785
 
755
- This initially creates an Flexirest::Request object as if you'd called `Person.find(1234)` which is then passed in to the `_lazy_request` method to return an object that will call the request if any properties are actually used. This may be useful at some point, but it's actually easier to just prefix the `find` method call with `lazy_` like:
786
+ This initially creates an Flexirest::Request object as if you'd called `Person.find(1234)` which is then passed in to the `_lazy_request` method to return an object that will call the request if any properties are actually used. This may be useful at some point, but it's actually easier to just prefix the `find` method call with `lazy_` like:
756
787
 
757
788
  ```ruby
758
789
  @person = Person.lazy_find(1234)
@@ -786,9 +817,47 @@ end
786
817
 
787
818
  The response of a plain request (from either source) is a `Flexirest::PlainResponse` which acts like a string containing the response's body, but it also has a `_headers` method that returns the HTTP response headers and a `_status` method containing the response's HTTP method.
788
819
 
820
+ ### JSON API
821
+
822
+ If you are working with a [JSON API](http://jsonapi.org), you need to activate JSON API by specifying the `json_api` proxy:
823
+
824
+ ```ruby
825
+ class Article < Flexirest::Base
826
+ proxy :json_api
827
+ end
828
+ ```
829
+
830
+ This proxy translates requests according to the JSON API specifications, parses responses, and retrieves linked resources. It also adds the `Accept: application/vnd.api+json` header for all requests.
831
+
832
+ It supports lazy loading by default. Unless a compound document is returned from the connected JSON API service, it will make another request to the service for the specified linked resource.
833
+
834
+ To reduce the number of requests to the service, you can ask the service to include the linked resources in the response. Such responses are called "compound documents". To do this, use the `includes` method:
835
+
836
+ ```ruby
837
+ # Makes a call to /articles with parameters: include=images
838
+ Article.includes(:images).all
839
+
840
+ # For nested resources, the include parameter becomes: include=images.tags,images.photographer
841
+ Article.includes(:images => [:tags, :photographer]).all
842
+ ```
843
+
844
+ For post and patch requests, the proxy formats a JSON API complied request, and adds a `Content-Type: application/vnd.api+json` header. It guesses the `type` value in the resource object from the class name, but it can be set specifically with `alias_type`:
845
+
846
+ ```ruby
847
+ class Photographer < Flexirest::Base
848
+ proxy :json_api
849
+ # Sets the type in the resource object to "people"
850
+ alias_type :people
851
+
852
+ patch :update, '/photographers/:id'
853
+ end
854
+ ```
855
+
856
+ NB: Updating relationships is not yet supported.
857
+
789
858
  ### Proxying APIs
790
859
 
791
- Sometimes you may be working with an old API that returns JSON in a less than ideal format or the URL or parameters required have changed. In this case you can define a descendent of `Flexirest::ProxyBase`, pass it to your model as the proxy and have it rework URLs/parameters on the way out and the response on the way back in (already converted to a Ruby hash/array). By default any non-proxied URLs are just passed through to the underlying connection layer. For example:
860
+ Sometimes you may be working with an old API that returns JSON in a less than ideal format or the URL or parameters required have changed. In this case you can define a descendent of `Flexirest::ProxyBase`, pass it to your model as the proxy and have it rework URLs/parameters on the way out and the response on the way back in (already converted to a Ruby hash/array). By default any non-proxied URLs are just passed through to the underlying connection layer. For example:
792
861
 
793
862
  ```ruby
794
863
  class ArticleProxy < Flexirest::ProxyBase
@@ -815,8 +884,8 @@ Article.all.first_name == "Billy"
815
884
 
816
885
  This example does two things:
817
886
 
818
- 1. It rewrites the incoming URL for any requests matching "*/all*" to "/all_people"
819
- 2. It uses the `translate` method to move the "fname" attribute from the response body to be called "first_name". The translate method must return the new object at the end (either the existing object alterered, or a new object to replace it with)
887
+ 1. It rewrites the incoming URL for any requests matching "_/all_" to "/all_people"
888
+ 2. It uses the `translate` method to move the "fname" attribute from the response body to be called "first_name". The translate method must return the new object at the end (either the existing object alterered, or a new object to replace it with)
820
889
 
821
890
  As the comment shows, you can use `url value` to set the request URL to a particular value, or you can call `gsub!` on the url to replace parts of it using more complicated regular expressions.
822
891
 
@@ -829,7 +898,7 @@ get "/list" do
829
898
  end
830
899
  ```
831
900
 
832
- This example renames the get_parameter for the request from `identifier` to `id` (the same would have worked with post_params if it was a POST/PUT request). The `passthrough` method will take care of automatically recombining them in to the URL or encoding them in to the body as appropriate.
901
+ This example renames the get_parameter for the request from `identifier` to `id` (the same would have worked with post_params if it was a POST/PUT request). The `passthrough` method will take care of automatically recombining them in to the URL or encoding them in to the body as appropriate.
833
902
 
834
903
  If you want to manually set the body for the API yourself you can use the `body` method
835
904
 
@@ -848,7 +917,7 @@ The proxy block expects one of three things to be the return value of the block.
848
917
  2. The second option is to save the response from `passthrough` and use `translate` on it to alter the structure.
849
918
  3. The third option is to use `render` if you want to completely fake an API and return the JSON yourself
850
919
 
851
- To completely fake the API, you can do the following. Note, this is also achievable using the `fake` setting when mapping a method, however by doing it in a Proxy block means you can dynamically generate the JSON rather than just a hard coded string.
920
+ To completely fake the API, you can do the following. Note, this is also achievable using the `fake` setting when mapping a method, however by doing it in a Proxy block means you can dynamically generate the JSON rather than just a hard coded string.
852
921
 
853
922
  ```ruby
854
923
  put "/fake" do
@@ -858,9 +927,9 @@ end
858
927
 
859
928
  ### Translating APIs
860
929
 
861
- **IMPORTANT: This functionality has been deprecated in favour of the "Proxying APIs" functionality above. You should aim to remove this from your code as soon as possible.**
930
+ **IMPORTANT: This functionality has been deprecated in favour of the "Proxying APIs" functionality above. You should aim to remove this from your code as soon as possible.**
862
931
 
863
- Sometimes you may be working with an API that returns JSON in a less than ideal format. In this case you can define a barebones class and pass it to your model. The Translator class must have class methods that are passed the JSON object and should return an object in the correct format. It doesn't need to have a method unless it's going to translate that mapping though (so in the example below there's no list method). For example:
932
+ Sometimes you may be working with an API that returns JSON in a less than ideal format. In this case you can define a barebones class and pass it to your model. The Translator class must have class methods that are passed the JSON object and should return an object in the correct format. It doesn't need to have a method unless it's going to translate that mapping though (so in the example below there's no list method). For example:
864
933
 
865
934
  ```ruby
866
935
  class ArticleTranslator
@@ -934,7 +1003,7 @@ class Person < Flexirest::Base
934
1003
  get :all, '/people', :requires => [:active]
935
1004
  end
936
1005
 
937
- @people = Person.all # raises Flexirest::MissingParametersException
1006
+ @people = Person.all # raises Flexirest::MissingParametersException
938
1007
  @people = Person.all(active:false)
939
1008
  ```
940
1009
 
@@ -966,13 +1035,14 @@ end
966
1035
 
967
1036
  #### Additional Notes:
968
1037
 
969
- * The above examples specifically showed PATCH methods, but this is also available for POST and PUT methods for flexibility purposes (even though they break typical REST methodology).
970
- * This logic is currently evaluated before Required Parameters, so it is possible to ensure that requirements are met by some clever usage.
971
- - This means that if a method is `:requires => [:active], :only_changed => {active: false}` then `active` will always have a value and would always pass the `:requires` directive (so you need to be very careful because the answer may end up being `nil` if you didn't specifically set it).
1038
+ - The above examples specifically showed PATCH methods, but this is also available for POST and PUT methods for flexibility purposes (even though they break typical REST methodology).
1039
+ - This logic is currently evaluated before Required Parameters, so it is possible to ensure that requirements are met by some clever usage.
1040
+
1041
+ - This means that if a method is `:requires => [:active], :only_changed => {active: false}` then `active` will always have a value and would always pass the `:requires` directive (so you need to be very careful because the answer may end up being `nil` if you didn't specifically set it).
972
1042
 
973
1043
  ### HTTP/Parse Error Handling
974
1044
 
975
- Sometimes the backend server may respond with a non-200/304 header, in which case the code will raise an `Flexirest::HTTPClientException` for 4xx errors or an `Flexirest::HTTPServerException` for 5xx errors. These both have a `status` accessor and a `result` accessor (for getting access to the parsed body):
1045
+ Sometimes the backend server may respond with a non-200/304 header, in which case the code will raise an `Flexirest::HTTPClientException` for 4xx errors or an `Flexirest::HTTPServerException` for 5xx errors. These both have a `status` accessor and a `result` accessor (for getting access to the parsed body):
976
1046
 
977
1047
  ```ruby
978
1048
  begin
@@ -986,7 +1056,7 @@ If the response is unparsable (e.g. not in the desired content type), then it wi
986
1056
 
987
1057
  ### Validation
988
1058
 
989
- You can create validations on your objects just like Rails' built in ActiveModel validations. For example:
1059
+ You can create validations on your objects just like Rails' built in ActiveModel validations. For example:
990
1060
 
991
1061
  ```ruby
992
1062
  class Person < Flexirest::Base
@@ -1021,6 +1091,7 @@ validates :name, :presence, message: "must be given please"
1021
1091
  ```
1022
1092
 
1023
1093
  #### Permitting nil values
1094
+
1024
1095
  The default behavior for `:length`, `:numericality` and `:inclusion` validators is to fail when a `nil` value is encountered. You can prevent `nil` attribute values from triggering validation errors for attributes that may permit `nil` by adding the `:allow_nil => true` option. Adding this option with a `true` value to `:length`, `:numericality` and `:inclusion` validators will permit `nil` values and not trigger errors. Some examples are:
1025
1096
 
1026
1097
  ```ruby
@@ -1082,7 +1153,7 @@ class Person < Flexirest::Base
1082
1153
  end
1083
1154
  ```
1084
1155
 
1085
- By default verbose logging isn't enabled, so it's up to the developer to enable it (and remember to disable it afterwards). It does use debug level logging, so it shouldn't fill up a correctly configured production server anyway.
1156
+ By default verbose logging isn't enabled, so it's up to the developer to enable it (and remember to disable it afterwards). It does use debug level logging, so it shouldn't fill up a correctly configured production server anyway.
1086
1157
 
1087
1158
  If you prefer to record the output of an API call in a more automated fashion you can use a callback called `record_response` like this:
1088
1159
 
@@ -1098,7 +1169,7 @@ end
1098
1169
 
1099
1170
  ## XML Responses
1100
1171
 
1101
- Flexirest uses Crack to allow parsing of XML responses. For example, given an XML response of (with a content type of `application/xml` or `text/xml`):
1172
+ Flexirest uses Crack to allow parsing of XML responses. For example, given an XML response of (with a content type of `application/xml` or `text/xml`):
1102
1173
 
1103
1174
  ```xml
1104
1175
  <?xml version="1.0" encoding="utf-8"?>
data/flexirest.gemspec CHANGED
@@ -24,10 +24,11 @@ Gem::Specification.new do |spec|
24
24
  spec.add_development_dependency "rspec", "~> 3"
25
25
  if Gem::Version.new(RUBY_VERSION) < Gem::Version.new('2.1.0')
26
26
  spec.add_development_dependency "webmock", "~> 2.1.0"
27
+ spec.add_development_dependency "rspec_junit_formatter", "= 0.2.3"
27
28
  else
28
29
  spec.add_development_dependency "webmock"
30
+ spec.add_development_dependency "rspec_junit_formatter"
29
31
  end
30
- spec.add_development_dependency "rspec_junit_formatter"
31
32
  spec.add_development_dependency "simplecov"
32
33
  spec.add_development_dependency "simplecov-rcov"
33
34
  spec.add_development_dependency 'coveralls'
@@ -51,6 +51,19 @@ module Flexirest
51
51
  end
52
52
  end
53
53
 
54
+ def includes(*keys)
55
+ @_include_associations = keys
56
+ self
57
+ end
58
+
59
+ def _include_associations
60
+ @_include_associations
61
+ end
62
+
63
+ def _reset_include_associations!
64
+ @_include_associations = []
65
+ end
66
+
54
67
  def parse_date(*keys)
55
68
  keys.each { |key| @_date_fields << key }
56
69
  end
@@ -59,8 +72,15 @@ module Flexirest
59
72
  @_date_fields.uniq
60
73
  end
61
74
 
75
+ def _associations
76
+ @_associations
77
+ end
78
+
79
+
62
80
  def inherited(subclass)
63
81
  subclass.instance_variable_set(:@_date_fields, [])
82
+ subclass.instance_variable_set(:@_associations, {})
83
+ subclass.instance_variable_set(:@_include_associations, [])
64
84
  super
65
85
  end
66
86
  end
@@ -40,7 +40,7 @@ module Flexirest
40
40
  end
41
41
 
42
42
  def _copy_from(result)
43
- @attributes = result._attributes
43
+ @attributes = result._attributes
44
44
  @_status = result._status
45
45
  end
46
46