api_presenter 0.3.0 → 1.0.0
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.
- checksums.yaml +5 -5
- data/.travis.yml +1 -1
- data/CHANGELOG.md +12 -0
- data/README.md +43 -37
- data/api_presenter.gemspec +1 -1
- data/lib/api_presenter/base.rb +2 -2
- data/lib/api_presenter/concerns/presentable.rb +1 -0
- data/lib/api_presenter/version.rb +1 -1
- data/lib/generators/api_presenter/config/USAGE +8 -1
- data/lib/generators/api_presenter/config/config_generator.rb +1 -1
- data/lib/generators/api_presenter/config/templates/{api_presenter_config.rb → config.rb} +0 -0
- data/lib/generators/api_presenter/presenter/USAGE +9 -0
- data/lib/generators/api_presenter/presenter/presenter_generator.rb +17 -0
- data/lib/generators/api_presenter/presenter/templates/application_presenter.rb +2 -0
- data/lib/generators/api_presenter/presenter/templates/presenter.rb +13 -0
- metadata +13 -10
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
|
-
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: b9b45f72f53a0f0d6ddf34ba993ec0a6f5cbcd6fd7f2b8b2436d5963d2f50296
|
|
4
|
+
data.tar.gz: 48d1037e5b1af28c574b421996e9350e119fddc89024aeb7bbb7b059f9cec8e7
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 696d7eb80f9327f58f44ab02adfdc14da7a204fa4ae18ac360bde122f5953b55fc986cfa8489fa69c0f4372f3de1d525047512b3e4344b68e35fe3e1fb0f8c54
|
|
7
|
+
data.tar.gz: 1df07f9a061b26b111c00ce52b57a6f89911b914fd787e9e008254da9bf5d01c350ea29af57ea948dd5836b6e2723d979ab946053fd842fb973cbe6c574022e5
|
data/.travis.yml
CHANGED
data/CHANGELOG.md
CHANGED
|
@@ -1,3 +1,15 @@
|
|
|
1
|
+
## 0.4.1 (2016-11-10)
|
|
2
|
+
|
|
3
|
+
Bug fix: include `ApplicationApiPresenter` in presenter lookup path.
|
|
4
|
+
|
|
5
|
+
## 0.4.0 (2016-11-10)
|
|
6
|
+
|
|
7
|
+
Adds presenter generator.
|
|
8
|
+
|
|
9
|
+
## 0.3.0 (2016-11-09)
|
|
10
|
+
|
|
11
|
+
Adds configuration options and generator.
|
|
12
|
+
|
|
1
13
|
## 0.2.4 (2016-11-07)
|
|
2
14
|
|
|
3
15
|
Logical restructure. No API changes.
|
data/README.md
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
# ApiPresenter
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
REST APIs provide a concise and conventional means of retrieving resources for a client. But in the real world, clients often have additional data requirements beyond the specifically requested resource(s):
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
1. Current user permissions for the returned records, so that the client can intelligently draw its UI (ex: edit/delete buttons).
|
|
6
|
+
2. Associated data, to mitigate total number of requests (ex: return authors with posts).
|
|
6
7
|
|
|
7
|
-
|
|
8
|
-
* Allow inclusion of associated data to mitigate total number of requests
|
|
9
|
-
|
|
10
|
-
ApiPresenter does both of these things, plus a bit more.
|
|
8
|
+
ApiPresenter provides both of these things, plus a bit more.
|
|
11
9
|
|
|
12
10
|
## Installation
|
|
13
11
|
|
|
@@ -27,7 +25,7 @@ Or install it yourself as:
|
|
|
27
25
|
|
|
28
26
|
## Usage
|
|
29
27
|
|
|
30
|
-
We'll use a
|
|
28
|
+
ApiPresenter is well suited to large, relational systems. We'll use a blog as the usage example for this gem. The blog has the following model structure:
|
|
31
29
|
|
|
32
30
|
```ruby
|
|
33
31
|
class Category < ActiveRecord::Base
|
|
@@ -52,16 +50,22 @@ class User < ActiveRecord::Base
|
|
|
52
50
|
end
|
|
53
51
|
```
|
|
54
52
|
|
|
53
|
+
Usage examples will be in the context of requesting posts as the primary collection.
|
|
54
|
+
|
|
55
55
|
### 0. Generate config file
|
|
56
56
|
|
|
57
57
|
`rails g api_presenter:config`
|
|
58
58
|
|
|
59
|
-
|
|
59
|
+
Generate your configuration file. Currently, ApiPresenter allows customization of querystring parameter names for including policies and associated resources (see below). More configuration options to come.
|
|
60
60
|
|
|
61
61
|
### 1. Create your Presenter
|
|
62
62
|
|
|
63
|
+
Generate a presenter class for your ActiveRecord model. The generator will also ensure the presence of an `ApplicationApiPresenter` base class for centralized methods.
|
|
64
|
+
|
|
65
|
+
`rails g api_presenter:presenter post`
|
|
66
|
+
|
|
63
67
|
```ruby
|
|
64
|
-
class PostPresenter <
|
|
68
|
+
class PostPresenter < ApplicationApiPresenter
|
|
65
69
|
def associations_map
|
|
66
70
|
{
|
|
67
71
|
categories: { associations: { sub_category: :category } },
|
|
@@ -80,21 +84,15 @@ class PostPresenter < ApiPresenter::Base
|
|
|
80
84
|
end
|
|
81
85
|
```
|
|
82
86
|
|
|
83
|
-
Presenters can define
|
|
87
|
+
Presenters can define three opt-in methods:
|
|
84
88
|
|
|
85
|
-
* `associations_map`
|
|
89
|
+
* `associations_map` Associated resources that you would like to be includable with the primary collection. Consists of the model name as key and the traversal required to preload/load them. In most cases, the value of `associations` will correspond directly to associations on the primary model.
|
|
86
90
|
* `policy_methods` A list of Pundit policy methods to resolve for the primary collection if policies are requested.
|
|
87
|
-
* `policy_associations` Additional
|
|
91
|
+
* `policy_associations` Additional associations to preload in order to optimize policies that must traverse asscoiations.
|
|
88
92
|
|
|
89
93
|
### 2. Enable your controllers
|
|
90
94
|
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
The following querystring params are used by the supplied controller concern's `present` method:
|
|
94
|
-
|
|
95
|
-
* `count [Boolean]` Pass true if you just want a count of the primary collection
|
|
96
|
-
* `policies [Boolean]` Pass true if you want to resolve policies for the primary collection records
|
|
97
|
-
* `include [String, Array]` A comma-delimited list or array of collection names (camelCase or under_scored) to include with the primary collection
|
|
95
|
+
Include the supplied controller concern at your `ApplicationController` level, or on a specific controller. This concern provides the `present` method, which can be called on an `ActiveRecord::Relation`, an array of records, or even a single record (preloading of associated collections is only performed for relations).
|
|
98
96
|
|
|
99
97
|
```ruby
|
|
100
98
|
class ApplicationController
|
|
@@ -107,6 +105,7 @@ class PostsController < ApplicationController
|
|
|
107
105
|
# GET /posts?include=categories,subCategories,users&policies=true
|
|
108
106
|
#
|
|
109
107
|
def index
|
|
108
|
+
authorize Post
|
|
110
109
|
posts = PostQuery.records(current_user, params)
|
|
111
110
|
present posts
|
|
112
111
|
end
|
|
@@ -115,23 +114,32 @@ class PostsController < ApplicationController
|
|
|
115
114
|
# GET /posts/:id?include=categories,subCategories,users&policies=true
|
|
116
115
|
#
|
|
117
116
|
def show
|
|
118
|
-
post = Post.find(params[:id])
|
|
119
|
-
|
|
117
|
+
@post = Post.find(params[:id])
|
|
118
|
+
authorize @post
|
|
119
|
+
present @post
|
|
120
120
|
end
|
|
121
121
|
end
|
|
122
122
|
```
|
|
123
123
|
|
|
124
|
+
Controller params are used to tell the presenter what to load. The default param keys are `count`, `policies`, and `include`:
|
|
125
|
+
|
|
126
|
+
* `count [Boolean]` Pass true if you just want a count of the primary collection.
|
|
127
|
+
* `policies [Boolean]` Pass true if you want to resolve policies for the primary collection.
|
|
128
|
+
* `include [String, Array]` A comma-delimited list or array of collection names (camelCase or under_scored) to include with the primary collection.
|
|
129
|
+
|
|
124
130
|
### 3. Render the result
|
|
125
131
|
|
|
126
|
-
|
|
132
|
+
After calling the `present` method in a controller action, you access your processed collection through the `@presenter` instance variable. How you ultimately render the data produced by ApiPresenter is up to you.
|
|
127
133
|
|
|
128
|
-
|
|
129
|
-
* `total_count` When using Kaminari or another pagination method that defines a `total_count` property, returns unpaginated count. If the primary collection is not an `ActiveRecord::Relation`, simply returns the number of records.
|
|
130
|
-
* `included_collection_names` Convenience method that returns an array of included collecton model names.
|
|
131
|
-
* `included_collections` A hash of included collections, consisting of the model name and corresponding records.
|
|
132
|
-
* `policies` An array of resolved policies for the primary collection.
|
|
134
|
+
`@presenter` has the following properties:
|
|
133
135
|
|
|
134
|
-
|
|
136
|
+
* `collection [Array<ActiveRecord::Base>]` The primary collection that was passed into the presenter. Empty if count requested.
|
|
137
|
+
* `total_count [Integer]` When using Kaminari or another pagination method that defines a `total_count` property, returns unpaginated count. If the primary collection is not an `ActiveRecord::Relation`, simply returns the number of records.
|
|
138
|
+
* `included_collection_names [Array<Symbol>]` Convenience method that returns an array of included collection model names.
|
|
139
|
+
* `included_collections [Hash]` A hash of included collections, consisting of the model name and corresponding records.
|
|
140
|
+
* `policies [Array<Hash>]` An array of resolved policies for the primary collection.
|
|
141
|
+
|
|
142
|
+
Here's an example of how you might render your data using JBduiler:
|
|
135
143
|
|
|
136
144
|
### api/posts/index.json.jbuilder
|
|
137
145
|
|
|
@@ -168,7 +176,7 @@ end
|
|
|
168
176
|
|
|
169
177
|
### 4. Output
|
|
170
178
|
|
|
171
|
-
Using the code above, our call to `GET /posts` would result in the following JSON:
|
|
179
|
+
Using the code above, our call to `GET /posts?include=categories,subCategories,users&policies=true` would result in the following JSON:
|
|
172
180
|
|
|
173
181
|
```json
|
|
174
182
|
{
|
|
@@ -198,7 +206,7 @@ Using the code above, our call to `GET /posts` would result in the following JSO
|
|
|
198
206
|
}
|
|
199
207
|
```
|
|
200
208
|
|
|
201
|
-
And similarily, for `GET /posts/1`:
|
|
209
|
+
And similarily, for `GET /posts/1?include=categories,subCategories,users&policies=true`:
|
|
202
210
|
|
|
203
211
|
```json
|
|
204
212
|
{
|
|
@@ -226,12 +234,12 @@ And similarily, for `GET /posts/1`:
|
|
|
226
234
|
|
|
227
235
|
### Conditional includes
|
|
228
236
|
|
|
229
|
-
There are a number of ways you can conditionally include resources, depending, for
|
|
237
|
+
There are a number of ways you can conditionally include resources, depending, for instance, on user type.
|
|
230
238
|
|
|
231
239
|
#### Add conditions inside `associations_map` method
|
|
232
240
|
|
|
233
241
|
```ruby
|
|
234
|
-
class PostPresenter <
|
|
242
|
+
class PostPresenter < ApiApplicationPresenter
|
|
235
243
|
def associations_map
|
|
236
244
|
current_user.admin? ? admin_associations_map : user_associations_map
|
|
237
245
|
end
|
|
@@ -257,7 +265,7 @@ end
|
|
|
257
265
|
|
|
258
266
|
#### Use `condition` property within `association_map` definition
|
|
259
267
|
|
|
260
|
-
Via inline string
|
|
268
|
+
##### Via inline string
|
|
261
269
|
|
|
262
270
|
```ruby
|
|
263
271
|
class PostPresenter < ApiPresenter::Base
|
|
@@ -271,7 +279,7 @@ class PostPresenter < ApiPresenter::Base
|
|
|
271
279
|
end
|
|
272
280
|
```
|
|
273
281
|
|
|
274
|
-
Via method call
|
|
282
|
+
##### Via method call
|
|
275
283
|
|
|
276
284
|
```ruby
|
|
277
285
|
class PostPresenter < ApiPresenter::Base
|
|
@@ -303,12 +311,10 @@ end
|
|
|
303
311
|
|
|
304
312
|
## TODO
|
|
305
313
|
|
|
306
|
-
* More doc
|
|
307
314
|
* Decouple from Pundit
|
|
308
315
|
* Make index policy checking on includes optional
|
|
309
316
|
* Allow custom collection names
|
|
310
317
|
* Add test helper to assert presenter was called for a given controller action
|
|
311
|
-
* Add presenter generator
|
|
312
318
|
|
|
313
319
|
## Development
|
|
314
320
|
|
|
@@ -318,7 +324,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
|
|
|
318
324
|
|
|
319
325
|
## Contributing
|
|
320
326
|
|
|
321
|
-
Bug reports and pull requests are welcome on GitHub at https://github.com/
|
|
327
|
+
Bug reports and pull requests are welcome on GitHub at https://github.com/uberllama/api_presenter.
|
|
322
328
|
|
|
323
329
|
|
|
324
330
|
## License
|
data/api_presenter.gemspec
CHANGED
|
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
|
19
19
|
spec.add_dependency "activesupport", ">= 3.0.0"
|
|
20
20
|
spec.add_dependency "pundit"
|
|
21
21
|
spec.add_development_dependency "activerecord", ">= 4.2.3"
|
|
22
|
-
spec.add_development_dependency "bundler", "~>
|
|
22
|
+
spec.add_development_dependency "bundler", "~> 2.2.12"
|
|
23
23
|
spec.add_development_dependency "rake", "~> 10.0"
|
|
24
24
|
spec.add_development_dependency "rspec"
|
|
25
25
|
spec.add_development_dependency "sqlite3"
|
data/lib/api_presenter/base.rb
CHANGED
|
@@ -14,8 +14,8 @@ module ApiPresenter
|
|
|
14
14
|
#
|
|
15
15
|
# @return [ApiPresenter::Base]
|
|
16
16
|
#
|
|
17
|
-
def self.call(
|
|
18
|
-
new(
|
|
17
|
+
def self.call(current_user: nil, relation:, params: {})
|
|
18
|
+
new(current_user: current_user, relation: relation, params: params).call
|
|
19
19
|
end
|
|
20
20
|
|
|
21
21
|
# @param current_user [User] Optional. current_user context.
|
|
@@ -4,7 +4,7 @@ module ApiPresenter
|
|
|
4
4
|
source_root File.expand_path('../templates', __FILE__)
|
|
5
5
|
|
|
6
6
|
def copy_config_file
|
|
7
|
-
copy_file
|
|
7
|
+
copy_file('config.rb', 'config/initializers/api_presenter.rb')
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
end
|
|
File without changes
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
Description:
|
|
2
|
+
Generates an API presenter for an ActiveRecord model with the given name, and a base class ApplicationApiPresenter unless already exists.
|
|
3
|
+
|
|
4
|
+
Example:
|
|
5
|
+
rails generate api_presenter:presenter post
|
|
6
|
+
|
|
7
|
+
This will create:
|
|
8
|
+
app/presenters/application_api_presenter.rb
|
|
9
|
+
app/presenters/post_presenter.rb
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
module ApiPresenter
|
|
2
|
+
module Generators
|
|
3
|
+
class PresenterGenerator < ::Rails::Generators::NamedBase
|
|
4
|
+
source_root File.expand_path('../templates', __FILE__)
|
|
5
|
+
|
|
6
|
+
def create_application_presenter
|
|
7
|
+
unless File.exist?('app/presenters/application_api_presenter.rb')
|
|
8
|
+
copy_file('application_presenter.rb', 'app/presenters/application_api_presenter.rb')
|
|
9
|
+
end
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def create_presenter
|
|
13
|
+
template('presenter.rb', File.join('app/presenters', class_path, "#{file_name}_presenter.rb"))
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
metadata
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: api_presenter
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 1.0.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Yuval Kordov
|
|
8
8
|
- Little Blimp
|
|
9
|
-
autorequire:
|
|
9
|
+
autorequire:
|
|
10
10
|
bindir: exe
|
|
11
11
|
cert_chain: []
|
|
12
|
-
date:
|
|
12
|
+
date: 2022-06-14 00:00:00.000000000 Z
|
|
13
13
|
dependencies:
|
|
14
14
|
- !ruby/object:Gem::Dependency
|
|
15
15
|
name: activesupport
|
|
@@ -59,14 +59,14 @@ dependencies:
|
|
|
59
59
|
requirements:
|
|
60
60
|
- - "~>"
|
|
61
61
|
- !ruby/object:Gem::Version
|
|
62
|
-
version:
|
|
62
|
+
version: 2.2.12
|
|
63
63
|
type: :development
|
|
64
64
|
prerelease: false
|
|
65
65
|
version_requirements: !ruby/object:Gem::Requirement
|
|
66
66
|
requirements:
|
|
67
67
|
- - "~>"
|
|
68
68
|
- !ruby/object:Gem::Version
|
|
69
|
-
version:
|
|
69
|
+
version: 2.2.12
|
|
70
70
|
- !ruby/object:Gem::Dependency
|
|
71
71
|
name: rake
|
|
72
72
|
requirement: !ruby/object:Gem::Requirement
|
|
@@ -139,12 +139,16 @@ files:
|
|
|
139
139
|
- lib/api_presenter/version.rb
|
|
140
140
|
- lib/generators/api_presenter/config/USAGE
|
|
141
141
|
- lib/generators/api_presenter/config/config_generator.rb
|
|
142
|
-
- lib/generators/api_presenter/config/templates/
|
|
142
|
+
- lib/generators/api_presenter/config/templates/config.rb
|
|
143
|
+
- lib/generators/api_presenter/presenter/USAGE
|
|
144
|
+
- lib/generators/api_presenter/presenter/presenter_generator.rb
|
|
145
|
+
- lib/generators/api_presenter/presenter/templates/application_presenter.rb
|
|
146
|
+
- lib/generators/api_presenter/presenter/templates/presenter.rb
|
|
143
147
|
homepage: http://github.com/uberllama/api_presenter
|
|
144
148
|
licenses:
|
|
145
149
|
- MIT
|
|
146
150
|
metadata: {}
|
|
147
|
-
post_install_message:
|
|
151
|
+
post_install_message:
|
|
148
152
|
rdoc_options: []
|
|
149
153
|
require_paths:
|
|
150
154
|
- lib
|
|
@@ -159,9 +163,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
159
163
|
- !ruby/object:Gem::Version
|
|
160
164
|
version: '0'
|
|
161
165
|
requirements: []
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
signing_key:
|
|
166
|
+
rubygems_version: 3.0.9
|
|
167
|
+
signing_key:
|
|
165
168
|
specification_version: 4
|
|
166
169
|
summary: Return associations and policies with API responses
|
|
167
170
|
test_files: []
|