rest-api-generator 0.1.1 → 0.1.3

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 (29) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile +3 -1
  3. data/Gemfile.lock +191 -43
  4. data/README.md +173 -7
  5. data/Rakefile +1 -1
  6. data/lib/generators/rest_api_generator/helpers.rb +72 -0
  7. data/lib/generators/rest_api_generator/resource_generator.rb +33 -16
  8. data/lib/generators/rest_api_generator/spec/rspec_generator.rb +36 -0
  9. data/lib/generators/rest_api_generator/spec/rswag_generator.rb +54 -0
  10. data/lib/generators/rest_api_generator/spec/templates/rspec/nested_resource_controller_spec.rb.tt +62 -0
  11. data/lib/generators/rest_api_generator/{templates/rest_api_spec.rb.tt → spec/templates/rspec/resource_controller_spec.rb.tt} +5 -5
  12. data/lib/generators/rest_api_generator/spec/templates/rswag/nested_resource_controller_spec.rb.tt +116 -0
  13. data/lib/generators/rest_api_generator/spec/templates/rswag/resource_controller_spec.rb.tt +111 -0
  14. data/lib/generators/rest_api_generator/templates/child_api_controller.rb.tt +10 -9
  15. data/lib/generators/rest_api_generator/templates/implicit_child_resource_controller.rb.tt +4 -0
  16. data/lib/generators/rest_api_generator/templates/implicit_resource_controller.rb.tt +4 -0
  17. data/lib/generators/rest_api_generator/templates/rest_api_controller.rb.tt +2 -0
  18. data/lib/rest-api-generator.rb +3 -0
  19. data/lib/rest_api_generator/application_controller.rb +8 -0
  20. data/lib/rest_api_generator/child_resource_controller.rb +97 -0
  21. data/lib/rest_api_generator/filterable.rb +30 -0
  22. data/lib/rest_api_generator/helpers/render.rb +1 -1
  23. data/lib/rest_api_generator/orderable.rb +30 -0
  24. data/lib/rest_api_generator/resource_controller.rb +73 -0
  25. data/lib/rest_api_generator/version.rb +1 -1
  26. data/lib/rest_api_generator.rb +11 -1
  27. data/rest-api-generator.gemspec +11 -4
  28. metadata +90 -8
  29. data/lib/generators/rest_api_generator/templates/child_api_spec.rb.tt +0 -59
@@ -1,6 +1,8 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require "rails/generators"
4
+ require "rails/generators/active_model"
5
+ require "rails/generators/rails/model/model_generator"
4
6
  require "generators/rest_api_generator/helpers"
5
7
  module RestApiGenerator
6
8
  class ResourceGenerator < Rails::Generators::NamedBase
@@ -8,31 +10,46 @@ module RestApiGenerator
8
10
  source_root File.expand_path("templates", __dir__)
9
11
 
10
12
  argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
13
+ class_option :eject, type: :boolean, default: false
11
14
  class_option :scope, type: :string, default: ""
12
-
13
- API_CONTROLLER_DIR_PATH = "app/controllers"
14
- API_TEST_DIR_PATH = "spec/requests"
15
+ class_option :father, type: :string, default: ""
16
+ hook_for :spec, in: "rest_api_generator:spec", default: "rspec"
15
17
 
16
18
  def create_service_file
17
- Rails::Generators.invoke("model", [file_name, build_model_attributes])
18
- if options["scope"].empty?
19
- controller_path = "#{API_CONTROLLER_DIR_PATH}/#{file_name.pluralize}_controller.rb"
20
- controller_test_path = "#{API_TEST_DIR_PATH}/#{file_name.pluralize}_controller_spec.rb"
21
- template "rest_api_controller.rb", controller_path
22
- template "rest_api_spec.rb", controller_test_path
23
- routes_string = "resources :#{file_name.pluralize}"
24
- route routes_string
19
+ create_model_files
20
+
21
+ template controller_template, controller_path
22
+
23
+ # Routes
24
+ if options["scope"].blank? && options["father"].blank?
25
+ route "resources :#{file_name.pluralize}"
25
26
  else
