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 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: []