flexirest 1.6.5 → 1.6.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +6 -0
- data/README.md +92 -1243
- data/docs/associations.md +181 -0
- data/docs/authentication.md +76 -0
- data/docs/automatic-conversion-of-fields-to-datedatetime.md +34 -0
- data/docs/basic-usage.md +103 -0
- data/docs/body-types.md +33 -0
- data/docs/caching.md +26 -0
- data/docs/combined-example.md +72 -0
- data/docs/debugging.md +32 -0
- data/docs/default-parameters.md +37 -0
- data/docs/faking-calls.md +22 -0
- data/docs/faraday-configuration.md +28 -0
- data/docs/filtering-result-lists.md +16 -0
- data/docs/httpparse-error-handling.md +17 -0
- data/{CONTRIBUTING.md → docs/internals.md} +4 -6
- data/docs/json-api.md +42 -0
- data/docs/lazy-loading.md +31 -0
- data/{Migrating-from-ActiveRestClient.md → docs/migrating-from-activerestclient.md} +2 -2
- data/docs/parallel-requests.md +28 -0
- data/docs/per-request-parameter-encoding.md +32 -0
- data/docs/per-request-timeouts.md +13 -0
- data/docs/plain-requests.md +30 -0
- data/docs/proxying-apis.md +86 -0
- data/docs/raw-requests.md +38 -0
- data/docs/required-parameters.md +17 -0
- data/docs/root-elements.md +34 -0
- data/{Ruby-on-Rails-Integration.md → docs/ruby-on-rails-integration.md} +16 -12
- data/docs/{Flexirest Internals.graffle → source/Flexirest Internals.graffle} +0 -0
- data/docs/{Flexirest Logo.sketch → source/Flexirest Logo.sketch} +0 -0
- data/docs/translating-apis.md +31 -0
- data/docs/updating-only-changed-dirty-attributes.md +37 -0
- data/docs/using-callbacks.md +114 -0
- data/docs/validation.md +89 -0
- data/docs/xml-responses.md +58 -0
- data/lib/flexirest/configuration.rb +36 -20
- data/lib/flexirest/request.rb +12 -4
- data/lib/flexirest/version.rb +1 -1
- data/spec/lib/request_spec.rb +28 -0
- metadata +35 -7
@@ -0,0 +1,17 @@
|
|
1
|
+
# *Flexirest:* HTTP/parse error handling
|
2
|
+
|
3
|
+
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):
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
begin
|
7
|
+
Person.all
|
8
|
+
rescue Flexirest::HTTPClientException, Flexirest::HTTPServerException => e
|
9
|
+
Rails.logger.error("API returned #{e.status} : #{e.result.message}")
|
10
|
+
end
|
11
|
+
```
|
12
|
+
|
13
|
+
If the response is unparsable (e.g. not in the desired content type), then it will raise an `Flexirest::ResponseParseException` which has a `status` accessor for the HTTP status code and a `body` accessor for the unparsed response body.
|
14
|
+
|
15
|
+
-----
|
16
|
+
|
17
|
+
[< Updating only changed/dirty attributes](updating-only-changed-dirty-attributes.md) | [Validation >](validation.md)
|
@@ -1,19 +1,17 @@
|
|
1
|
-
# Flexirest
|
1
|
+
# *Flexirest:* Internals of Flexirest
|
2
2
|
|
3
3
|
## Introduction
|
4
4
|
|
5
|
-
This project was built at Which? Ltd in the UK as ActiveRestClient, but was released as open source in 2014 under the MIT Licence. This is Andy Jeffries' fork of the project as the original seems not to be maintained by Which? and there's been no communication back about the project.
|
6
|
-
|
7
5
|
We're happy to receive contributions from the community for features and bugfixes and hopefully this guide helps new developers to the project to understand how to get started with the internals of Flexirest.
|
8
6
|
|
9
7
|
## Overview
|
10
8
|
|
11
|
-
![Component Overview Diagram](
|
9
|
+
![Component Overview Diagram](Flexirest%20Internals.png)
|
12
10
|
|
13
11
|
## Components
|
14
12
|
|
15
13
|
### Base
|
16
|
-
The main class in
|
14
|
+
The main class in Flexirest is `Flexirest::Base`. This includes a number of modules to provide a basic object ready to inherit from to form your own API classes.
|
17
15
|
|
18
16
|
**Configuration** includes all of the functionality for class and library level configuration (base_url, verbose logging, request format, etc).
|
19
17
|
|
@@ -33,7 +31,7 @@ This is a simple class that either uses a plain text file or Rails' logger if be
|
|
33
31
|
|
34
32
|
### Connection Manager/Connection
|
35
33
|
|
36
|
-
The principle is that
|
34
|
+
The principle is that Flexirest keeps a cached `Connection` to each unique API server and these are kept open using [persistent connections](https://en.wikipedia.org/wiki/HTTP_persistent_connection). The connection for a given `base_url` is created or retrieved from the pool by the `ConnectionManager`.
|
37
35
|
|
38
36
|
### Request
|
39
37
|
|
data/docs/json-api.md
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
# *Flexirest:* JSON API
|
2
|
+
|
3
|
+
If you are working with a [JSON API](http://jsonapi.org), you need to activate JSON API by specifying the `json_api` proxy:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Article < Flexirest::Base
|
7
|
+
proxy :json_api
|
8
|
+
end
|
9
|
+
```
|
10
|
+
|
11
|
+
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.
|
12
|
+
|
13
|
+
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.
|
14
|
+
|
15
|
+
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:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
# Makes a call to /articles with parameters: include=images
|
19
|
+
Article.includes(:images).all
|
20
|
+
|
21
|
+
# For nested resources, the include parameter becomes: include=images.tags,images.photographer
|
22
|
+
Article.includes(:images => [:tags, :photographer]).all
|
23
|
+
```
|
24
|
+
|
25
|
+
For POST and PATCH requests, the proxy formats a JSON API compliant 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`:
|
26
|
+
|
27
|
+
```ruby
|
28
|
+
class Photographer < Flexirest::Base
|
29
|
+
proxy :json_api
|
30
|
+
# Sets the type in the resource object to "people"
|
31
|
+
alias_type :people
|
32
|
+
|
33
|
+
patch :update, '/photographers/:id'
|
34
|
+
end
|
35
|
+
```
|
36
|
+
|
37
|
+
NB: Updating relationships is not yet supported.
|
38
|
+
|
39
|
+
|
40
|
+
-----
|
41
|
+
|
42
|
+
[< Plain requests](plain-requests.md) | [Proxying APIs >](proxying-apis.md)
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# *Flexirest:* Lazy loading
|
2
|
+
|
3
|
+
Flexirest supports lazy loading (delaying the actual API call until the response is actually used, so that views can be cached without still causing API calls).
|
4
|
+
|
5
|
+
**Note: Currently this isn't enabled by default, but this is likely to change in the future to make lazy loading the default.**
|
6
|
+
|
7
|
+
To enable it, simply call the lazy_load! method in your class definition:
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
class Article < Flexirest::Base
|
11
|
+
lazy_load!
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
If you have a ResultIterator that has multiple objects, each being lazy loaded or HAL linked resources that isn't loaded until it's used, you can actually parallelise the fetching of the items using code like this:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
items.parallelise(:id)
|
19
|
+
|
20
|
+
# or
|
21
|
+
|
22
|
+
items.parallelise do |item|
|
23
|
+
item.id
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
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.
|
28
|
+
|
29
|
+
-----
|
30
|
+
|
31
|
+
[< Using callbacks](using-callbacks.md) | [Authentication >](authentication.md)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
# Migrating from ActiveRestClient
|
1
|
+
# *Flexirest:* Migrating from ActiveRestClient
|
2
2
|
|
3
3
|
While contracting at Which? Ltd I wrote a library in Ruby to access REST APIs in a very flexible, almost ActiveRecord style. This was agreed (after long discussions with the legal department) to be released as open source.
|
4
4
|
|
@@ -30,4 +30,4 @@ The second step is to find and replace across your codebase all instances of `Ac
|
|
30
30
|
|
31
31
|
The third and final step is to clear your Rails cache. The easiest way of doing this is to type `Rails.cache.clear` in a Rails console.
|
32
32
|
|
33
|
-
That's it, you've now switched over to Flexirest with a)lots of bug fixes, b)support for PATCH requests and c)someone actively continuing to support it!
|
33
|
+
That's it, you've now switched over to Flexirest with a)lots of bug fixes, b)support for PATCH requests and c)someone actively continuing to support it!
|
@@ -0,0 +1,28 @@
|
|
1
|
+
# *Flexirest:* Parallel requests
|
2
|
+
|
3
|
+
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).
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
# Set adapter to Typhoeus to use parallel requests
|
7
|
+
Flexirest::Base.adapter = :typhoeus
|
8
|
+
```
|
9
|
+
|
10
|
+
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.
|
11
|
+
|
12
|
+
```ruby
|
13
|
+
Flexirest::ConnectionManager.in_parallel('https://www.example.com') do
|
14
|
+
@person = Person.find(1234)
|
15
|
+
@employers = Employer.all
|
16
|
+
|
17
|
+
puts @person #=> nil
|
18
|
+
puts @employers #=> nil
|
19
|
+
end # The requests are all fired in parallel during this end statement
|
20
|
+
|
21
|
+
puts @person.name #=> "John"
|
22
|
+
puts @employers.size #=> 7
|
23
|
+
```
|
24
|
+
|
25
|
+
|
26
|
+
-----
|
27
|
+
|
28
|
+
[< Body types](body-types.md) | [Faking calls >](faking-calls.md)
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# *Flexirest:* Per-request parameter encoding
|
2
|
+
|
3
|
+
When URL-encoding GET parameters, Rudy adds brackets(`[]`) by default to any parameters in an `Array`. For example, if you tried to pass these parameters:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
Person.all(param: [1, 2, 3])
|
7
|
+
```
|
8
|
+
|
9
|
+
Ruby would encode the URL as
|
10
|
+
|
11
|
+
```
|
12
|
+
?param[]=1¶m[]=2¶m[]=3
|
13
|
+
```
|
14
|
+
|
15
|
+
If you prefer flattened notation instead, pass a `params_encoder` option of `:flat` when mapping the call. So this call:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
class Person < Flexirest::Base
|
19
|
+
get :all, '/people', params_encoder: :flat
|
20
|
+
end
|
21
|
+
```
|
22
|
+
|
23
|
+
would output the following URL:
|
24
|
+
|
25
|
+
```
|
26
|
+
?param=1¶m=2¶m=3
|
27
|
+
```
|
28
|
+
|
29
|
+
|
30
|
+
-----
|
31
|
+
|
32
|
+
[< Per-request timeouts](per-request-timeouts.md) | [Automatic conversion of fields to Date/DateTime >](automatic-conversion-of-fields-to-datedatetime.md)
|
@@ -0,0 +1,13 @@
|
|
1
|
+
# *Flexirest:* Per-request timeouts
|
2
|
+
|
3
|
+
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).
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Person < Flexirest::Base
|
7
|
+
get :all, '/people', timeout: 5
|
8
|
+
end
|
9
|
+
```
|
10
|
+
|
11
|
+
-----
|
12
|
+
|
13
|
+
[< Faking calls](faking-calls.md) | [Per-request parameter encoding >](per-request-parameter-encoding.md)
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# *Flexirest:* Plain requests
|
2
|
+
|
3
|
+
If you are already using Flexirest but then want to simply call a normal URL and receive the resulting content as a string (i.e. not going through JSON parsing or instantiating in to a `Flexirest::Base` descendent) you can use code like this:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Person < Flexirest::Base
|
7
|
+
end
|
8
|
+
|
9
|
+
people = Person._plain_request('http://api.example.com/v1/people') # Defaults to get with no parameters
|
10
|
+
# people is a normal Flexirest object, implementing iteration, HAL loading, etc.
|
11
|
+
|
12
|
+
Person._plain_request('http://api.example.com/v1/people', :post, {id:1234,name:"John"}) # Post with parameters
|
13
|
+
```
|
14
|
+
|
15
|
+
The parameters are the same as for `_request`, but it does no parsing on the response
|
16
|
+
|
17
|
+
You can also bypass the response parsing using a mapped method like this:
|
18
|
+
|
19
|
+
```ruby
|
20
|
+
class Person < Flexirest::Base
|
21
|
+
get :all, "/v1/people", plain: true
|
22
|
+
end
|
23
|
+
```
|
24
|
+
|
25
|
+
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.
|
26
|
+
|
27
|
+
|
28
|
+
-----
|
29
|
+
|
30
|
+
[< Raw requests](raw-requests.md) | [JSON API >](json-api.md)
|
@@ -0,0 +1,86 @@
|
|
1
|
+
# *Flexirest:* Proxying APIs
|
2
|
+
|
3
|
+
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.
|
4
|
+
|
5
|
+
If it's simply that you want attribute names like `SomeName` or `someName` to be more Ruby-style `some_name` then you can simply do that by setting `:rubify_names` when mapping an API call.
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class Article < Flexirest::Base
|
9
|
+
base_url "http://www.example.com"
|
10
|
+
|
11
|
+
get :all, "/all", rubify_names: true
|
12
|
+
end
|
13
|
+
```
|
14
|
+
|
15
|
+
In more complex cases 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:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
class ArticleProxy < Flexirest::ProxyBase
|
19
|
+
get "/all" do
|
20
|
+
url "/all_people" # Equiv to url.gsub!("/all", "/all_people") if you wanted to keep params
|
21
|
+
response = passthrough
|
22
|
+
translate(response) do |body|
|
23
|
+
body["first_name"] = body.delete("fname")
|
24
|
+
body
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class Article < Flexirest::Base
|
30
|
+
proxy ArticleProxy
|
31
|
+
base_url "http://www.example.com"
|
32
|
+
|
33
|
+
get :all, "/all", fake:"{\"name\":\"Billy\"}"
|
34
|
+
get :list, "/list", fake:"[{\"name\":\"Billy\"}, {\"name\":\"John\"}]"
|
35
|
+
end
|
36
|
+
|
37
|
+
Article.all.first_name == "Billy"
|
38
|
+
```
|
39
|
+
|
40
|
+
This example does two things:
|
41
|
+
|
42
|
+
1. It rewrites the incoming URL for any requests matching "_/all_" to "/all_people"
|
43
|
+
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)
|
44
|
+
|
45
|
+
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.
|
46
|
+
|
47
|
+
You can use the `get_params` or `post_params` methods within your proxy block to amend/create/delete items from those request parameters, like this:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
get "/list" do
|
51
|
+
get_params["id"] = get_params.delete("identifier")
|
52
|
+
passthrough
|
53
|
+
end
|
54
|
+
```
|
55
|
+
|
56
|
+
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.
|
57
|
+
|
58
|
+
If you want to manually set the body for the API yourself you can use the `body` method
|
59
|
+
|
60
|
+
```ruby
|
61
|
+
put "/update" do
|
62
|
+
body "{\"id\":#{post_params["id"]}}"
|
63
|
+
passthrough
|
64
|
+
end
|
65
|
+
```
|
66
|
+
|
67
|
+
This example takes the `post_params["id"]` and converts the body from being a normal form-encoded body in to being a JSON body.
|
68
|
+
|
69
|
+
The proxy block expects one of three things to be the return value of the block.
|
70
|
+
|
71
|
+
1. The first options is that the call to `passthrough` is the last thing and it calls down to the connection layer and returns the actual response from the server in to the "API->Object" mapping layer ready for use in your application
|
72
|
+
2. The second option is to save the response from `passthrough` and use `translate` on it to alter the structure.
|
73
|
+
3. The third option is to use `render` if you want to completely fake an API and return the JSON yourself
|
74
|
+
|
75
|
+
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.
|
76
|
+
|
77
|
+
```ruby
|
78
|
+
put "/fake" do
|
79
|
+
render "{\"id\":1234}"
|
80
|
+
end
|
81
|
+
```
|
82
|
+
|
83
|
+
|
84
|
+
-----
|
85
|
+
|
86
|
+
[< JSON API](json-api.md) | [Translating APIs >](translating-apis.md)
|
@@ -0,0 +1,38 @@
|
|
1
|
+
# *Flexirest:* Raw requests
|
2
|
+
|
3
|
+
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:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Person < Flexirest::Base
|
7
|
+
end
|
8
|
+
|
9
|
+
people = Person._request('http://api.example.com/v1/people') # Defaults to get with no parameters
|
10
|
+
# people is a normal Flexirest object, implementing iteration, HAL loading, etc.
|
11
|
+
|
12
|
+
Person._request('http://api.example.com/v1/people', :post, {id:1234,name:"John"}) # Post with parameters
|
13
|
+
```
|
14
|
+
|
15
|
+
When you need to specify custom headers (for example for authentication) you can do this with a fourth option to the `_request` method. If you are using the default paramaters you'll need to specify them. For example:
|
16
|
+
|
17
|
+
```ruby
|
18
|
+
Person._request("http://api.example.com/v1/people", :get, {}, {headers:{"X-Something": "foo/bar"}})
|
19
|
+
```
|
20
|
+
|
21
|
+
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):
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
@person = Person._lazy_request(Person._request_for(:find, 1234))
|
25
|
+
```
|
26
|
+
|
27
|
+
This initially creates a `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:
|
28
|
+
|
29
|
+
```ruby
|
30
|
+
@person = Person.lazy_find(1234)
|
31
|
+
```
|
32
|
+
|
33
|
+
Doing this will try to find a literally mapped method called "lazy_find" and if it fails, it will try to use "find" but instantiate the object lazily.
|
34
|
+
|
35
|
+
|
36
|
+
-----
|
37
|
+
|
38
|
+
[< Automatic conversion of fields to Date/DateTime](automatic-conversion-of-fields-to-datedatetime.md) | [Plain requests >](plain-requests.md)
|
@@ -0,0 +1,17 @@
|
|
1
|
+
# *Flexirest:* Required parameters
|
2
|
+
|
3
|
+
If you want to specify that certain parameters are required for a specific call, you can specify them like:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Person < Flexirest::Base
|
7
|
+
get :all, '/people', :requires => [:active]
|
8
|
+
end
|
9
|
+
|
10
|
+
@people = Person.all # raises Flexirest::MissingParametersException
|
11
|
+
@people = Person.all(active:false)
|
12
|
+
```
|
13
|
+
|
14
|
+
|
15
|
+
-----
|
16
|
+
|
17
|
+
[< Root elements](root-elements.md) | [Updating only changed/dirty attributes >](updating-only-changed-dirty-attributes.md)
|
@@ -0,0 +1,34 @@
|
|
1
|
+
# *Flexirest:* Root elements
|
2
|
+
|
3
|
+
If your response comes back with a root node and you'd like to ignore it, you can define the mapping as:
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Feed < Flexirest::Base
|
7
|
+
post :list, "/feed", ignore_root: "feed"
|
8
|
+
end
|
9
|
+
```
|
10
|
+
|
11
|
+
Alternatively if you want to wrap your JSON request body in a root element, e.g.:
|
12
|
+
|
13
|
+
```json
|
14
|
+
{
|
15
|
+
"feed": {
|
16
|
+
"id": 1
|
17
|
+
}
|
18
|
+
}
|
19
|
+
```
|
20
|
+
|
21
|
+
You can do it like this:
|
22
|
+
|
23
|
+
```ruby
|
24
|
+
class Feed < Flexirest::Base
|
25
|
+
post :list, "/feed", wrap_root: "feed"
|
26
|
+
end
|
27
|
+
|
28
|
+
Feed.list(id: 1)
|
29
|
+
```
|
30
|
+
|
31
|
+
|
32
|
+
-----
|
33
|
+
|
34
|
+
[< Default parameters](default-parameters.md) | [Required parameters >](required-parameters.md)
|
@@ -1,7 +1,6 @@
|
|
1
|
-
# Ruby on Rails Integration
|
2
|
-
|
3
|
-
Flexirest works fine with Ruby on Rails Framework. This guide was tested with a Rails 4.2.x Application.
|
1
|
+
# *Flexirest:* Ruby on Rails Integration
|
4
2
|
|
3
|
+
The author built Flexirest to use within Ruby on Rails, but it doesn't require full Ruby on Rails. This guide was tested with a Rails 4.2.x Application but also works fine with Rails 5.x.
|
5
4
|
|
6
5
|
## Integration
|
7
6
|
|
@@ -15,9 +14,9 @@ gem 'flexirest'
|
|
15
14
|
|
16
15
|
## Configuration
|
17
16
|
|
18
|
-
It's possible to explicit specify the `base_url` in the
|
17
|
+
It's possible to explicit specify the `base_url` in the model class. If you have an common API Endpoint it makes sense to setup an initializer in `config/initializers` or use the `Rails.configuration.x` namespace.
|
19
18
|
|
20
|
-
This example use a custom file in `config/initializers` to setup the API endpoint. Either set a fixed URL or use environment variables if you would like to follow the [12factor](http://12factor.net/config) rules for preparing your application running on cloud infrastructure like
|
19
|
+
This example use a custom file in `config/initializers` to setup the API endpoint. Either set a fixed URL or use environment variables if you would like to follow the [12factor](http://12factor.net/config) rules for preparing your application running on cloud infrastructure like Heroku, Kubernetes or Docker Swarm.
|
21
20
|
|
22
21
|
```ruby
|
23
22
|
# config/initializers/flexirest.rb
|
@@ -41,17 +40,19 @@ end
|
|
41
40
|
|
42
41
|
## Model
|
43
42
|
|
44
|
-
The `ActiveModel` shortcuts will add support for `form_for` helper and `@person.errors` functionally in your views.
|
45
|
-
|
43
|
+
The `ActiveModel` shortcuts will add support for `form_for` helper and `@person.errors` functionally in your views. For example, if you have a scaffolded view structure this will just work out of the box.
|
44
|
+
|
45
|
+
Read more about `ActiveModel` here:
|
46
46
|
|
47
47
|
* [ActiveModel::Naming](http://api.rubyonrails.org/classes/ActiveModel/Naming.html)
|
48
48
|
* [ActiveModel::Conversion](http://api.rubyonrails.org/classes/ActiveModel/Conversion.html)
|
49
49
|
* [ActiveModel::Validations](http://api.rubyonrails.org/classes/ActiveModel/Validations.html)
|
50
50
|
|
51
51
|
In Rails, a resourceful route provides a mapping between HTTP verbs and URLs to controller actions.
|
52
|
+
|
52
53
|
Add the GET, POST, PATCH and DELETE methods that reflect to your endpoint.
|
53
54
|
|
54
|
-
Add the `persisted?` method to your Class to support Rails named_routes
|
55
|
+
Add the `persisted?` method to your Class to support Rails' `named_routes`, so you could use `edit_person_path(person)` without explicitly passing the `person.id`.
|
55
56
|
|
56
57
|
|
57
58
|
```ruby
|
@@ -78,11 +79,9 @@ end
|
|
78
79
|
|
79
80
|
## Controller
|
80
81
|
|
81
|
-
The Controller is structured like an standard RESTful Rails Controller. Only the `update` method has
|
82
|
-
a small change about how params getting processed.
|
82
|
+
The Controller is structured like an standard RESTful Rails Controller. Only the `update` method has a small change about how params getting processed.
|
83
83
|
|
84
|
-
Flexirest requires the `id` inside the params hash, this is not included by default.
|
85
|
-
Easily merge the current id into the params with `person_params.merge(id: @person.id)`
|
84
|
+
Flexirest requires the `id` inside the params hash, this is not included by default. Easily merge the current `id` into the parameters with `person_params.merge(id: @person.id)`
|
86
85
|
|
87
86
|
No other changes had to be made for the controller.
|
88
87
|
|
@@ -140,3 +139,8 @@ class PeopleController < ApplicationController
|
|
140
139
|
end
|
141
140
|
end
|
142
141
|
```
|
142
|
+
|
143
|
+
|
144
|
+
-----
|
145
|
+
|
146
|
+
[< Basic usage](basic-usage.md) | [Faraday configuration >](faraday-configuration.md)
|
File without changes
|
File without changes
|
@@ -0,0 +1,31 @@
|
|
1
|
+
# *Flexirest:* Translating APIS (DEPRECATED)
|
2
|
+
|
3
|
+
**IMPORTANT: This functionality has been deprecated in favour of the [Proxying APIs](proxying-apis.md) functionality. You should aim to remove this from your code as soon as possible.**
|
4
|
+
|
5
|
+
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:
|
6
|
+
|
7
|
+
```ruby
|
8
|
+
class ArticleTranslator
|
9
|
+
def self.all(object)
|
10
|
+
ret = {}
|
11
|
+
ret["first_name"] = object["name"]
|
12
|
+
ret
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Article < Flexirest::Base
|
17
|
+
translator ArticleTranslator
|
18
|
+
base_url "http://www.example.com"
|
19
|
+
|
20
|
+
get :all, "/all", fake:"{\"name\":\"Billy\"}"
|
21
|
+
get :list, "/list", fake:"[{\"name\":\"Billy\"}, {\"name\":\"John\"}]"
|
22
|
+
end
|
23
|
+
|
24
|
+
Article.all.first_name == "Billy"
|
25
|
+
```
|
26
|
+
|
27
|
+
|
28
|
+
|
29
|
+
-----
|
30
|
+
|
31
|
+
[< Proxying APIs](proxying-apis.md) | [Default parameters >](default-parameters.md)
|
@@ -0,0 +1,37 @@
|
|
1
|
+
# *Flexirest:* Updating only changed/dirty attributes
|
2
|
+
|
3
|
+
The most common RESTful usage of the PATCH http-method is to only send fields that have changed. The default action for all calls is to send all known object attributes for POST/PUT/PATCH calls, but this can be altered by setting the `only_changed` option on your call declaration.
|
4
|
+
|
5
|
+
```ruby
|
6
|
+
class Person < Flexirest::Base
|
7
|
+
get :all, '/people'
|
8
|
+
patch :update, '/people/:id', :only_changed => true # only send attributes that are changed/dirty
|
9
|
+
end
|
10
|
+
|
11
|
+
person = Person.all.first
|
12
|
+
person.first_name = 'Billy'
|
13
|
+
person.update # performs a PATCH request, sending only the now changed 'first_name' attribute
|
14
|
+
```
|
15
|
+
|
16
|
+
This functionality is per-call, and there is some additional flexibility to control which attributes are sent and when.
|
17
|
+
|
18
|
+
```ruby
|
19
|
+
class Person < Flexirest::Base
|
20
|
+
get :all, '/people'
|
21
|
+
patch :update_1, '/people/:id', :only_changed => true # only send attributes that are changed/dirty (all known attributes on this object are subject to evaluation)
|
22
|
+
patch :update_2, "/people/:id", :only_changed => [:first_name, :last_name, :dob] # only send these listed attributes, and only if they are changed/dirty
|
23
|
+
patch :update_3, "/people/:id", :only_changed => {first_name: true, last_name: true, dob: false} # include the listed attributes marked 'true' only when changed; attributes marked 'false' are always included (changed or not, and if not present will be sent as nil); unspecified attributes are never sent
|
24
|
+
end
|
25
|
+
```
|
26
|
+
|
27
|
+
#### Additional Notes:
|
28
|
+
|
29
|
+
- 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).
|
30
|
+
- This logic is currently evaluated before Required Parameters, so it is possible to ensure that requirements are met by some clever usage.
|
31
|
+
|
32
|
+
- 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).
|
33
|
+
|
34
|
+
|
35
|
+
-----
|
36
|
+
|
37
|
+
[< Required parameters](required-parameters.md) | [HTTP/parse error handling >](httpparse-error-handling.md)
|