26
- scope_path = options["scope"].pluralize
27
- controller_path = "#{API_CONTROLLER_DIR_PATH}/#{scope_path}/#{file_name.pluralize}_controller.rb"
28
- controller_test_path = "#{API_TEST_DIR_PATH}/#{scope_path}/#{file_name.pluralize}_controller_spec.rb"
29
- template "child_api_controller.rb", controller_path
30
- template "child_api_spec.rb", controller_test_path
27
+ log("You need to manually setup routes files for nested or scoped resource")
31
28
  end
32
29
  end
33
30
 
34
31
  private
35
32
 
33
+ def controller_template
34
+ if options["eject"]
35
+ if options["father"].present?
36
+ "child_api_controller.rb"
37
+ else
38
+ "rest_api_controller.rb"
39
+ end
40
+ elsif options["father"].present?
41
+ "implicit_child_resource_controller.rb"
42
+ else
43
+ "implicit_resource_controller.rb"
44
+ end
45
+ end
46
+
47
+ def create_model_files
48
+ g = Rails::Generators::ModelGenerator.new([file_name, build_model_attributes])
49
+ g.destination_root = destination_root
50
+ g.invoke_all
51
+ end
52
+
36
53
  def build_model_attributes
37
54
  model_attributes = []
38
55
  attributes.each do |attribute|
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "generators/rest_api_generator/helpers"
5
+
6
+ module RestApiGenerator
7
+ module Spec
8
+ class RspecGenerator < Rails::Generators::NamedBase
9
+ include Helpers
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
13
+ class_option :eject, type: :boolean, default: false
14
+ class_option :scope, type: :string, default: ""
15
+ class_option :father, type: :string, default: ""
16
+
17
+ def create_service_file
18
+ template spec_controller_template, controller_test_path
19
+ end
20
+
21
+ private
22
+
23
+ def controller_test_path
24
+ "#{API_TEST_DIR_PATH}#{scope_path}/#{file_name.pluralize}_controller_spec.rb"
25
+ end
26
+
27
+ def spec_controller_template
28
+ if options["father"].present?
29
+ "rspec/resource_controller_spec.rb"
30
+ else
31
+ "rspec/nested_resource_controller_spec.rb"
32
+ end
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "rails/generators"
4
+ require "generators/rest_api_generator/helpers"
5
+
6
+ module RestApiGenerator
7
+ module Spec
8
+ class RswagGenerator < Rails::Generators::NamedBase
9
+ include Helpers
10
+ source_root File.expand_path("templates", __dir__)
11
+
12
+ argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
13
+ class_option :eject, type: :boolean, default: false
14
+ class_option :scope, type: :string, default: ""
15
+ class_option :father, type: :string, default: ""
16
+
17
+ def create_service_file
18
+ template spec_controller_template, controller_test_path
19
+ end
20
+
21
+ private
22
+
23
+ # Changes nested routes for rswag format
24
+ # Example: /cars/{car.id}/drivers/{id}
25
+ def nested_routes
26
+ return "" if options["father"].blank?
27
+
28
+ "#{options["father"].downcase.pluralize}/{#{options["father"].singularize.downcase}_id}/#{plural_name}"
29
+ end
30
+
31
+ def spec_routes
32
+ {
33
+ index: initial_route,
34
+ show: initial_route + "/{id}",
35
+ create: initial_route,
36
+ update: initial_route + "/{id}",
37
+ delete: initial_route + "/{id}",
38
+ }
39
+ end
40
+
41
+ def controller_test_path
42
+ "#{API_TEST_DIR_PATH}#{scope_path}/#{file_name.pluralize}_spec.rb"
43
+ end
44
+
45
+ def spec_controller_template
46
+ if options["father"].present?
47
+ "rswag/nested_resource_controller_spec.rb"
48
+ else
49
+ "rswag/resource_controller_spec.rb"
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,62 @@
1
+ require "rails_helper"
2
+
3
+ RSpec.describe "<%= class_name %>", type: :request do
4
+ let(:valid_attributes) { attributes_for(:<%= singular_table_name %>) }
5
+
6
+ describe "GET /<%= plural_name %>" do
7
+ let(:<%= options['father'].downcase.singularize %>) { create(:<%= options['father'].downcase.singularize %>) }
8
+ let(:<%= singular_table_name %>) { create(:<%=singular_table_name %>, <%= options['father'].downcase %>: <%= options['father'].downcase %>) }
9
+
10
+ it "renders a successful response" do
11
+ get "<%= spec_routes[:index] %>"
12
+ expect(response).to be_successful
13
+ end
14
+ end
15
+
16
+
17
+ describe "GET /<%= singular_name %>" do
18
+ let(:<%= options['father'].downcase.singularize %>) { create(:<%= options['father'].downcase.singularize %>) }
19
+ let(:<%= singular_table_name %>) { create(:<%=singular_table_name %>, <%= options['father'].downcase.singularize %>: <%= options['father'].downcase %>)}
20
+
21
+ it "gets an <%= singular_name %>" do
22
+ get "<%= spec_routes[:show] %>"
23
+ expect(response).to be_successful
24
+ end
25
+ end
26
+
27
+
28
+ describe "POST /<%= plural_name %>" do
29
+ context "with valid parameters" do
30
+ let(:<%= options['father'].downcase.singularize %>) { create(:<%= options['father'].downcase.singularize %>) }
31
+ it "creates a new <%= singular_name %>" do
32
+ expect do
33
+ post "<%= spec_routes[:create] %>",
34
+ params: { <%= singular_name %>: valid_attributes }
35
+ end.to change(<%= class_name %>, :count).by(1)
36
+ end
37
+ end
38
+ end
39
+
40
+ describe "PATCH /<%= plural_name %>" do
41
+ let(:<%= options['father'].downcase.singularize %>) { create(:<%= options['father'].downcase.singularize %>) }
42
+ let(:<%= singular_table_name %>) { create(:<%=singular_table_name %>, <%= options['father'].downcase.singularize %>: <%= options['father'].downcase.singularize %>)}
43
+
44
+ it "updates an <%= singular_name %>" do
45
+ patch "<%= spec_routes[:update] %>",
46
+ params: { <%= singular_name %>: valid_attributes }
47
+ expect(response).to have_http_status(:success)
48
+ end
49
+ end
50
+
51
+ describe "DELETE /<%= singular_name %>" do
52
+ let(:<%= options['father'].downcase.singularize %>) { create(:<%= options['father'].downcase.singularize %>) }
53
+
54
+ it "deletes an <%= singular_name %>" do
55
+ <%= singular_table_name %> = create(:<%=singular_table_name %>, <%= options['father'].downcase.singularize %>: <%= options['father'].downcase.singularize %>)
56
+ expect do
57
+ delete "<%= spec_routes[:delete] %>"
58
+ end.to change(<%= class_name %>, :count).by(-1)
59
+ end
60
+ end
61
+
62
+ end
@@ -7,14 +7,14 @@ RSpec.describe "<%= class_name %>", type: :request do
7
7
  describe "GET /<%= plural_name %>" do
