api_presenter 0.2.4 → 0.3.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
2
  SHA1:
3
- metadata.gz: 2a3e0a34d5d50f902760195ffd58a409d3231a15
4
- data.tar.gz: 8a4261472f7ba030c7ff72e2bed5d9c3171b98d6
3
+ metadata.gz: 9e411385a8bd85df6bc3efe9f1ec418d5608f26f
4
+ data.tar.gz: a7da14574b9aabd4501edafd1555feb16246ff05
5
5
  SHA512:
6
- metadata.gz: 3e3491e2c26ac8affb10ba11eaeef277be47b4b9666f205a6c9f5a07da5178b204ffcc28961e10df10811abb6a98ff5cc0e0968e0d213911f07f96d1094a94fa
7
- data.tar.gz: e426cce2a716fc7cdcb4e5066cd9b9a51509a5c339886031c27159fe0a6698c07f08f107f939cd6c0a071637bd7f4ee942f61e2876d139757f42383e157ed367
6
+ metadata.gz: 810bfdd0b1932949b0d6984ac1b0935467de3e5d27858f05890b906447aa2124fc0beee23c0cf95a5d624df168a2e20eb2b38d567650c685e291a905724859a4
7
+ data.tar.gz: 29e3508ed61708ae44f7f4470e9687a0131d0e56a991b307ba1519ebd6b6755d92ff7903a26f7af6d25b6bd78d8f62b4a742332439d5b9b758f6bfc08e65f583
data/README.md CHANGED
@@ -2,10 +2,10 @@
2
2
 
3
3
  A much longer readme is coming, including best practices and cautions, but in the meantime lets keep it simple...
4
4
 
5
- When creating RESTful APIs for web or mobile clients, there are a couple of common use cases that have emerged:
5
+ When creating RESTful APIs for web or mobile clients, there are a couple of desirable endpoint behaviors:
6
6
 
7
- * Allow inclusion of associated data to mitigate number of requests
8
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
9
 
10
10
  ApiPresenter does both of these things, plus a bit more.
11
11
 
@@ -52,7 +52,11 @@ class User < ActiveRecord::Base
52
52
  end
53
53
  ```
54
54
 
55
- When clients request posts (the primary collection), they may want any or all of the above data for those posts.
55
+ ### 0. Generate config file
56
+
57
+ `rails g api_presenter:config`
58
+
59
+ Generates a configuration file that allows you to override the default querystring params used by the `presenter` concern. More to come.
56
60
 
57
61
  ### 1. Create your Presenter
58
62
 
@@ -78,13 +82,19 @@ end
78
82
 
79
83
  Presenters can define up to three methods:
80
84
 
81
- * `associations_map` The 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.
82
- * `policy_methods` A list of Pundit policy methods to resolve for the primary collection.
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.
86
+ * `policy_methods` A list of Pundit policy methods to resolve for the primary collection if policies are requested.
83
87
  * `policy_associations` Additional records to preload in order to optimize policies that must traverse asscoiations.
84
88
 
85
89
  ### 2. Enable your controllers
86
90
 
87
- Your presentable collection can be an `ActiveRecord::Relation`, an array of records, or even a single record. Just call `present` on it. The preloads will be performed, and the included collections/policies will be available in the `@presenter` instance variable.
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
88
98
 
89
99
  ```ruby
90
100
  class ApplicationController
@@ -92,11 +102,18 @@ class ApplicationController
92
102
  end
93
103
 
94
104
  class PostsController < ApplicationController
105
+
106
+ # @example
107
+ # GET /posts?include=categories,subCategories,users&policies=true
108
+ #
95
109
  def index
96
110
  posts = PostQuery.records(current_user, params)
97
111
  present posts
98
112
  end
99
113
 
114
+ # @example
115
+ # GET /posts/:id?include=categories,subCategories,users&policies=true
116
+ #
100
117
  def show
101
118
  post = Post.find(params[:id])
102
119
  present post
@@ -134,13 +151,6 @@ end
134
151
  json.partial!("api/shared/included_collections_and_meta", presenter: @presenter)
135
152
  ```
136
153
 
137
- ```ruby
138
- json.posts(@presenter.collection) do |post|
139
- json.partial!(post)
140
- end
141
- json.partial!("api/shared/included_collections_and_meta", presenter: @presenter)
142
- ```
143
-
144
154
  ### api/shared/included_collections_and_meta
145
155
 
146
156
  ```ruby
@@ -156,6 +166,62 @@ json.meta do
156
166
  end
