power_api 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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.