8
8
  it "returns http success" do
9
9
  create(:<%= singular_table_name %>)
10
- get "/<%= plural_name %>"
10
+ get "<%= spec_routes[:index] %>"
11
11
  expect(response).to have_http_status(:success)
12
12
  end
13
13
  end
14
14
 
15
15
  describe "GET /<%= plural_name %>/:id" do
16
16
  it "returns http success" do
17
- get "/<%= plural_name %>/#{<%= singular_name %>.id}"
17
+ get "<%= spec_routes[:show] %>"
18
18
  expect(response).to have_http_status(:success)
19
19
  end
20
20
  end
@@ -22,7 +22,7 @@ RSpec.describe "<%= class_name %>", type: :request do
22
22
  describe "POST /<%= plural_name %>" do
23
23
  it "creates a new item" do
24
24
  expect do
25
- post "/<%= plural_name %>",
25
+ post "<%= spec_routes[:create] %>",
26
26
  params: { <%= singular_name %>: valid_attributes }
27
27
  end.to change(<%= class_name %>, :count).by(1)
28
28
  end
@@ -30,7 +30,7 @@ RSpec.describe "<%= class_name %>", type: :request do
30
30
 
31
31
  describe "PATCH /<%= plural_name %>/:id" do
32
32
  it "return http success" do
