api_presenter 0.3.0 → 1.0.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
- SHA1:
3
- metadata.gz: 9e411385a8bd85df6bc3efe9f1ec418d5608f26f
4
- data.tar.gz: a7da14574b9aabd4501edafd1555feb16246ff05
2
+ SHA256:
3
+ metadata.gz: b9b45f72f53a0f0d6ddf34ba993ec0a6f5cbcd6fd7f2b8b2436d5963d2f50296
4
+ data.tar.gz: 48d1037e5b1af28c574b421996e9350e119fddc89024aeb7bbb7b059f9cec8e7
5
5
  SHA512:
6
- metadata.gz: 810bfdd0b1932949b0d6984ac1b0935467de3e5d27858f05890b906447aa2124fc0beee23c0cf95a5d624df168a2e20eb2b38d567650c685e291a905724859a4
7
- data.tar.gz: 29e3508ed61708ae44f7f4470e9687a0131d0e56a991b307ba1519ebd6b6755d92ff7903a26f7af6d25b6bd78d8f62b4a742332439d5b9b758f6bfc08e65f583
6
+ metadata.gz: 696d7eb80f9327f58f44ab02adfdc14da7a204fa4ae18ac360bde122f5953b55fc986cfa8489fa69c0f4372f3de1d525047512b3e4344b68e35fe3e1fb0f8c54
7
+ data.tar.gz: 1df07f9a061b26b111c00ce52b57a6f89911b914fd787e9e008254da9bf5d01c350ea29af57ea948dd5836b6e2723d979ab946053fd842fb973cbe6c574022e5
data/.travis.yml CHANGED
@@ -1,4 +1,4 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 2.2.2
4
- before_install: gem install bundler -v 1.10.3
4
+ before_install: gem install bundler -v 2.2.12
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
- A much longer readme is coming, including best practices and cautions, but in the meantime lets keep it simple...
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
- When creating RESTful APIs for web or mobile clients, there are a couple of desirable endpoint behaviors:
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
- * Include permissions so that the client can intelligently draw its UI (ex: edit/delete buttons), while maintaining a single source of truth
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 simple blog as the usage example for this gem. The blog has the following model structure:
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
- Generates a configuration file that allows you to override the default querystring params used by the `presenter` concern. More to come.
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 < ApiPresenter::Base
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 up to three methods:
87
+ Presenters can define three opt-in methods:
84
88
 
85
- * `associations_map` The business-dictated includable resources for the ActiveRecord model (`Post`, in this case). Consists of the model name as key and traversal required to preload/load them. In most cases, the value of `associations` will correspond directly to associations on the primary model.
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 records to preload in order to optimize policies that must traverse asscoiations.
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
- Your presentable collection can be an `ActiveRecord::Relation`, an array of records, or even a single record. Just call `present` on it from your controller action. The preloads will be performed, and the included collections/policies will be available in the `@presenter` instance variable.
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
- present post
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
- How you ultimately render the primary collection and the data produced by ApiPresenter is up to you. `@presenter` has the following properties:
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
- * `collection` The primary collection that was passed into the presenter.
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
- Here's an example of how you might render this using JBduiler:
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 insatnce, on user type.
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 < ApiPresenter::Base
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/[USERNAME]/api_presenter.
327
+ Bug reports and pull requests are welcome on GitHub at https://github.com/uberllama/api_presenter.
322
328
 
323
329
 
324
330
  ## License
@@ -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", "~> 1.10"
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"
@@ -14,8 +14,8 @@ module ApiPresenter
14
14
  #
15
15
  # @return [ApiPresenter::Base]
16
16
  #
17
- def self.call(**kwargs)
18
- new(kwargs).call
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.
@@ -55,6 +55,7 @@ module ApiPresenter
55
55
  def presenter_klass(klass)
56
56
  "#{klass.name}Presenter".safe_constantize ||
57
57
  "#{klass.base_class.name}Presenter".safe_constantize ||
58
+ "ApplicationApiPresenter".safe_constantize ||
58
59
  ApiPresenter::Base
59
60
  end
60
61
 
@@ -1,3 +1,3 @@
1
1
  module ApiPresenter
2
- VERSION = "0.3.0"
2
+ VERSION = "1.0.0"
3
3
  end
@@ -1 +1,8 @@
1
- Creates configuration file
1
+ Description:
2
+ Generates configuration filee.
3
+
4
+ Example:
5
+ rails generate api_presenter:config
6
+
7
+ This will create:
8
+ config/initializers/api_presenter.rb
@@ -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 'api_presenter_config.rb', 'config/initializers/api_presenter_config.rb'
7
+ copy_file('config.rb', 'config/initializers/api_presenter.rb')
8
8
  end
9
9
  end
10
10
  end
@@ -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
@@ -0,0 +1,2 @@
1
+ class ApplicationApiPresenter < ApiPresenter::Base
2
+ end
@@ -0,0 +1,13 @@
1
+ class <%= class_name %>Presenter < ApplicationApiPresenter
2
+ def associations_map
3
+ {}
4
+ end
5
+
6
+ def policy_methods
7
+ []
8
+ end
9
+
10
+ def policy_associations
11
+ []
12
+ end
13
+ 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.3.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: 2016-11-09 00:00:00.000000000 Z
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: '1.10'
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: '1.10'
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/api_presenter_config.rb
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
- rubyforge_project:
163
- rubygems_version: 2.4.6
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: []