Skip to content

Commit

Permalink
Merge pull request #21 from SwitchDreams/feature/rswag
Browse files Browse the repository at this point in the history
[CU-326fyxt] Feature/rswag
  • Loading branch information
PedroAugustoRamalhoDuarte authored Jan 6, 2023
2 parents e9a64d5 + ddfd4ac commit 38b7343
Show file tree
Hide file tree
Showing 20 changed files with 775 additions and 60 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -16,4 +16,6 @@ rest-api-generator.iml

rest-api-generator-*.gem

tmp
tmp

.tool-versions
19 changes: 17 additions & 2 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ AllCops:
NewCops: enable
TargetRubyVersion: 2.6
Exclude:
- 'rest-api-generator.gemspec'
- 'tmp/**/*'
- '**/*/bundle'

Expand All @@ -17,6 +16,7 @@ Style/Documentation:

Layout/LineLength:
Max: 120
Exclude: ["rest-api-generator.gemspec"]

Metrics/MethodLength:
Max: 17
Expand All @@ -28,4 +28,19 @@ RSpec/ExampleLength:
Max: 6

Metrics/BlockLength:
AllowedMethods: [ 'describe', 'context' ]
AllowedMethods: [ 'describe', 'context' ]


# DSL rubocop for rswag
RSpec:
Language:
Helpers:
- run_test!
ExampleGroups:
Regular:
- path
- get
Includes:
Examples:
- response

2 changes: 2 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,5 @@ gem "rake", "~> 13.0"
gem "rspec", "~> 3.0"

gem "switchcop"

gem "sqlite3"
23 changes: 22 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
rest-api-generator (0.1.1)
rest-api-generator (0.1.2)
rails (>= 5.0)

GEM
Expand Down Expand Up @@ -72,6 +72,8 @@ GEM
i18n (>= 1.6, < 2)
minitest (>= 5.1)
tzinfo (~> 2.0)
addressable (2.8.1)
public_suffix (>= 2.0.2, < 6.0)
ammeter (1.1.5)
activesupport (>= 3.0)
railties (>= 3.0)
Expand All @@ -94,6 +96,8 @@ GEM
i18n (1.12.0)
concurrent-ruby (~> 1.0)
json (2.6.3)
json-schema (3.0.0)
addressable (>= 2.8)
loofah (2.19.1)
crass (~> 1.0.2)
nokogiri (>= 1.5.9)
Expand Down Expand Up @@ -121,6 +125,7 @@ GEM
parallel (1.22.1)
parser (3.1.3.0)
ast (~> 2.4.1)
public_suffix (5.0.1)
racc (1.6.2)
rack (2.2.4)
rack-test (2.0.2)
Expand Down Expand Up @@ -176,6 +181,20 @@ GEM
rspec-mocks (~> 3.11)
rspec-support (~> 3.11)
rspec-support (3.12.0)
rswag (2.8.0)
rswag-api (= 2.8.0)
rswag-specs (= 2.8.0)
rswag-ui (= 2.8.0)
rswag-api (2.8.0)
railties (>= 3.1, < 7.1)
rswag-specs (2.8.0)
activesupport (>= 3.1, < 7.1)
json-schema (>= 2.2, < 4.0)
railties (>= 3.1, < 7.1)
rspec-core (>= 2.14)
rswag-ui (2.8.0)
actionpack (>= 3.1, < 7.1)
railties (>= 3.1, < 7.1)
rubocop (1.40.0)
json (~> 2.3)
parallel (~> 1.10)
Expand Down Expand Up @@ -227,6 +246,8 @@ DEPENDENCIES
rest-api-generator!
rspec (~> 3.0)
rspec-rails (~> 6.0.0)
rswag
rswag-specs
sqlite3
switchcop