33
- patch "/<%= plural_name %>/#{<%= singular_name %>.id}",
33
+ patch "<%= spec_routes[:update] %>",
34
34
  params: { <%= singular_name %>: valid_attributes }
35
35
  expect(response).to have_http_status(:success)
36
36
  end
@@ -40,7 +40,7 @@ RSpec.describe "<%= class_name %>", type: :request do
40
40
  it "deletes an <%= plural_name %>" do
41
41
  item = create(:<%= singular_name %>)
42
42
  expect do
43
- delete "/<%= plural_name %>/#{item.id}"
43
+ delete "<%= spec_routes[:delete] %>"
44
44
  end.to change(<%= class_name %>, :count).by(-1)
45
45
  end
46
46
  end
@@ -0,0 +1,116 @@
1
+ require "rails_helper"
2
+
3
+ RSpec.describe "<%= spec_routes[:index] %>", type: :request do
4
+ let(:resource) { create(:<%= singular_table_name %>, <%= options['father'].downcase.singularize %>: parent_resource) }
5
+ let(:parent_resource ) { create(:<%= options['father'].downcase.singularize %>) }
6
+
7
+ path "<%= spec_routes[:index] %>" do
8
+ parameter name: "<%= options['father'].downcase.singularize %>_id", in: :path, type: :string, description: "<%= options['father'].downcase.singularize %> id"
9
+ let(:<%= options['father'].downcase.singularize %>_id) { parent_resource.id }
10
+
11
+ get("list <%= plural_name %>") do
12
+ consumes "application/json"
13
+
14
+ response(200, "successful") do
15
+ after do |example|
16
+ example.metadata[:response][:content] = {
17
+ "application/json" => {
18
+ example: JSON.parse(response.body, symbolize_names: true)
19
+ }
20
+ }
21
+ end
22
+
23
+ run_test!
24
+ end
25
+ end
26
+
27
+ post("create <%= singular_table_name %>") do
28
+ consumes "application/json"
29
+
30
+ # You'll want to customize the parameter types...
31
+ parameter name: :<%= singular_table_name %>, in: :body, schema: {
32
+ type: :object,
33
+ properties: {
34
+ <% attributes.each do |attribute| -%>
35
+ <%= attribute.name %>: { type: :string },
36
+ <% end -%>
37
+ }
38
+ }
39
+ response(201, "successful") do
40
+ let(:<%= singular_table_name %>) { attributes_for(:<%= singular_table_name %>) }
41
+
42
+ after do |example|
43
+ example.metadata[:response][:content] = {
44
+ "application/json" => {
45
+ example: JSON.parse(response.body, symbolize_names: true)
46
+ }
47
+ }
48
+ end
49
+
50
+ run_test!
51
+ end
52
+ end
53
+ end
54
+
55
+ path "<%= spec_routes[:show] %>" do
56
+ parameter name: "id", in: :path, type: :string, description: "id"
57
+ parameter name: "<%= options['father'].downcase.singularize %>_id", in: :path, type: :string, description: "<%= options['father'].downcase.singularize %> id"
58
+
59
+ let(:id) { resource.id }
60
+ let(:<%= options['father'].downcase.singularize %>_id) { parent_resource.id }
61
+
62
+ get("show <%= singular_table_name %>") do
63
+ consumes "application/json"
64
+
65
+ response(200, "successful") do
66
+
67
+ after do |example|
68
+ example.metadata[:response][:content] = {
69
+ "application/json" => {
70
+ example: JSON.parse(response.body, symbolize_names: true)
71
+ }
72
+ }
73
+ end
74
+ run_test!
75
+ end
76
+ end
77
+
78
+
79
+ patch("update <%= singular_table_name %>") do
80
+ consumes "application/json"
81
+
82
+ # You'll want to customize the parameter types...
83
+ parameter name: :<%= singular_table_name %>, in: :body, schema: {
84
+ type: :object,
85
+ properties: {
86
+ <% attributes.each do |attribute| -%>
87
+ <%= attribute.name %>: { type: :string },
88
+ <% end -%>
89
+ }
90
+ }
91
+
92
+ response(200, "successful") do
93
+ let(:<%= singular_table_name %>) { attributes_for(:<%= singular_table_name %>) }
94
+
95
+ after do |example|
96
+ example.metadata[:response][:content] = {
97
+ "application/json" => {
98
+ example: JSON.parse(response.body, symbolize_names: true)
99
+ }
100
+ }
101
+ end
102
+
103
+ run_test!
104
+ end
105
+ end
106
+
107
+ delete("delete plan") do
108
+ consumes "application/json"
109
+
110
+ response(204, "successful") do
111
+
112
+ run_test!
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,111 @@
1
+ require "rails_helper"
2
+
3
+ RSpec.describe "<%= spec_routes[:index] %>", type: :request do
4
+ let(:resource) { create(:<%= singular_table_name %>) }
5
+
6
+ path "<%= spec_routes[:index] %>" do
7
+ get("list <%= plural_name %>") do
8
+ consumes "application/json"
9
+
10
+ response(200, "successful") do
11
+ after do |example|
12
+ example.metadata[:response][:content] = {
13
+ "application/json" => {
14
+ example: JSON.parse(response.body, symbolize_names: true)
15
+ }
16
+ }
17
+ end
18
+
19
+ run_test!
20
+ end
21
+ end
22
+
23
+ post("create <%= singular_table_name %>") do
24
+ consumes "application/json"
25
+
26
+ # You'll want to customize the parameter types...
27
+ parameter name: :<%= singular_table_name %>, in: :body, schema: {
28
+ type: :object,
29
+ properties: {
30
+ <% attributes.each do |attribute| -%>
31
+ <%= attribute.name %>: { type: :string },
32
+ <% end -%>
33
+ }
34
+ }
35
+ response(201, "successful") do
36
+ let(:<%= singular_table_name %>) { attributes_for(:<%= singular_table_name %>) }
37
+
38
+ after do |example|
39
+ example.metadata[:response][:content] = {
40
+ "application/json" => {
41
+ example: JSON.parse(response.body, symbolize_names: true)
42
+ }
43
+ }
44
+ end
45
+
46
+ run_test!
47
+ end
48
+ end
49
+ end
50
+
51
+ path "<%= spec_routes[:show] %>" do
52
+ parameter name: "id", in: :path, type: :string, description: "id"
53
+
54
+ get("show <%= singular_table_name %>") do
55
+ consumes "application/json"
56
+
57
+ response(200, "successful") do
58
+ let(:id) { resource.id }
59
+
60
+ after do |example|
61
+ example.metadata[:response][:content] = {
62
+ "application/json" => {
63
+ example: JSON.parse(response.body, symbolize_names: true)
64
+ }
65
+ }
66
+ end
67
+ run_test!
68
+ end
69
+ end
70
+
71
+
72
+ patch("update <%= singular_table_name %>") do
73
+ consumes "application/json"
74
+
75
+ # You'll want to customize the parameter types...
76
+ parameter name: :<%= singular_table_name %>, in: :body, schema: {
77
+ type: :object,
78
+ properties: {
79
+ <% attributes.each do |attribute| -%>
80
+ <%= attribute.name %>: { type: :string },
81
+ <% end -%>
82
+ }
83
+ }
84
+
85
+ response(200, "successful") do
86
+ let(:id) { resource.id }
87
+ let(:<%= singular_table_name %>) { attributes_for(:<%= singular_table_name %>) }
88
+
89
+ after do |example|
90
+ example.metadata[:response][:content] = {
91
+ "application/json" => {
92
+ example: JSON.parse(response.body, symbolize_names: true)
93
+ }
94
+ }
95
+ end
96
+
97
+ run_test!
98
+ end
99
+ end
100
+
101
+ delete("delete plan") do
102
+ consumes "application/json"
103
+
104
+ response(204, "successful") do
105
+ let(:id) { resource.id }
106
+
107
+ run_test!
108
+ end
109
+ end
110
+ end
111
+ end
@@ -1,9 +1,10 @@
1
+ <% scope_namespacing do -%>
1
2
  class <%= class_name.pluralize %>Controller < ApplicationController
