diff --git a/.gitignore b/.gitignore index d688e03..a96771f 100644 --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,4 @@ **/*/*.sqlite3 **/*/public/assets *.swp +.idea diff --git a/rswag-specs/lib/generators/rspec/swagger/USAGE b/rswag-specs/lib/generators/rspec/swagger/USAGE new file mode 100644 index 0000000..bc354c4 --- /dev/null +++ b/rswag-specs/lib/generators/rspec/swagger/USAGE @@ -0,0 +1,9 @@ +Description: + This creates an RSpec request spec to define Swagger documentation for a + controller. It will create a test for each of the controller's methods. + +Example: + rails generate rspec:swagger V3::AccountsController + + This will create: + spec/requests/v3/accounts_spec.rb diff --git a/rswag-specs/lib/generators/rspec/swagger/swagger_generator.rb b/rswag-specs/lib/generators/rspec/swagger/swagger_generator.rb new file mode 100644 index 0000000..48f92aa --- /dev/null +++ b/rswag-specs/lib/generators/rspec/swagger/swagger_generator.rb @@ -0,0 +1,24 @@ +require 'rspec/rails/swagger/route_parser' +require 'rails/generators' + +module Rspec + module Generators + class SwaggerGenerator < ::Rails::Generators::NamedBase + source_root File.expand_path('../templates', __FILE__) + + def setup + @routes = RSpec::Rails::Swagger::RouteParser.new(controller_path).routes + end + + def create_spec_file + template 'spec.rb', File.join('spec/requests', "#{controller_path}_spec.rb") + end + + private + + def controller_path + file_path.chomp('_controller') + end + end + end +end diff --git a/rswag-specs/lib/generators/rspec/swagger/templates/spec.rb b/rswag-specs/lib/generators/rspec/swagger/templates/spec.rb new file mode 100644 index 0000000..75ff7b3 --- /dev/null +++ b/rswag-specs/lib/generators/rspec/swagger/templates/spec.rb @@ -0,0 +1,30 @@ +require 'swagger_helper' + +RSpec.describe '<%= controller_path %>', type: :request do + <% @routes.each do | template, path_item | %> + path '<%= template %>' do +<% unless path_item[:params].empty? -%> + # You'll want to customize the parameter types... + <% path_item[:params].each do |param| -%> + parameter '<%= param %>', in: :body, type: :string + <% end -%> +<% end -%> +<% path_item[:actions].each do | action, details | -%> + <%= action %>('<%= details[:summary] %>') do + response(200, 'successful') do +<% unless path_item[:params].empty? -%> + <% path_item[:params].each do |param| -%> + let(:<%= param %>) { '123' } + <% end -%> +<% end -%> + + after do |example| + example.metadata[:response][:examples] = { 'application/json' => JSON.parse(response.body, symbolize_names: true) } + end + run_test! + end + end +<% end -%> + end +<% end -%> +end diff --git a/rswag-specs/lib/rspec/rails/swagger/route_parser.rb b/rswag-specs/lib/rspec/rails/swagger/route_parser.rb new file mode 100644 index 0000000..9ab9513 --- /dev/null +++ b/rswag-specs/lib/rspec/rails/swagger/route_parser.rb @@ -0,0 +1,62 @@ +module RSpec + module Rails + module Swagger + class RouteParser + attr_reader :controller + + def initialize(controller) + @controller = controller + end + + def routes + ::Rails.application.routes.routes.select do |route| + route.defaults[:controller] == controller + end.reduce({}) do |tree, route| + path = path_from(route) + verb = verb_from(route) + tree[path] ||= { params: params_from(route), actions: {} } + tree[path][:actions][verb] = { summary: summary_from(route) } + tree + end + end + + private + + def path_from(route) + route.path.spec.to_s + .chomp('(.:format)') # Ignore any format suffix + .gsub(/:([^\/.?]+)/, '{\1}') # Convert :id to {id} + end + + def verb_from(route) + verb = route.verb + if verb.kind_of? String + verb.downcase + else + verb.source.gsub(/[$^]/, '').downcase + end + end + + def summary_from(route) + verb = route.requirements[:action] + noun = route.requirements[:controller].split('/').last.singularize + + # Apply a few customizations to make things more readable + case verb + when 'index' + verb = 'list' + noun = noun.pluralize + when 'destroy' + verb = 'delete' + end + + "#{verb} #{noun}" + end + + def params_from(route) + route.segments - ['format'] + end + end + end + end +end diff --git a/rswag-specs/lib/rswag/specs/railtie.rb b/rswag-specs/lib/rswag/specs/railtie.rb index 8deec2b..43e2302 100644 --- a/rswag-specs/lib/rswag/specs/railtie.rb +++ b/rswag-specs/lib/rswag/specs/railtie.rb @@ -5,6 +5,10 @@ module Rswag rake_tasks do load File.expand_path('../../../tasks/rswag-specs_tasks.rake', __FILE__) end + + generators do + require 'generators/rspec/swagger/swagger_generator.rb' + end end end end