157
167
  ```
158
168
 
169
+ ### 4. Output
170
+
171
+ Using the code above, our call to `GET /posts` would result in the following JSON:
172
+
173
+ ```json
174
+ {
175
+ "posts": [
176
+ { "id": 1, "sub_category": 1, "creator_id": 1, "publisher_id": 2, "body": "Lorem dim sum", "published": true },
177
+ { "id": 2, "sub_category": 2, "creator_id": 3, "publisher_id": null, "body": "Lorem dim sum", "published": false }
178
+ ],
179
+ "categories": [
180
+ { "id": 1, "name": "Animals" }
181
+ ],
182
+ "sub_categories": [
183
+ { "id": 1, "category_id": 1, "name": "Lemurs" },
184
+ { "id": 2, "category_id": 1, "name": "Anteaters" }
185
+ ],
186
+ "users": [
187
+ { "id": 1, "name": "Dora" },
188
+ { "id": 2, "name": "Boots" },
189
+ { "id": 3, "name": "Backpack" }
190
+ ],
191
+ "meta": {
192
+ "total_count": 2,
193
+ "policies": [
194
+ { "post_id": 1, "update": true, "destroy": false },
195
+ { "post_id": 2, "update": true, "destroy": true }
196
+ ]
197
+ }
198
+ }
199
+ ```
200
+
201
+ And similarily, for `GET /posts/1`:
202
+
203
+ ```json
204
+ {
205
+ "post": { "id": 1, "sub_category": 1, "creator_id": 1, "publisher_id": 2, "body": "Lorem dim sum", "published": true },
206
+ "categories": [
207
+ { "id": 1, "name": "Animals" }
208
+ ],
209
+ "sub_categories": [
210
+ { "id": 1, "category_id": 1, "name": "Lemurs" }
211
+ ],
212
+ "users": [
213
+ { "id": 1, "name": "Dora" },
214
+ { "id": 2, "name": "Boots" }
215
+ ],
216
+ "meta": {
217
+ "total_count": 1,
218
+ "policies": [
219
+ { "post_id": 1, "update": true, "destroy": false }
220
+ ]
221
+ }
222
+ }
223
+ ```
224
+
159
225
  ## Advanced Usage
160
226
 
161
227
  ### Conditional includes
@@ -242,6 +308,7 @@ end
242
308
  * Make index policy checking on includes optional
243
309
  * Allow custom collection names
244
310
  * Add test helper to assert presenter was called for a given controller action
311
+ * Add presenter generator
245
312
 
246
313
  ## Development
247
314
 
data/lib/api_presenter.rb CHANGED
@@ -1,7 +1,9 @@
1
1
  require 'pundit'
2
+
2
3
  module ApiPresenter; end
3
4
 
4
5
  require 'api_presenter/base'
6
+ require 'api_presenter/configuration'
5
7
  require 'api_presenter/concerns/presentable'
6
8
  require 'api_presenter/parsers/parse_include_params'
7
9
  require 'api_presenter/resolvers/base'
@@ -80,7 +80,7 @@ module ApiPresenter
80
80
  # @return [Array<Symbol>]
81
81
  #
82
82
  def included_collection_names
83
- @included_collection_names ||= Parsers::ParseIncludeParams.call(params[:include])
83
+ @included_collection_names ||= Parsers::ParseIncludeParams.call(params[ApiPresenter.configuration.include_param])
84
84
  end
85
85
 
86
86
  # Map of included collection names and loaded record
@@ -172,11 +172,11 @@ module ApiPresenter
172
172
  private
173
173
 
174
174
  def count_only?
175
- @count_only ||= !!params[:count]
175
+ @count_only ||= !!params[ApiPresenter.configuration.count_param]
176
176
  end
177
177
 
178
178
  def resolve_policies?
179
- @resolve_policies ||= current_user && !!params[:policies]
179
+ @resolve_policies ||= current_user && !!params[ApiPresenter.configuration.policies_param]
180
180
  end
181
181
 
182
182
  def resolve_included_collctions?
@@ -0,0 +1,43 @@
1
+ module ApiPresenter
2
+ class Configuration
3
+ # Querystring param key that determines whether or not to just produce a total count
4
+ #
5
+ # @return [Symbol]
6
+ #
7
+ attr_accessor :count_param
8
+
9
+ # Querystring param key containing the included collection names
10
+ #
11
+ # @return [Symbol]
12
+ #
13
+ attr_accessor :include_param
14
+
15
+ # Querystring param key that determines whether or not to resolve policies for primary collection
16
+ #
17
+ # @return [Symbol]
18
+ #
19
+ attr_accessor :policies_param
20
+
21
+ def initialize
22
+ @count_param = :count
23
+ @include_param = :include
24
+ @policies_param = :policies
25
+ end
26
+ end
27
+
28
+ # @return [ApiPresenter::Configuration] ApiPresenter's current configuration
29
+ def self.configuration
30
+ @configuration ||= Configuration.new
31
+ end
32
+
33
+ # Set ApiPresenter configuration
34
+ #
35
+ # @example
36
+ # ApiPresenter.configure do |config|
37
+ # config.include_param = :includes
38
+ # end
39
+ #
40
+ def self.configure
41
+ yield configuration
42
+ end
43
+ end
@@ -1,3 +1,3 @@
1
1
  module ApiPresenter
2
- VERSION = "0.2.4"
2
+ VERSION = "0.3.0"
3
3
  end
@@ -0,0 +1 @@
1
+ Creates configuration file
@@ -0,0 +1,11 @@
1
+ module ApiPresenter
2
+ module Generators
3
+ class ConfigGenerator < Rails::Generators::Base
4
+ source_root File.expand_path('../templates', __FILE__)
5
+
6
+ def copy_config_file
7
+ copy_file 'api_presenter_config.rb', 'config/initializers/api_presenter_config.rb'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ ApiPresenter.configure do |config|
2
+ # config.count_param = :count
3
+ # config.include_param = :include
4
+ # config.policies_param = :policies
5
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: api_presenter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Yuval Kordov
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2016-11-07 00:00:00.000000000 Z
12
+ date: 2016-11-09 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -131,11 +131,15 @@ files:
131
131
  - lib/api_presenter.rb
132
132
  - lib/api_presenter/base.rb
133
133
  - lib/api_presenter/concerns/presentable.rb
134
+ - lib/api_presenter/configuration.rb
134
135
  - lib/api_presenter/parsers/parse_include_params.rb
135
136
  - lib/api_presenter/resolvers/base.rb
136
137
  - lib/api_presenter/resolvers/included_collections_resolver.rb
137
138
  - lib/api_presenter/resolvers/policies_resolver.rb
138
139
  - lib/api_presenter/version.rb
140
+ - lib/generators/api_presenter/config/USAGE
141
+ - lib/generators/api_presenter/config/config_generator.rb
142
+ - lib/generators/api_presenter/config/templates/api_presenter_config.rb
139
143
  homepage: http://github.com/uberllama/api_presenter
140
144
  licenses:
141
145
  - MIT