2
- before_action :set_<%= options['scope'].downcase %>
3
- before_action :set_<%= singular_name %>, only: %i[show update destroy]
3
+ before_action :set_<%= options['father'].downcase.singularize %>
4
+ before_action :set_<%= singular_name %>, only: %i[ show update destroy ]
4
5
 
5
6
  def index
6
- @<%= plural_name %> = @<%= options['scope'].downcase %>.<%= plural_name %>
7
+ @<%= plural_name %> = @<%= options['father'].downcase.singularize %>.<%= plural_name %>
7
8
  render json: @<%= plural_name %>, status: :ok
8
9
  end
9
10
 
@@ -12,7 +13,7 @@ class <%= class_name.pluralize %>Controller < ApplicationController
12
13
  end
13
14
 
14
15
  def create
15
- @<%= singular_name %> = @<%= options['scope'].downcase %>.<%= plural_name %>.create!(<%= singular_name %>_params)
16
+ @<%= singular_name %> = @<%= options['father'].downcase.singularize %>.<%= plural_name %>.create!(<%= singular_name %>_params)
16
17
  render json: @<%= singular_name %>, status: :created
17
18
  end
18
19
 
@@ -25,18 +26,18 @@ class <%= class_name.pluralize %>Controller < ApplicationController
25
26
  @<%= singular_name %>.destroy!
