power_api 0.1.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.
Files changed (138) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +1 -0
  3. data/.gitignore +9 -0
  4. data/.hound.yml +4 -0
  5. data/.rspec +3 -0
  6. data/.rubocop.yml +479 -0
  7. data/.ruby-version +1 -0
  8. data/.travis.yml +15 -0
  9. data/CHANGELOG.md +7 -0
  10. data/Gemfile +18 -0
  11. data/Gemfile.lock +310 -0
  12. data/Guardfile +15 -0
  13. data/LICENSE.txt +21 -0
  14. data/README.md +904 -0
  15. data/Rakefile +10 -0
  16. data/app/assets/config/power_api_manifest.js +2 -0
  17. data/app/assets/images/power_api/.keep +0 -0
  18. data/app/assets/javascripts/power_api/application.js +13 -0
  19. data/app/assets/stylesheets/power_api/application.css +15 -0
  20. data/app/controllers/concerns/api/deprecated.rb +23 -0
  21. data/app/controllers/concerns/api/error.rb +37 -0
  22. data/app/controllers/concerns/api/filtered.rb +15 -0
  23. data/app/controllers/concerns/api/versioned.rb +36 -0
  24. data/app/controllers/power_api/base_controller.rb +14 -0
  25. data/app/helpers/power_api/application_helper.rb +4 -0
  26. data/app/jobs/power_api/application_job.rb +4 -0
  27. data/app/mailers/power_api/application_mailer.rb +6 -0
  28. data/app/models/power_api/application_record.rb +5 -0
  29. data/app/responders/api_responder.rb +13 -0
  30. data/app/views/layouts/power_api/application.html.erb +14 -0
  31. data/bin/rails +14 -0
  32. data/config/routes.rb +2 -0
  33. data/lib/generators/power_api/controller/USAGE +5 -0
  34. data/lib/generators/power_api/controller/controller_generator.rb +152 -0
  35. data/lib/generators/power_api/install/USAGE +5 -0
  36. data/lib/generators/power_api/install/install_generator.rb +67 -0
  37. data/lib/generators/power_api/version/USAGE +5 -0
  38. data/lib/generators/power_api/version/version_generator.rb +54 -0
  39. data/lib/power_api.rb +35 -0
  40. data/lib/power_api/engine.rb +28 -0
  41. data/lib/power_api/errors.rb +4 -0
  42. data/lib/power_api/generator_helper/active_record_resource.rb +192 -0
  43. data/lib/power_api/generator_helper/ams_helper.rb +43 -0
  44. data/lib/power_api/generator_helper/controller_helper.rb +184 -0
  45. data/lib/power_api/generator_helper/pagination_helper.rb +48 -0
  46. data/lib/power_api/generator_helper/resource_helper.rb +33 -0
  47. data/lib/power_api/generator_helper/routes_helper.rb +91 -0
  48. data/lib/power_api/generator_helper/rubocop_helper.rb +11 -0
  49. data/lib/power_api/generator_helper/simple_token_auth_helper.rb +124 -0
  50. data/lib/power_api/generator_helper/swagger_helper.rb +463 -0
  51. data/lib/power_api/generator_helper/template_builder_helper.rb +23 -0
  52. data/lib/power_api/generator_helper/version_helper.rb +16 -0
  53. data/lib/power_api/generator_helpers.rb +25 -0
  54. data/lib/power_api/version.rb +3 -0
  55. data/lib/tasks/power_api_tasks.rake +4 -0
  56. data/power_api.gemspec +44 -0
  57. data/spec/dummy/Rakefile +6 -0
  58. data/spec/dummy/app/assets/config/manifest.js +5 -0
  59. data/spec/dummy/app/assets/javascripts/application.js +13 -0
  60. data/spec/dummy/app/assets/javascripts/cable.js +13 -0
  61. data/spec/dummy/app/assets/stylesheets/application.css +15 -0
  62. data/spec/dummy/app/channels/application_cable/channel.rb +4 -0
  63. data/spec/dummy/app/channels/application_cable/connection.rb +4 -0
  64. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  65. data/spec/dummy/app/controllers/concerns/api/deprecated_spec.rb +37 -0
  66. data/spec/dummy/app/controllers/concerns/api/error_spec.rb +97 -0
  67. data/spec/dummy/app/controllers/concerns/api/filtered_spec.rb +42 -0
  68. data/spec/dummy/app/controllers/concerns/api/versioned_spec.rb +64 -0
  69. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  70. data/spec/dummy/app/jobs/application_job.rb +2 -0
  71. data/spec/dummy/app/mailers/application_mailer.rb +4 -0
  72. data/spec/dummy/app/models/application_record.rb +3 -0
  73. data/spec/dummy/app/models/blog.rb +5 -0
  74. data/spec/dummy/app/models/portfolio.rb +5 -0
  75. data/spec/dummy/app/models/user.rb +3 -0
  76. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  77. data/spec/dummy/app/views/layouts/mailer.html.erb +13 -0
  78. data/spec/dummy/app/views/layouts/mailer.text.erb +1 -0
  79. data/spec/dummy/bin/bundle +3 -0
  80. data/spec/dummy/bin/rails +4 -0
  81. data/spec/dummy/bin/rake +4 -0
  82. data/spec/dummy/bin/setup +38 -0
  83. data/spec/dummy/bin/update +29 -0
  84. data/spec/dummy/bin/yarn +11 -0
  85. data/spec/dummy/config.ru +5 -0
  86. data/spec/dummy/config/application.rb +26 -0
  87. data/spec/dummy/config/boot.rb +5 -0
  88. data/spec/dummy/config/cable.yml +10 -0
  89. data/spec/dummy/config/database.yml +25 -0
  90. data/spec/dummy/config/environment.rb +5 -0
  91. data/spec/dummy/config/environments/development.rb +54 -0
  92. data/spec/dummy/config/environments/production.rb +91 -0
  93. data/spec/dummy/config/environments/test.rb +42 -0
  94. data/spec/dummy/config/initializers/application_controller_renderer.rb +8 -0
  95. data/spec/dummy/config/initializers/assets.rb +14 -0
  96. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  97. data/spec/dummy/config/initializers/cookies_serializer.rb +5 -0
  98. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  99. data/spec/dummy/config/initializers/inflections.rb +16 -0
  100. data/spec/dummy/config/initializers/mime_types.rb +4 -0
  101. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  102. data/spec/dummy/config/locales/en.yml +33 -0
  103. data/spec/dummy/config/puma.rb +56 -0
  104. data/spec/dummy/config/routes.rb +12 -0
  105. data/spec/dummy/config/secrets.yml +32 -0
  106. data/spec/dummy/config/spring.rb +6 -0
  107. data/spec/dummy/db/migrate/20190322205209_create_blogs.rb +10 -0
  108. data/spec/dummy/db/migrate/20200215225917_create_users.rb +9 -0
  109. data/spec/dummy/db/migrate/20200227150449_create_portfolios.rb +10 -0
  110. data/spec/dummy/db/migrate/20200227150548_add_portfolio_id_blogs.rb +5 -0
  111. data/spec/dummy/db/schema.rb +38 -0
  112. data/spec/dummy/package.json +5 -0
  113. data/spec/dummy/public/404.html +67 -0
  114. data/spec/dummy/public/422.html +67 -0
  115. data/spec/dummy/public/500.html +66 -0
  116. data/spec/dummy/public/apple-touch-icon-precomposed.png +0 -0
  117. data/spec/dummy/public/apple-touch-icon.png +0 -0
  118. data/spec/dummy/public/favicon.ico +0 -0
  119. data/spec/dummy/spec/assets/image.png +0 -0
  120. data/spec/dummy/spec/assets/video.mp4 +0 -0
  121. data/spec/dummy/spec/factories/blogs.rb +7 -0
  122. data/spec/dummy/spec/factories/portfolios.rb +6 -0
  123. data/spec/dummy/spec/factories/users.rb +5 -0
  124. data/spec/dummy/spec/lib/power_api/generator_helper/ams_helper_spec.rb +87 -0
  125. data/spec/dummy/spec/lib/power_api/generator_helper/controller_helper_spec.rb +361 -0
  126. data/spec/dummy/spec/lib/power_api/generator_helper/pagination_helper_spec.rb +56 -0
  127. data/spec/dummy/spec/lib/power_api/generator_helper/resource_helper_spec.rb +31 -0
  128. data/spec/dummy/spec/lib/power_api/generator_helper/routes_helper_spec.rb +179 -0
  129. data/spec/dummy/spec/lib/power_api/generator_helper/simple_token_auth_helper_spec.rb +164 -0
  130. data/spec/dummy/spec/lib/power_api/generator_helper/swagger_helper_spec.rb +451 -0
  131. data/spec/dummy/spec/lib/power_api/generator_helper/version_helper_spec.rb +55 -0
  132. data/spec/dummy/spec/support/shared_examples/active_record_resource.rb +101 -0
  133. data/spec/dummy/spec/support/shared_examples/active_record_resource_atrributes.rb +164 -0
  134. data/spec/dummy/spec/support/test_generator_helpers.rb +29 -0
  135. data/spec/dummy/spec/support/test_helpers.rb +11 -0
  136. data/spec/rails_helper.rb +49 -0
  137. data/spec/spec_helper.rb +9 -0
  138. metadata +602 -0
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.5
data/.travis.yml ADDED
@@ -0,0 +1,15 @@
1
+ sudo: false
2
+ language: ruby
3
+ rvm:
4
+ - 2.3.1
5
+ script:
6
+ - RAILS_ENV=test bundle exec rake db:create db:migrate
7
+ - bundle exec rspec spec
8
+ deploy:
9
+ provider: rubygems
10
+ api_key:
11
+ secure: your_secure_secret
12
+ gem: power_api
13
+ on:
14
+ tags: true
15
+ repo: platanus/power_api
data/CHANGELOG.md ADDED
@@ -0,0 +1,7 @@
1
+ # Change Log
2
+ All notable changes to this project will be documented in this file.
3
+ This project adheres to [Semantic Versioning](http://semver.org/).
4
+
5
+ ### v0.1.0
6
+
7
+ * Initial release.
data/Gemfile ADDED
@@ -0,0 +1,18 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Declare your gem's dependencies in power_api.gemspec.
4
+ # Bundler will treat runtime dependencies like base dependencies, and
5
+ # development dependencies will be added by default to the :development group.
6
+ gemspec
7
+
8
+ # Declare any dependencies that are still in development here instead of in
9
+ # your gemspec. These might include edge Rails or gems from your path or
10
+ # Git. Remember to move these dependencies to your gemspec before releasing
11
+ # your gem to rubygems.org.
12
+
13
+ # To use a debugger
14
+ # gem 'byebug', group: [:development, :test]
15
+
16
+ group :development, :test do
17
+ gem 'rswag-specs'
18
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,310 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ power_api (0.1.0)
5
+ active_model_serializers (~> 0.10.0)
6
+ api-pagination
7
+ kaminari
8
+ rails (>= 4.2.0)
9
+ ransack
10
+ responders
11
+ rswag-api
12
+ rswag-ui
13
+ simple_token_authentication (~> 1.0)
14
+ versionist (~> 1.0)
15
+
16
+ GEM
17
+ remote: https://rubygems.org/
18
+ specs:
19
+ actioncable (5.2.2.1)
20
+ actionpack (= 5.2.2.1)
21
+ nio4r (~> 2.0)
22
+ websocket-driver (>= 0.6.1)
23
+ actionmailer (5.2.2.1)
24
+ actionpack (= 5.2.2.1)
25
+ actionview (= 5.2.2.1)
26
+ activejob (= 5.2.2.1)
27
+ mail (~> 2.5, >= 2.5.4)
28
+ rails-dom-testing (~> 2.0)
29
+ actionpack (5.2.2.1)
30
+ actionview (= 5.2.2.1)
31
+ activesupport (= 5.2.2.1)
32
+ rack (~> 2.0)
33
+ rack-test (>= 0.6.3)
34
+ rails-dom-testing (~> 2.0)
35
+ rails-html-sanitizer (~> 1.0, >= 1.0.2)
36
+ actionview (5.2.2.1)
37
+ activesupport (= 5.2.2.1)
38
+ builder (~> 3.1)
39
+ erubi (~> 1.4)
40
+ rails-dom-testing (~> 2.0)
41
+ rails-html-sanitizer (~> 1.0, >= 1.0.3)
42
+ active_model_serializers (0.10.10)
43
+ actionpack (>= 4.1, < 6.1)
44
+ activemodel (>= 4.1, < 6.1)
45
+ case_transform (>= 0.2)
46
+ jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
47
+ activejob (5.2.2.1)
48
+ activesupport (= 5.2.2.1)
49
+ globalid (>= 0.3.6)
50
+ activemodel (5.2.2.1)
51
+ activesupport (= 5.2.2.1)
52
+ activerecord (5.2.2.1)
53
+ activemodel (= 5.2.2.1)
54
+ activesupport (= 5.2.2.1)
55
+ arel (>= 9.0)
56
+ activestorage (5.2.2.1)
57
+ actionpack (= 5.2.2.1)
58
+ activerecord (= 5.2.2.1)
59
+ marcel (~> 0.3.1)
60
+ activesupport (5.2.2.1)
61
+ concurrent-ruby (~> 1.0, >= 1.0.2)
62
+ i18n (>= 0.7, < 2)
63
+ minitest (~> 5.1)
64
+ tzinfo (~> 1.1)
65
+ addressable (2.7.0)
66
+ public_suffix (>= 2.0.2, < 5.0)
67
+ api-pagination (4.8.2)
68
+ arel (9.0.0)
69
+ ast (2.4.0)
70
+ bcrypt (3.1.13)
71
+ builder (3.2.3)
72
+ case_transform (0.2)
73
+ activesupport
74
+ coderay (1.1.2)
75
+ concurrent-ruby (1.1.5)
76
+ coveralls (0.8.22)
77
+ json (>= 1.8, < 3)
78
+ simplecov (~> 0.16.1)
79
+ term-ansicolor (~> 1.3)
80
+ thor (~> 0.19.4)
81
+ tins (~> 1.6)
82
+ crass (1.0.4)
83
+ devise (4.7.1)
84
+ bcrypt (~> 3.0)
85
+ orm_adapter (~> 0.1)
86
+ railties (>= 4.1.0)
87
+ responders
88
+ warden (~> 1.2.3)
89
+ diff-lcs (1.3)
90
+ docile (1.3.1)
91
+ erubi (1.8.0)
92
+ factory_bot (5.0.2)
93
+ activesupport (>= 4.2.0)
94
+ factory_bot_rails (5.0.1)
95
+ factory_bot (~> 5.0.0)
96
+ railties (>= 4.2.0)
97
+ ffi (1.10.0)
98
+ formatador (0.2.5)
99
+ globalid (0.4.2)
100
+ activesupport (>= 4.2.0)
101
+ guard (2.15.0)
102
+ formatador (>= 0.2.4)
103
+ listen (>= 2.7, < 4.0)
104
+ lumberjack (>= 1.0.12, < 2.0)
105
+ nenv (~> 0.1)
106
+ notiffany (~> 0.0)
107
+ pry (>= 0.9.12)
108
+ shellany (~> 0.0)
109
+ thor (>= 0.18.1)
110
+ guard-compat (1.2.1)
111
+ guard-rspec (4.7.3)
112
+ guard (~> 2.1)
113
+ guard-compat (~> 1.1)
114
+ rspec (>= 2.99.0, < 4.0)
115
+ i18n (1.6.0)
116
+ concurrent-ruby (~> 1.0)
117
+ jaro_winkler (1.5.4)
118
+ json (2.2.0)
119
+ json-schema (2.8.1)
120
+ addressable (>= 2.4)
121
+ jsonapi-renderer (0.2.2)
122
+ kaminari (1.1.1)
123
+ activesupport (>= 4.1.0)
124
+ kaminari-actionview (= 1.1.1)
125
+ kaminari-activerecord (= 1.1.1)
126
+ kaminari-core (= 1.1.1)
127
+ kaminari-actionview (1.1.1)
128
+ actionview
129
+ kaminari-core (= 1.1.1)
130
+ kaminari-activerecord (1.1.1)
131
+ activerecord
132
+ kaminari-core (= 1.1.1)
133
+ kaminari-core (1.1.1)
134
+ listen (3.1.5)
135
+ rb-fsevent (~> 0.9, >= 0.9.4)
136
+ rb-inotify (~> 0.9, >= 0.9.7)
137
+ ruby_dep (~> 1.2)
138
+ loofah (2.2.3)
139
+ crass (~> 1.0.2)
140
+ nokogiri (>= 1.5.9)
141
+ lumberjack (1.0.13)
142
+ mail (2.7.1)
143
+ mini_mime (>= 0.1.1)
144
+ marcel (0.3.3)
145
+ mimemagic (~> 0.3.2)
146
+ method_source (0.9.2)
147
+ mimemagic (0.3.3)
148
+ mini_mime (1.0.2)
149
+ mini_portile2 (2.4.0)
150
+ minitest (5.11.3)
151
+ nenv (0.3.0)
152
+ nio4r (2.5.2)
153
+ nokogiri (1.10.1)
154
+ mini_portile2 (~> 2.4.0)
155
+ notiffany (0.1.1)
156
+ nenv (~> 0.1)
157
+ shellany (~> 0.0)
158
+ orm_adapter (0.5.0)
159
+ parallel (1.19.1)
160
+ parser (2.7.0.2)
161
+ ast (~> 2.4.0)
162
+ polyamorous (2.3.0)
163
+ activerecord (>= 5.0)
164
+ powerpack (0.1.2)
165
+ pry (0.12.2)
166
+ coderay (~> 1.1.0)
167
+ method_source (~> 0.9.0)
168
+ pry-rails (0.3.9)
169
+ pry (>= 0.10.4)
170
+ psych (3.1.0)
171
+ public_suffix (4.0.1)
172
+ rack (2.0.6)
173
+ rack-test (1.1.0)
174
+ rack (>= 1.0, < 3)
175
+ rails (5.2.2.1)
176
+ actioncable (= 5.2.2.1)
177
+ actionmailer (= 5.2.2.1)
178
+ actionpack (= 5.2.2.1)
179
+ actionview (= 5.2.2.1)
180
+ activejob (= 5.2.2.1)
181
+ activemodel (= 5.2.2.1)
182
+ activerecord (= 5.2.2.1)
183
+ activestorage (= 5.2.2.1)
184
+ activesupport (= 5.2.2.1)
185
+ bundler (>= 1.3.0)
186
+ railties (= 5.2.2.1)
187
+ sprockets-rails (>= 2.0.0)
188
+ rails-dom-testing (2.0.3)
189
+ activesupport (>= 4.2.0)
190
+ nokogiri (>= 1.6)
191
+ rails-html-sanitizer (1.0.4)
192
+ loofah (~> 2.2, >= 2.2.2)
193
+ railties (5.2.2.1)
194
+ actionpack (= 5.2.2.1)
195
+ activesupport (= 5.2.2.1)
196
+ method_source
197
+ rake (>= 0.8.7)
198
+ thor (>= 0.19.0, < 2.0)
199
+ rainbow (3.0.0)
200
+ rake (12.3.2)
201
+ ransack (2.3.0)
202
+ actionpack (>= 5.0)
203
+ activerecord (>= 5.0)
204
+ activesupport (>= 5.0)
205
+ i18n
206
+ polyamorous (= 2.3.0)
207
+ rb-fsevent (0.10.3)
208
+ rb-inotify (0.10.0)
209
+ ffi (~> 1.0)
210
+ responders (3.0.0)
211
+ actionpack (>= 5.0)
212
+ railties (>= 5.0)
213
+ rspec (3.8.0)
214
+ rspec-core (~> 3.8.0)
215
+ rspec-expectations (~> 3.8.0)
216
+ rspec-mocks (~> 3.8.0)
217
+ rspec-core (3.8.0)
218
+ rspec-support (~> 3.8.0)
219
+ rspec-expectations (3.8.2)
220
+ diff-lcs (>= 1.2.0, < 2.0)
221
+ rspec-support (~> 3.8.0)
222
+ rspec-mocks (3.8.0)
223
+ diff-lcs (>= 1.2.0, < 2.0)
224
+ rspec-support (~> 3.8.0)
225
+ rspec-rails (3.8.2)
226
+ actionpack (>= 3.0)
227
+ activesupport (>= 3.0)
228
+ railties (>= 3.0)
229
+ rspec-core (~> 3.8.0)
230
+ rspec-expectations (~> 3.8.0)
231
+ rspec-mocks (~> 3.8.0)
232
+ rspec-support (~> 3.8.0)
233
+ rspec-support (3.8.0)
234
+ rswag-api (2.2.0)
235
+ railties (>= 3.1, < 6.1)
236
+ rswag-specs (2.2.0)
237
+ activesupport (>= 3.1, < 6.1)
238
+ json-schema (~> 2.2)
239
+ railties (>= 3.1, < 6.1)
240
+ rswag-ui (2.2.0)
241
+ actionpack (>= 3.1, < 6.1)
242
+ railties (>= 3.1, < 6.1)
243
+ rubocop (0.65.0)
244
+ jaro_winkler (~> 1.5.1)
245
+ parallel (~> 1.10)
246
+ parser (>= 2.5, != 2.5.1.1)
247
+ powerpack (~> 0.1)
248
+ psych (>= 3.1.0)
249
+ rainbow (>= 2.2.2, < 4.0)
250
+ ruby-progressbar (~> 1.7)
251
+ unicode-display_width (~> 1.4.0)
252
+ rubocop-rspec (1.35.0)
253
+ rubocop (>= 0.60.0)
254
+ ruby-progressbar (1.10.1)
255
+ ruby_dep (1.5.0)
256
+ shellany (0.0.1)
257
+ simple_token_authentication (1.17.0)
258
+ actionmailer (>= 3.2.6, < 7)
259
+ actionpack (>= 3.2.6, < 7)
260
+ devise (>= 3.2, < 6)
261
+ simplecov (0.16.1)
262
+ docile (~> 1.1)
263
+ json (>= 1.8, < 3)
264
+ simplecov-html (~> 0.10.0)
265
+ simplecov-html (0.10.2)
266
+ sprockets (4.0.0)
267
+ concurrent-ruby (~> 1.0)
268
+ rack (> 1, < 3)
269
+ sprockets-rails (3.2.1)
270
+ actionpack (>= 4.0)
271
+ activesupport (>= 4.0)
272
+ sprockets (>= 3.0.0)
273
+ sqlite3 (1.3.13)
274
+ term-ansicolor (1.7.1)
275
+ tins (~> 1.0)
276
+ thor (0.19.4)
277
+ thread_safe (0.3.6)
278
+ tins (1.20.2)
279
+ tzinfo (1.2.5)
280
+ thread_safe (~> 0.1)
281
+ unicode-display_width (1.4.1)
282
+ versionist (1.7.0)
283
+ activesupport (>= 3)
284
+ railties (>= 3)
285
+ yard (~> 0.9.11)
286
+ warden (1.2.8)
287
+ rack (>= 2.0.6)
288
+ websocket-driver (0.7.1)
289
+ websocket-extensions (>= 0.1.0)
290
+ websocket-extensions (0.1.4)
291
+ yard (0.9.20)
292
+
293
+ PLATFORMS
294
+ ruby
295
+
296
+ DEPENDENCIES
297
+ coveralls
298
+ factory_bot_rails
299
+ guard-rspec
300
+ power_api!
301
+ pry
302
+ pry-rails
303
+ rspec-rails
304
+ rswag-specs
305
+ rubocop (= 0.65.0)
306
+ rubocop-rspec
307
+ sqlite3 (~> 1.3.0)
308
+
309
+ BUNDLED WITH
310
+ 1.17.3
data/Guardfile ADDED
@@ -0,0 +1,15 @@
1
+ guard :rspec, cmd: "bundle exec rspec" do
2
+ spec_dic = "spec/dummy/spec"
3
+ # RSpec files
4
+ watch("spec/spec_helper.rb") { spec_dic }
5
+ watch("spec/rails_helper.rb") { spec_dic }
6
+ watch(%r{^spec\/dummy\/spec\/support\/(.+)\.rb$}) { spec_dic }
7
+ watch(%r{^spec\/dummy\/spec\/.+_spec\.rb$})
8
+ # Engine files
9
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/dummy/spec/lib/#{m[1]}_spec.rb" }
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/dummy/spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb)$}) { |m| "spec/dummy/spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ # Dummy app files
13
+ watch(%r{^spec\/dummy\/app/(.+)\.rb$}) { |m| "spec/dummy/spec/#{m[1]}_spec.rb" }
14
+ watch(%r{^spec\/dummy\/app/(.*)(\.erb)$}) { |m| "spec/dummy/spec/#{m[1]}#{m[2]}_spec.rb" }
15
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright 2019 Platanus
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,904 @@
1
+ # Power API
2
+
3
+ It's a Rails engine that gathers a set of gems and configurations designed to build incredible REST APIs.
4
+
5
+ These gems are:
6
+
7
+ - [API Pagination](https://github.com/davidcelis/api-pagination): to handle issues related to pagination.
8
+ - [ActiveModelSerializers](https://github.com/rails-api/active_model_serializers): to handle API response format.
9
+ - [Ransack](https://github.com/activerecord-hackery/ransack): to handle filters.
10
+ - [Responders](https://github.com/heartcombo/responders): to dry up your API.
11
+ - [Rswag](https://github.com/rswag/rswag): to test and document the API.
12
+ - [Simple Token Authentication](https://github.com/gonzalo-bulnes/simple_token_authentication): to authenticate your resources.
13
+ - [Versionist](https://github.com/bploetz/versionist): to handle the API versioning.
14
+
15
+ > To understand what this gem does, it is recommended to read first about those mentioned above.
16
+
17
+
18
+ ## Content
19
+
20
+ - [Installation](#installation)
21
+ - [Usage](#usage)
22
+ - [Initial Setup](#initial-setup)
23
+ - [Command Options](#command-options)
24
+ - [--authenticated-resources](#--authenticated-resources)
25
+ - [Version Creation](#version-creation)
26
+ - [Controller Generation](#controller-generation)
27
+ - [Command Options](#command-options-1)
28
+ - [--attributes](#--attributes)
29
+ - [--version-number](#--version-number)
30
+ - [--use-paginator](#--use-paginator)
31
+ - [--allow-filters](#--allow-filters)
32
+ - [--authenticate-with](#--authenticate-with)
33
+ - [--owned-by-authenticated-resource](#--owned-by-authenticated-resource)
34
+ - [--parent-resource](#--parent-resource)
35
+ - [Inside the gem](#inside-the-gem)
36
+ - [The Api::Error Concern](#the-apierror-concern)
37
+ - [The Api::Deprecated Concern](#the-apideprecated-concern)
38
+ - [The Api::Versioned Concern](#the-apiversioned-concern)
39
+ - [The ApiResponder](#the-apiresponder)
40
+ - [Testing](#testing)
41
+ - [Contributing](#contributing)
42
+ - [Credits](#credits)
43
+ - [License](#license)
44
+
45
+ ## Installation
46
+
47
+ Add to your Gemfile:
48
+
49
+ ```ruby
50
+ gem 'power_api'
51
+
52
+ group :development, :test do
53
+ gem 'factory_bot_rails'
54
+ gem 'rspec-rails'
55
+ gem 'rswag-specs'
56
+ gem 'rubocop'
57
+ gem 'rubocop-rspec'
58
+ end
59
+ ```
60
+
61
+ Then,
62
+
63
+ ```bash
64
+ bundle install
65
+ ```
66
+
67
+ ## Usage
68
+
69
+ ### Initial Setup
70
+
71
+ You must run the following command to have the initial configuration:
72
+
73
+ ```bash
74
+ rails generate power_api:install
75
+ ```
76
+
77
+ After doing this you will get:
78
+
79
+ - A base controller for your API under `/your_app/app/controllers/api/base_controller.rb`
80
+ ```ruby
81
+ class Api::BaseController < PowerApi::BaseController
82
+ end
83
+ ```
84
+ Here you should include everything common to all your API versions. It is usually empty because most of the configuration comes in the `PowerApi::BaseController` that es inside the gem.
85
+
86
+ - A base controller for the first version of your API under `/your_api/app/controllers/api/v1/base_controller.rb`
87
+ ```ruby
88
+ class Api::V1::BaseController < Api::BaseController
89
+ before_action do
90
+ self.namespace_for_serializer = ::Api::V1
91
+ end
92
+ end
93
+ ```
94
+ Everything related to version 1 of your API must be included here.
95
+
96
+ - Some initializers:
97
+ - `/your_api/config/initializers/active_model_serializers.rb`:
98
+ ```ruby
99
+ class ActiveModelSerializers::Adapter::JsonApi
100
+ def self.default_key_transform
101
+ :unaltered
102
+ end
103
+ end
104
+
105
+ ActiveModelSerializers.config.adapter = :json_api
106
+ ```
107
+ Here we tell AMS that we will use the [json api](https://jsonapi.org/) format.
108
+
109
+ - `/your_api/config/initializers/api_pagination.rb`:
110
+ ```ruby
111
+ ApiPagination.configure do |config|
112
+ config.paginator = :kaminari
113
+
114
+ # more options...
115
+ end
116
+ ```
117
+ We use what comes by default and kaminari as pager.
118
+
119
+ - `/your_api/config/initializers/rswag-api.rb`:
120
+ ```ruby
121
+ Rswag::Api.configure do |c|
122
+ c.swagger_root = Rails.root.to_s + '/swagger'
123
+ end
124
+ ```
125
+ We use the default options but setting the `your_api/swagger` directory as container for the generated Swagger JSON files.
126
+
127
+ - `/your_api/config/initializers/rswag-ui.rb`:
128
+ ```ruby
129
+ Rswag::Ui.configure do |c|
130
+ c.swagger_endpoint '/api-docs/v1/swagger.json', 'API V1 Docs'
131
+ end
132
+ ```
133
+ We configure the first version to be seen in the documentation view.
134
+
135
+ - `/your_api/config/initializers/simple_token_authentication.rb`:
136
+ ```ruby
137
+ SimpleTokenAuthentication.configure do |config|
138
+ # options...
139
+ end
140
+ ```
141
+ We use the default options.
142
+ - A modified `/your_api/config/routes.rb` file:
143
+ ```ruby
144
+ Rails.application.routes.draw do
145
+ scope path: '/api' do
146
+ api_version(module: 'Api::V1', path: { value: 'v1' }, defaults: { format: 'json' }) do
147
+ end
148
+ end
149
+ mount Rswag::Api::Engine => '/api-docs'
150
+ mount Rswag::Ui::Engine => '/api-docs'
151
+ # ...
152
+ end
153
+ ```
154
+ Here we create the first version with [Versionist](https://github.com/bploetz/versionist) and mount Rswag.
155
+ - A file with the swagger definition for the first version under `/your_api/spec/swagger/v1/definition.rb`
156
+ ```ruby
157
+ API_V1 = {
158
+ swagger: '2.0',
159
+ info: {
160
+ title: 'API V1',
161
+ version: 'v1'
162
+ },
163
+ basePath: '/api/v1',
164
+ definitions: {
165
+ }
166
+ }
167
+ ```
168
+ - The `/your_api/spec/swagger_helper.rb` (similar to rails_helper.rb file):
169
+ ```ruby
170
+ require 'rails_helper'
171
+
172
+ Dir[::Rails.root.join("spec/swagger/**/schemas/*.rb")].each { |f| require f }
173
+ Dir[::Rails.root.join("spec/swagger/**/definition.rb")].each { |f| require f }
174
+
175
+ RSpec.configure do |config|
176
+ # Specify a root folder where Swagger JSON files are generated
177
+ # NOTE: If you're using the rswag-api to serve API descriptions, you'll need
178
+ # to ensure that it's confiugred to serve Swagger from the same folder
179
+ config.swagger_root = Rails.root.to_s + '/swagger'
180
+
181
+ # Define one or more Swagger documents and provide global metadata for each one
182
+ # When you run the 'rswag:specs:to_swagger' rake task, the complete Swagger will
183
+ # be generated at the provided relative path under swagger_root
184
+ # By default, the operations defined in spec files are added to the first
185
+ # document below. You can override this behavior by adding a swagger_doc tag to the
186
+ # the root example_group in your specs, e.g. describe '...', swagger_doc: 'v2/swagger.json'
187
+ config.swagger_docs = {
188
+ 'v1/swagger.json' => API_V1
189
+ }
190
+ end
191
+ ```
192
+ - An empty directory indicating where you should put your serializers for the first version: `/your_api/app/serializers/api/v1/.gitkeep`
193
+ - An empty directory indicating where you should put your API tests: `/your_api/spec/integration/.gitkeep`
194
+ - An empty directory indicating where you should put your swagger schemas `/your_api/spec/swagger/v1/schemas/.gitkeep`
195
+
196
+ #### Command options:
197
+
198
+ ##### `--authenticated-resources`
199
+
200
+ Use this option if you want to configure [Simple Token Authentication](https://github.com/gonzalo-bulnes/simple_token_authentication) for one or more models.
201
+
202
+ ```bash
203
+ rails g power_api:install --authenticated-resources=user
204
+ ```
205
+
206
+ Running the above code will generate, in addition to everything described in the initial setup, the following:
207
+
208
+ - The [Simple Token Authentication](https://github.com/gonzalo-bulnes/simple_token_authentication) initializer `/your_api/config/initializers/simple_token_authentication.rb`
209
+
210
+ - An edited version of the User model with the configuration needed for Simple Token Authentication.
211
+
212
+ ```ruby
213
+ class User < ApplicationRecord
214
+ acts_as_token_authenticatable
215
+
216
+ # more code...
217
+ end
218
+ ```
219
+ - The migration `/your_api/db/migrate/20200228173608_add_authentication_token_to_users.rb` to add the `authentication_token` to your users table.
220
+
221
+ ### Version Creation
222
+
223
+ To add a new version you must run the following command:
224
+ ```bash
225
+ rails g power_api:version VERSION_NUMBER
226
+ ```
227
+ Example:
228
+ ```bash
229
+ rails g power_api:version 2
230
+ ```
231
+
232
+ Doing this will add the same thing that was added for version one in the initial setup but this time for the number version provided as parameter.
233
+
234
+ ### Controller Generation
235
+
236
+ To add a controller you must run the following command:
237
+ ```bash
238
+ rails g power_api:controller MODEL_NAME [options]
239
+ ```
240
+ Example:
241
+ ```bash
242
+ rails g power_api:controller blog
243
+ ```
244
+ Assuming we have the following model,
245
+
246
+ ```ruby
247
+ class Blog < ApplicationRecord
248
+ # == Schema Information
249
+ #
250
+ # Table name: blogs
251
+ #
252
+ # id :bigint(8) not null, primary key
253
+ # title :string(255)
254
+ # body :text(65535)
255
+ # created_at :datetime not null
256
+ # updated_at :datetime not null
257
+ #
258
+ end
259
+ ```
260
+
261
+ after doing this you will get:
262
+
263
+ - A modified `/your_api/config/routes.rb` file with the new resource:
264
+ ```ruby
265
+ Rails.application.routes.draw do
266
+ scope path: '/api' do
267
+ api_version(module: 'Api::V1', path: { value: 'v1' }, defaults: { format: 'json' }) do
268
+ resources :blogs
269
+ end
270
+ end
271
+ end
272
+ ```
273
+ - A controller under `/your_api/app/controllers/api/v1/blogs_controller.rb`
274
+ ```ruby
275
+ class Api::V1::BlogsController < Api::V1::BaseController
276
+ def index
277
+ respond_with Blog.all
278
+ end
279
+
280
+ def show
281
+ respond_with blog
282
+ end
283
+
284
+ def create
285
+ respond_with Blog.create!(blog_params)
286
+ end
287
+
288
+ def update
289
+ respond_with blog.update!(blog_params)
290
+ end
291
+
292
+ def destroy
293
+ respond_with blog.destroy!
294
+ end
295
+
296
+ private
297
+
298
+ def blog
299
+ @blog ||= Blog.find_by!(id: params[:id])
300
+ end
301
+
302
+ def blog_params
303
+ params.require(:blog).permit(
304
+ :title,
305
+ :body,
306
+ )
307
+ end
308
+ end
309
+ ```
310
+ - A serializer under `/your_api/app/serializers/api/v1/blog_serializer.rb`
311
+ ```ruby
312
+ class Api::V1::BlogSerializer < ActiveModel::Serializer
313
+ type :blog
314
+
315
+ attributes(
316
+ :title,
317
+ :body,
318
+ :created_at,
319
+ :updated_at
320
+ )
321
+ end
322
+ ```
323
+ - A spec file under `/your_api/spec/integration/api/v1/blogs_spec.rb`
324
+ ```ruby
325
+ require 'swagger_helper'
326
+
327
+ describe 'API V1 Blogs', swagger_doc: 'v1/swagger.json' do
328
+ path '/blogs' do
329
+ get 'Retrieves Blogs' do
330
+ description 'Retrieves all the blogs'
331
+ produces 'application/json'
332
+
333
+ let(:collection_count) { 5 }
334
+ let(:expected_collection_count) { collection_count }
335
+
336
+ before { create_list(:blog, collection_count) }
337
+
338
+ response '200', 'Blogs retrieved' do
339
+ schema('$ref' => '#/definitions/blogs_collection')
340
+
341
+ run_test! do |response|
342
+ expect(JSON.parse(response.body)['data'].count).to eq(expected_collection_count)
343
+ end
344
+ end
345
+ end
346
+
347
+ post 'Creates Blog' do
348
+ description 'Creates Blog'
349
+ consumes 'application/json'
350
+ produces 'application/json'
351
+ parameter(name: :blog, in: :body)
352
+
353
+ response '201', 'blog creaed' do
354
+ let(:blog) do
355
+ {
356
+ title: 'Some title',
357
+ body: 'Some body'
358
+ }
359
+ end
360
+
361
+ run_test!
362
+ end
363
+ end
364
+ end
365
+
366
+ path '/blogs/{id}' do
367
+ parameter name: :id, in: :path, type: :integer
368
+
369
+ let(:existent_blog) { create(:blog) }
370
+ let(:id) { existent_blog.id }
371
+
372
+ get 'Retrieves Blog' do
373
+ produces 'application/json'
374
+
375
+ response '200', 'blog retrieved' do
376
+ schema('$ref' => '#/definitions/blog_resource')
377
+
378
+ run_test!
379
+ end
380
+
381
+ response '404', 'invalid blog id' do
382
+ let(:id) { 'invalid' }
383
+ run_test!
384
+ end
385
+ end
386
+
387
+ put 'Updates Blog' do
388
+ description 'Updates Blog'
389
+ consumes 'application/json'
390
+ produces 'application/json'
391
+ parameter(name: :blog, in: :body)
392
+
393
+ response '200', 'blog updated' do
394
+ let(:blog) do
395
+ {
396
+ title: 'Some title',
397
+ body: 'Some body'
398
+ }
399
+ end
400
+
401
+ run_test!
402
+ end
403
+ end
404
+
405
+ delete 'Deletes Blog' do
406
+ produces 'application/json'
407
+ description 'Deletes specific blog'
408
+
409
+ response '204', 'blog deleted' do
410
+ run_test!
411
+ end
412
+
413
+ response '404', 'blog not found' do
414
+ let(:id) { 'invalid' }
415
+
416
+ run_test!
417
+ end
418
+ end
419
+ end
420
+ end
421
+ ```
422
+ - A swagger schema definition under `/your_api/spec/swagger/v1/schemas/blog_schema.rb`
423
+ ```ruby
424
+ BLOG_SCHEMA = {
425
+ type: :object,
426
+ properties: {
427
+ id: { type: :string, example: '1' },
428
+ type: { type: :string, example: 'blog' },
429
+ attributes: {
430
+ type: :object,
431
+ properties: {
432
+ title: { type: :string, example: 'Some title', 'x-nullable': true },
433
+ body: { type: :string, example: 'Some body', 'x-nullable': true },
434
+ created_at: { type: :string, example: '1984-06-04 09:00', 'x-nullable': true },
435
+ updated_at: { type: :string, example: '1984-06-04 09:00', 'x-nullable': true }
436
+ },
437
+ required: [
438
+ ]
439
+ }
440
+ },
441
+ required: [
442
+ :id,
443
+ :type,
444
+ :attributes
445
+ ]
446
+ }
447
+
448
+ BLOGS_COLLECTION_SCHEMA = {
449
+ type: "object",
450
+ properties: {
451
+ data: {
452
+ type: "array",
453
+ items: { "$ref" => "#/definitions/blog" }
454
+ }
455
+ },
456
+ required: [
457
+ :data
458
+ ]
459
+ }
460
+
461
+ BLOG_RESOURCE_SCHEMA = {
462
+ type: "object",
463
+ properties: {
464
+ data: { "$ref" => "#/definitions/blog" }
465
+ },
466
+ required: [
467
+ :data
468
+ ]
469
+ }
470
+ ```
471
+ - An edited version of `your_api/api_example/spec/swagger/v1/definition.rb` with the schema definitions for the `Blog` resource.
472
+ ```ruby
473
+ API_V1 = {
474
+ swagger: '2.0',
475
+ info: {
476
+ title: 'API V1',
477
+ version: 'v1'
478
+ },
479
+ basePath: '/api/v1',
480
+ definitions: {
481
+ blog: BLOG_SCHEMA,
482
+ blogs_collection: BLOGS_COLLECTION_SCHEMA,
483
+ blog_resource: BLOG_RESOURCE_SCHEMA,
484
+ }
485
+ }
486
+ ```
487
+
488
+ #### Command options:
489
+
490
+ ##### `--attributes`
491
+
492
+ Use this option if you want to choose which attributes of your model to add to the API response.
493
+
494
+ ```bash
495
+ rails g power_api:controller blog --attributes=title
496
+ ```
497
+
498
+ When you do this, you will see permited_params, serializers, swagger definitions, etc. showing only the selected attributes
499
+
500
+ For example, the serializer under `/your_api/app/serializers/api/v1/blog_serializer.rb` will show:
501
+ ```ruby
502
+ class Api::V1::BlogSerializer < ActiveModel::Serializer
503
+ type :blog
504
+
505
+ attributes(
506
+ :title,
507
+ )
508
+ end
509
+ ```
510
+
511
+ ##### `--version-number`
512
+
513
+ Use this option if you want to decide which version the new controller will belong to.
514
+
515
+ ```bash
516
+ rails g power_api:controller blog --version-number=2
517
+ ```
518
+
519
+ ##### `--use-paginator`
520
+
521
+ Use this option if you want to paginate the index endpoint collection.
522
+
523
+ ```bash
524
+ rails g power_api:controller blog --use-paginator
525
+ ```
526
+
527
+ The controller under `/your_api/app/controllers/api/v1/blogs_controller.rb` will be modified to use the paginator like this:
528
+
529
+ ```ruby
530
+ class Api::V1::BlogsController < Api::V1::BaseController
531
+ def index
532
+ respond_with paginate(Blog.all)
533
+ end
534
+
535
+ # more code...
536
+ end
537
+ ```
538
+
539
+ Due to the API Pagination gem the `X-Total`, `X-Per-Page` and `X-Page` headers will be added to the answer. The parameters `params[:page][:number]` and `params[:page][:size]` can also be passed through the query string to access the different pages.
540
+
541
+ Because the AMS gem is set with "json api" format, links related to pagination will be added to the API response.
542
+
543
+ ##### `--allow-filters`
544
+
545
+ Use this option if you want to filter your index endpoint collection with [Ransack](https://github.com/activerecord-hackery/ransack)
546
+
547
+ ```bash
548
+ rails g power_api:controller blog --allow-filters
549
+ ```
550
+
551
+ The controller under `/your_api/app/controllers/api/v1/blogs_controller.rb` will be modified like this:
552
+
553
+ ```ruby
554
+ class Api::V1::BlogsController < Api::V1::BaseController
555
+ def index
556
+ respond_with filtered_collection(Blog.all)
557
+ end
558
+
559
+ # more code...
560
+ end
561
+ ```
562
+
563
+ The `filtered_collection` method is defined inside the gem and uses ransack below.
564
+ You will be able to filter the results according to this: https://github.com/activerecord-hackery/ransack#search-matchers
565
+
566
+ For example:
567
+
568
+ `http://localhost:3000/api/v1/blogs?q[id_gt]=22`
569
+
570
+ to search blogs with id greater than 22
571
+
572
+ ##### `--authenticate-with`
573
+
574
+ Use this option if you want to have authorized resources.
575
+
576
+ > To learn more about the authentication method used please read more about [Simple Token Authentication](https://github.com/gonzalo-bulnes/simple_token_authentication) gem.
577
+
578
+ ```bash
579
+ rails g power_api:controller MODEL_NAME --authenticate-with=ANOTHER_MODEL_NAME
580
+ ```
581
+
582
+ Example:
583
+
584
+ ```bash
585
+ rails g power_api:controller blog --authenticate-with=user
586
+ ```
587
+
588
+ When you do this your controller will have the following line:
589
+
590
+ ```ruby
591
+ class Api::V1::BlogsController < Api::V1::BaseController
592
+ acts_as_token_authentication_handler_for User, fallback: :exception
593
+
594
+ # mode code...
595
+ end
596
+ ```
597
+
598
+ In addition, the specs under `/your_api/spec/integration/api/v1/blogs_spec.rb` will add tests related with authorization.
599
+
600
+ ```ruby
601
+ response '401', 'user unauthorized' do
602
+ let(:user_token) { 'invalid' }
603
+
604
+ run_test!
605
+ end
606
+ ```
607
+
608
+ ##### `--owned-by-authenticated-resource`
609
+
610
+ If you have an authenticated resource you can choose your new resource be owned by the authenticated one.
611
+
612
+ ```bash
613
+ rails g power_api:controller blog --authenticate-with=user --owned-by-authenticated-resource
614
+ ```
615
+
616
+ The controller will look like this:
617
+
618
+ ```ruby
619
+ class Api::V1::BlogsController < Api::V1::BaseController
620
+ acts_as_token_authentication_handler_for User, fallback: :exception
621
+
622
+ def index
623
+ respond_with blogs
624
+ end
625
+
626
+ def show
627
+ respond_with blog
628
+ end
629
+
630
+ def create
631
+ respond_with blogs.create!(blog_params)
632
+ end
633
+
634
+ def update
635
+ respond_with blog.update!(blog_params)
636
+ end
637
+
638
+ def destroy
639
+ respond_with blog.destroy!
640
+ end
641
+
642
+ private
643
+
644
+ def blog
645
+ @blog ||= blogs.find_by!(id: params[:id])
646
+ end
647
+
648
+ def blogs
649
+ @blogs ||= current_user.blogs
650
+ end
651
+
652
+ def blog_params
653
+ params.require(:blog).permit(
654
+ :title,
655
+ :body
656
+ )
657
+ end
658
+ end
659
+ ```
660
+
661
+ As you can see the resource (`blog`) will always come from the authorized one (`current_user.blogs`)
662
+
663
+ To make this possible, the models should be related as follows:
664
+
665
+ ```ruby
666
+ class Blog < ApplicationRecord
667
+ belongs_to :user
668
+ end
669
+
670
+ class User < ApplicationRecord
671
+ has_many :blogs
672
+ end
673
+ ```
674
+
675
+ ##### `--parent-resource`
676
+
677
+ Assuming we have the following models,
678
+
679
+ ```ruby
680
+ class Blog < ApplicationRecord
681
+ has_many :comments
682
+ end
683
+
684
+ class Comment < ApplicationRecord
685
+ belongs_to :blog
686
+ end
687
+ ```
688
+
689
+ we can run the following code to handle nested resources:
690
+
691
+ ```ruby
692
+ rails g power_api:controller comment --attributes=body --parent-resource=blog
693
+ ```
694
+
695
+ Running the previous code we will get:
696
+
697
+ - The controller under `/your_api/app/controllers/api/v1/comments_controller.rb`:
698
+ ```ruby
699
+ class Api::V1::CommentsController < Api::V1::BaseController
700
+ def index
701
+ respond_with comments
702
+ end
703
+
704
+ def show
705
+ respond_with comment
706
+ end
707
+
708
+ def create
709
+ respond_with comments.create!(comment_params)
710
+ end
711
+
712
+ def update
713
+ respond_with comment.update!(comment_params)
714
+ end
715
+
716
+ def destroy
717
+ respond_with comment.destroy!
718
+ end
719
+
720
+ private
721
+
722
+ def comment
723
+ @comment ||= Comment.find_by!(id: params[:id])
724
+ end
725
+
726
+ def comments
727
+ @comments ||= blog.comments
728
+ end
729
+
730
+ def blog
731
+ @blog ||= Blog.find_by!(id: params[:blog_id])
732
+ end
733
+
734
+ def comment_params
735
+ params.require(:comment).permit(
736
+ :body
737
+ )
738
+ end
739
+ end
740
+ ```
741
+ As you can see the `comments` used on `index` and `create` will always come from `blog` (the parent resource)
742
+
743
+ - A modified `/your_api/config/routes.rb` file with the nested resource:
744
+ ```ruby
745
+ Rails.application.routes.draw do
746
+ scope path: '/api' do
747
+ api_version(module: 'Api::V1', path: { value: 'v1' }, defaults: { format: 'json' }) do
748
+ resources :comments, only: [:show, :update, :destroy]
749
+ resources :blogs do
750
+ resources :comments, only: [:index, :create]
751
+ end
752
+ end
753
+ end
754
+ end
755
+ ```
756
+ - A spec file under `/your_api/spec/integration/api/v1/blogs_spec.rb` reflecting the nested resources:
757
+ ```ruby
758
+ require 'swagger_helper'
759
+
760
+ describe 'API V1 Comments', swagger_doc: 'v1/swagger.json' do
761
+ let(:blog) { create(:blog) }
762
+ let(:blog_id) { blog.id }
763
+
764
+ path '/blogs/{blog_id}/comments' do
765
+ parameter name: :blog_id, in: :path, type: :integer
766
+ get 'Retrieves Comments' do
767
+ description 'Retrieves all the comments'
768
+ produces 'application/json'
769
+
770
+ let(:collection_count) { 5 }
771
+ let(:expected_collection_count) { collection_count }
772
+
773
+ before { create_list(:comment, collection_count, blog: blog) }
774
+
775
+ response '200', 'Comments retrieved' do
776
+ schema('$ref' => '#/definitions/comments_collection')
777
+
778
+ run_test! do |response|
779
+ expect(JSON.parse(response.body)['data'].count).to eq(expected_collection_count)
780
+ end
781
+ end
782
+ end
783
+ end
784
+
785
+ # more code...
786
+ end
787
+ ```
788
+ > Note that the options: `--parent-resource` and `--owned-by-authenticated-resource` cannot be used together.
789
+
790
+ ## Inside the gem
791
+
792
+ ```ruby
793
+ module PowerApi
794
+ class BaseController < ApplicationController
795
+ include Api::Error
796
+ include Api::Deprecated
797
+ include Api::Versioned
798
+
799
+ self.responder = ApiResponder
800
+
801
+ respond_to :json
802
+ end
803
+ end
804
+ ```
805
+
806
+ The `PowerApi::BaseController` class that exists inside this gem and is inherited by the base class of your API (`/your_app/app/controllers/api/base_controller.rb`) includes functionality that I will describe bellow:
807
+
808
+ ### The `Api::Error` concern
809
+
810
+ This module handles common exceptions like:
811
+
812
+ - `ActiveRecord::RecordNotFound`
813
+ - `ActiveModel::ForbiddenAttributesError`
814
+ - `ActiveRecord::RecordInvalid`
815
+ - `PowerApi::InvalidVersion`
816
+ - `Exception`
817
+
818
+ If you want to handle new errors, this can be done by calling the `respond_api_error` method in the base class of your API like this:
819
+
820
+ ```ruby
821
+ class Api::BaseController < PowerApi::BaseController
822
+ rescue_from "MyCustomErrorClass" do |exception|
823
+ respond_api_error(:bad_request, message: "some error message", detail: exception.message)
824
+ end
825
+ end
826
+ ```
827
+
828
+ ### The `Api::Deprecated` concern
829
+
830
+ This module is useful when you want to mark endpoints as deprecated.
831
+
832
+ For example, if you have the following controller:
833
+
834
+ ```ruby
835
+ class Api::V1::CommentsController < Api::V1::BaseController
836
+ deprecate :index
837
+
838
+ def index
839
+ respond_with comments
840
+ end
841
+
842
+ # more code...
843
+ end
844
+ ```
845
+
846
+ And then in your browser you execute: `GET /api/v1/comments`, you will get a `Deprecated: true` response header.
847
+
848
+ This is useful to notify your customers that an endpoint will not be available in the next version of the API.
849
+
850
+ ### The `Api::Versioned` concern
851
+
852
+ This module includes to your API responses the version of the API in a header. For example: `Content-Type: application/json; charset=utf-8; version=1`
853
+
854
+ ### The `ApiResponder`
855
+
856
+ It look like this:
857
+
858
+ ```ruby
859
+ class ApiResponder < ActionController::Responder
860
+ def api_behavior
861
+ raise MissingRenderer.new(format) unless has_renderer?
862
+
863
+ if delete?
864
+ head :no_content
865
+ elsif post?
866
+ display resource, status: :created
867
+ else
868
+ display resource
869
+ end
870
+ end
871
+ end
872
+ ```
873
+
874
+ As you can see, this simple [Responder](https://github.com/heartcombo/responders) handles the API response based on the HTTP verbs.
875
+
876
+ ## Testing
877
+
878
+ To run the specs you need to execute, **in the root path of the gem**, the following command:
879
+
880
+ ```bash
881
+ bundle exec guard
882
+ ```
883
+
884
+ You need to put **all your tests** in the `/power_api/spec/dummy/spec/` directory.
885
+
886
+ ## Contributing
887
+
888
+ 1. Fork it
889
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
890
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
891
+ 4. Push to the branch (`git push origin my-new-feature`)
892
+ 5. Create new Pull Request
893
+
894
+ ## Credits
895
+
896
+ Thank you [contributors](https://github.com/platanus/power_api/graphs/contributors)!
897
+
898
+ <img src="http://platan.us/gravatar_with_text.png" alt="Platanus" width="250"/>
899
+
900
+ Power API is maintained by [platanus](http://platan.us).
901
+
902
+ ## License
903
+
904
+ Power API is © 2019 platanus, spa. It is free software and may be redistributed under the terms specified in the LICENSE file.