Expand Down
24 changes: 21 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,14 +20,13 @@ Following [Switch Dreams's](https://www.switchdreams.com.br/]) coding practices,

- [Automatic rest api crud generation](#example)
- [Nested Resource](#nested-resource)
- [Modular error handler](#modular-error-handler)
- :goal_net: [Modular error handler](#modular-error-handler)
- :memo: [Automated documentation](#specsdocs)
- [Resource ordering](#ordering)
- [Resource filter](#filtering)

## Next Features

- Generate nested resource end-points 🚧
- Automated documentation 🚧 https://github.com/SwitchDreams/rest-api-generator/issues/12
- Serialization https://github.com/SwitchDreams/rest-api-generator/issues/14
https://github.com/SwitchDreams/rest-api-generator/issues/11
- Pagination https://github.com/SwitchDreams/rest-api-generator/issues/15
Expand Down Expand Up @@ -116,6 +115,7 @@ For a better experience you can override some methods from the
| father | Generate nested resource | nil | --father Users |
| scope | Scope the resource for other route or namespace organization | nil | --scope Api::V1 |
| eject | Eject the controller to high customization | false | true |
| spec | Choose the spec format. Current options: "rspec" or "rswag" | rspec | --spec rswag |

#### Scope

Expand Down Expand Up @@ -225,6 +225,24 @@ class CarsController < ApplicationController
end
```

#### Specs/Docs

The default generated spec for this gem is using plain rspec, but you can choose rswag, for scaffold you specs and docs
at the same time:

For this you need to setup https://github.com/rswag/rswag and you the following flag when generating resources.

```shell
rails g rest_api_generator:resource Car name:string color:string --spec rswag
```

This spec options work as generators too, so you can call them individually:

```shell
# rest_api_generator:spec:rswag or rest_api_generator:spec:rspec
rails g rest_api_generator:spec:rswag Car name:string color:string
```

### Resource Features

#### Modular Error Handler
Expand Down
42 changes: 42 additions & 0 deletions lib/generators/rest_api_generator/helpers.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,12 @@ module RestApiGenerator
module Helpers
attr_accessor :options, :attributes

API_CONTROLLER_DIR_PATH = "app/controllers"
API_TEST_DIR_PATH = "spec/requests"

private

# Columns handlers
def model_columns_for_attributes
class_name.singularize.constantize.columns.reject do |column|
column.name.to_s =~ /^(id|user_id|created_at|updated_at)$/
Expand All @@ -18,6 +22,42 @@ def editable_attributes
end
end

# Namespace scope

def scope_namespacing(&block)
content = capture(&block)
content = wrap_with_scope(content) if options["scope"].present? || options["father"].present?
concat(content)
end

def module_namespace
if options["scope"].present? && options["father"].present?
options["scope"] + "::" + options["father"]
else
options["scope"] + options["father"]
end
end

def wrap_with_scope(content)
content = indent(content).chomp
"module #{module_namespace}\n#{content}\nend\n"
end

# Paths handlers
def controller_path
"#{API_CONTROLLER_DIR_PATH}#{scope_path}/#{file_name.pluralize}_controller.rb"
end

def scope_path
return "" if options["scope"].blank? && options["father"].blank?

if options["scope"].present? && options["father"].present?
"/" + option_to_path(options["scope"]) + "/" + option_to_path(options["father"])
else
"/" + option_to_path(options["scope"]) + option_to_path(options["father"])
end
end

def option_to_path(option)
option.downcase.split("::").join("/")
end
Expand All @@ -35,6 +75,8 @@ def nested_routes
end

def initial_route
return "/#{plural_name}" if options["father"].blank? && options["scope"].blank?

scope_route_path + "/" + nested_routes
end

Expand Down
47 changes: 2 additions & 45 deletions lib/generators/rest_api_generator/resource_generator.rb
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,14 @@ class ResourceGenerator < Rails::Generators::NamedBase
class_option :eject, type: :boolean, default: false
class_option :scope, type: :string, default: ""
class_option :father, type: :string, default: ""

API_CONTROLLER_DIR_PATH = "app/controllers"
API_TEST_DIR_PATH = "spec/requests"
hook_for :spec, in: "rest_api_generator:spec", default: "rspec"

def create_service_file
create_model_files

# Create controller and specs
controller_path = "#{API_CONTROLLER_DIR_PATH}#{scope_path}/#{file_name.pluralize}_controller.rb"
controller_test_path = "#{API_TEST_DIR_PATH}#{scope_path}/#{file_name.pluralize}_controller_spec.rb"

template controller_template, controller_path
template spec_controller_template, controller_test_path

# Routes
if options["scope"].blank? && options["father"].blank?
route "resources :#{file_name.pluralize}"
else
Expand All @@ -36,35 +30,6 @@ def create_service_file

private

def scope_path
return "" if options["scope"].blank? && options["father"].blank?

if options["scope"].present? && options["father"].present?
"/" + option_to_path(options["scope"]) + "/" + option_to_path(options["father"])
else
"/" + option_to_path(options["scope"]) + option_to_path(options["father"])
end
end

def scope_namespacing(&block)
content = capture(&block)
content = wrap_with_scope(content) if options["scope"].present? || options["father"].present?
concat(content)
end

def module_namespace
if options["scope"].present? && options["father"].present?
options["scope"] + "::" + options["father"]
else
options["scope"] + options["father"]
end
end

def wrap_with_scope(content)
content = indent(content).chomp
"module #{module_namespace}\n#{content}\nend\n"
end

def controller_template
if options["eject"]
if options["father"].present?
Expand All @@ -79,14 +44,6 @@ def controller_template
end
end

def spec_controller_template
if options["father"].present?
"child_api_spec.rb"
else
"rest_api_spec.rb"
end
end

def create_model_files
g = Rails::Generators::ModelGenerator.new([file_name, build_model_attributes])
g.destination_root = destination_root
Expand Down
36 changes: 36 additions & 0 deletions lib/generators/rest_api_generator/spec/rspec_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
# frozen_string_literal: true

require "rails/generators"
require "generators/rest_api_generator/helpers"

module RestApiGenerator
module Spec
class RspecGenerator < Rails::Generators::NamedBase
include Helpers
source_root File.expand_path("templates", __dir__)

argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
class_option :eject, type: :boolean, default: false
class_option :scope, type: :string, default: ""
class_option :father, type: :string, default: ""

def create_service_file
template spec_controller_template, controller_test_path
end

private

def controller_test_path
"#{API_TEST_DIR_PATH}#{scope_path}/#{file_name.pluralize}_controller_spec.rb"
end

def spec_controller_template
if options["father"].present?
"rspec/resource_controller_spec.rb"
else
"rspec/nested_resource_controller_spec.rb"
end
end
end
end
end
54 changes: 54 additions & 0 deletions lib/generators/rest_api_generator/spec/rswag_generator.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
# frozen_string_literal: true

require "rails/generators"
require "generators/rest_api_generator/helpers"

module RestApiGenerator
module Spec
class RswagGenerator < Rails::Generators::NamedBase
include Helpers
source_root File.expand_path("templates", __dir__)

argument :attributes, type: :array, default: [], banner: "field[:type][:index] field[:type][:index]"
class_option :eject, type: :boolean, default: false
class_option :scope, type: :string, default: ""
class_option :father, type: :string, default: ""

def create_service_file
template spec_controller_template, controller_test_path
end

private

# Changes nested routes for rswag format
# Example: /cars/{car.id}/drivers/{id}
def nested_routes
return "" if options["father"].blank?

"#{options["father"].downcase.pluralize}/{#{options["father"].singularize.downcase}_id}/#{plural_name}"
end

def spec_routes
{
index: initial_route,
show: initial_route + "/{id}",
create: initial_route,
update: initial_route + "/{id}",
delete: initial_route + "/{id}",
}
end

def controller_test_path
"#{API_TEST_DIR_PATH}#{scope_path}/#{file_name.pluralize}_spec.rb"
end

def spec_controller_template
if options["father"].present?
"rswag/nested_resource_controller_spec.rb"
else
"rswag/resource_controller_spec.rb"
end
end
end
end
end
Loading

0 comments on commit 38b7343

Please sign in to comment.