26
27
  end
27
28
 
28
-
29
29
  private
30
30
 
31
- def set_<%= options['scope'].downcase %>
32
- @<%= options['scope'].downcase %> = <%= options['scope'] %>.find(params[:<%= options['scope'].downcase %>_id])
31
+ def set_<%= options['father'].downcase.singularize %>
32
+ @<%= options['father'].downcase %> = <%= options['father'] %>.find(params[:<%= options['father'].downcase.singularize %>_id])
33
33
  end
34
34
 
35
35
  def set_<%= singular_name %>
36
- @<%= singular_name %> = @<%= options['scope'].downcase %>.<%= plural_name %>.find(params[:id])
36
+ @<%= singular_name %> = @<%= options['father'].downcase.singularize %>.<%= plural_name %>.find(params[:id])
37
37
  end
38
38
 
39
39
  def <%= singular_name %>_params
40
40
  params.require(:<%= singular_name %>).permit(<%= attributes.map { |a| ':' + a.name }.join(', ') %>)
41
41
  end
42
- end
42
+ end
43
+ <% end -%>
@@ -0,0 +1,4 @@
1
+ <% scope_namespacing do -%>
2
+ class <%= class_name.pluralize %>Controller < RestApiGenerator::ChildResourceController
3
+ end
4
+ <% end -%>
@@ -0,0 +1,4 @@
1
+ <% scope_namespacing do -%>
2
+ class <%= class_name.pluralize %>Controller < RestApiGenerator::ResourceController
3
+ end
4
+ <% end -%>
@@ -1,3 +1,4 @@
1
+ <% scope_namespacing do -%>
1
2
  class <%= class_name.pluralize %>Controller < ApplicationController
2
3
  before_action :set_<%= singular_name %>, only: %i[show update destroy]
3
4
 
@@ -34,3 +35,4 @@ class <%= class_name.pluralize %>Controller < ApplicationController
34
35
  params.require(:<%= singular_name %>).permit(<%= attributes.map { |a| ':' + a.name }.join(', ') %>)
35
36
  end
36
37
  end
38
+ <% end -%>
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require File.dirname(__FILE__) + "/rest_api_generator"
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "action_controller"
4
+
5
+ module RestApiGenerator
6
+ class ApplicationController < ActionController::Base
7
+ end
8
+ end
@@ -0,0 +1,97 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RestApiGenerator
4
+ class ChildResourceController < RestApiGenerator.parent_controller.constantize
5
+ include Orderable
6
+
7
+ before_action :set_parent_resource
8
+ before_action :set_resource, only: [:show, :update, :destroy]
9
+
10
+ def index
11
+ @resources = resources
12
+ @resources = @resources.filter_resource(params_for_filter) if resource_class.include?(Filterable)
13
+ @resources = @resources.order(ordering_params(params[:sort])) if params[:sort]
14
+ render json: @resources, status: :ok
15
+ end
16
+
17
+ def show
18
+ render json: @resource, status: :ok
19
+ end
20
+
21
+ def create
22
+ @resource = resources.create!(resource_created_params)
23
+ render json: @resource, status: :created
24
+ end
25
+
26
+ def update
27
+ @resource.update!(resource_updated_params)
28
+ render json: @resource, status: :ok
29
+ end
30
+
31
+ def destroy
32
+ @resource.destroy!
33
+ end
34
+
35
+ private
36
+
37
+ def resources
38
+ @parent_resource.send(resource_class.to_s.downcase.pluralize)
39
+ end
40
+
41
+ def parent_resource_class
42
+ parent_resource_by_controller_name
43
+ end
44
+
45
+ def resource_class
46
+ resource_by_controller_name
47
+ end
48
+
49
+ # Params
50
+ def resource_created_params
51
+ resource_params
52
+ end
53
+
54
+ def resource_updated_params
55
+ resource_params
56
+ end
57
+
58
+ def resource_params
59
+ params.require(resource_class.model_name.singular.to_sym).permit(resource_attributes)
60
+ end
61
+
62
+ def resource_attributes
63
+ resource_class.attribute_names.map(&:to_sym)
64
+ end
65
+
66
+ def params_for_filter
67
+ params.slice(*resource_class.filter_scopes)
68
+ end
69
+
70
+ # Before actions
71
+ def set_parent_resource
72
+ @parent_resource = parent_resource_class.find(parent_record_id)
73
+ end
74
+
75
+ def set_resource
76
+ @resource = resources.find(record_id)
77
+ end
78
+
79
+ # UsersController => User
80
+ def resource_by_controller_name(controller_name = self.class.to_s)
81
+ controller_name.split(Regexp.union(["Controller", "::"]))[-1].singularize.constantize
82
+ end
83
+
84
+ # Users::DevicesController => User
85
+ def parent_resource_by_controller_name(controller_name = self.class.to_s)
86
+ controller_name.split(Regexp.union(["Controller", "::"]))[-2].singularize.constantize
87
+ end
88
+
89
+ def parent_record_id
90
+ params.dig("#{parent_resource_class.to_s.downcase.singularize}_id")
91
+ end
92
+
93
+ def record_id
94
+ params.permit(:id)[:id]
95
+ end
96
+ end
97
+ end