├── test ├── helpers │ ├── .keep │ └── auth_helper.rb ├── mailers │ └── .keep ├── models │ └── .keep ├── controllers │ ├── .keep │ └── oas_rails │ │ └── oas_rails_controller_test.rb ├── dummy │ ├── log │ │ └── .keep │ ├── lib │ │ ├── assets │ │ │ └── .keep │ │ ├── json_web_token.rb │ │ └── oas.json │ ├── public │ │ ├── favicon.ico │ │ ├── apple-touch-icon.png │ │ ├── apple-touch-icon-precomposed.png │ │ ├── 500.html │ │ ├── 422.html │ │ └── 404.html │ ├── app │ │ ├── assets │ │ │ ├── images │ │ │ │ └── .keep │ │ │ ├── config │ │ │ │ └── manifest.js │ │ │ └── stylesheets │ │ │ │ └── application.css │ │ ├── models │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ ├── project.rb │ │ │ ├── application_record.rb │ │ │ └── user.rb │ │ ├── controllers │ │ │ ├── concerns │ │ │ │ └── .keep │ │ │ ├── application_controller.rb │ │ │ ├── users │ │ │ │ └── avatar_controller.rb │ │ │ ├── projects_controller.rb │ │ │ └── users_controller.rb │ │ ├── views │ │ │ └── layouts │ │ │ │ ├── mailer.text.erb │ │ │ │ ├── mailer.html.erb │ │ │ │ └── application.html.erb │ │ ├── helpers │ │ │ ├── users_helper.rb │ │ │ └── application_helper.rb │ │ ├── channels │ │ │ └── application_cable │ │ │ │ ├── channel.rb │ │ │ │ └── connection.rb │ │ ├── mailers │ │ │ └── application_mailer.rb │ │ └── jobs │ │ │ └── application_job.rb │ ├── bin │ │ ├── rake │ │ ├── rails │ │ └── setup │ ├── config │ │ ├── environment.rb │ │ ├── cable.yml │ │ ├── initializers │ │ │ ├── crs.rb │ │ │ ├── filter_parameter_logging.rb │ │ │ ├── permissions_policy.rb │ │ │ ├── assets.rb │ │ │ ├── inflections.rb │ │ │ ├── content_security_policy.rb │ │ │ └── oas_rails.rb │ │ ├── boot.rb │ │ ├── routes.rb │ │ ├── database.yml │ │ ├── locales │ │ │ └── en.yml │ │ ├── storage.yml │ │ ├── application.rb │ │ ├── puma.rb │ │ └── environments │ │ │ ├── test.rb │ │ │ ├── development.rb │ │ │ └── production.rb │ ├── test │ │ ├── models │ │ │ ├── user_test.rb │ │ │ └── project_test.rb │ │ ├── factories │ │ │ ├── projects.rb │ │ │ └── users.rb │ │ ├── fixtures │ │ │ ├── projects.yml │ │ │ └── users.yml │ │ ├── system │ │ │ └── users_test.rb │ │ └── controllers │ │ │ ├── users_controller_test.rb │ │ │ ├── projects_controller_test.rb │ │ │ └── users │ │ │ └── avatar_controller_test.rb │ ├── config.ru │ ├── Rakefile │ ├── db │ │ ├── migrate │ │ │ ├── 20240712004600_create_users.rb │ │ │ ├── 20240718211538_create_projects.rb │ │ │ ├── 20250419163914_add_different_kind_of_variables_to_projects.rb │ │ │ └── 20250528212011_create_active_storage_tables.active_storage.rb │ │ └── schema.rb │ └── README.md ├── integration │ ├── .keep │ └── navigation_test.rb ├── fixtures │ └── files │ │ ├── .keep │ │ ├── plain_text.txt │ │ └── avatar.jpg ├── lib │ └── oas_rails │ │ ├── utils_test.rb │ │ ├── active_record_example_finder_test.rb │ │ ├── builders │ │ ├── esquema_builder_test.rb │ │ └── oas_route_builder_test.rb │ │ ├── extractors │ │ └── route_extractor_test.rb │ │ └── configuration_test.rb ├── oas_rails_test.rb └── test_helper.rb ├── .ruby-version ├── app ├── assets │ ├── images │ │ └── oas_rails │ │ │ └── .keep │ ├── config │ │ └── oas_rails_manifest.js │ └── stylesheets │ │ └── oas_rails │ │ └── application.css ├── controllers │ ├── concerns │ │ └── .keep │ └── oas_rails │ │ ├── application_controller.rb │ │ └── oas_rails_controller.rb ├── views │ ├── oas_rails │ │ ├── test │ │ │ └── show.html.erb │ │ └── oas_rails │ │ │ ├── _rapidoc_style_parts.html.erb │ │ │ └── index.html.erb │ └── layouts │ │ └── oas_rails │ │ └── application.html.erb └── helpers │ └── oas_rails │ ├── test_helper.rb │ ├── application_helper.rb │ └── oas_rails_helper.rb ├── .release-please-manifest.json ├── lib ├── oas_rails │ ├── version.rb │ ├── engine.rb │ ├── configuration.rb │ ├── builders │ │ ├── oas_route_builder.rb │ │ └── esquema_builder.rb │ ├── active_record_example_finder.rb │ ├── extractors │ │ ├── route_extractor.rb │ │ └── render_response_extractor.rb │ └── utils.rb ├── generators │ └── oas_rails │ │ └── config │ │ ├── config_generator.rb │ │ └── templates │ │ └── oas_rails_initializer.rb └── oas_rails.rb ├── config └── routes.rb ├── Rakefile ├── .rubocop.yml ├── .gitignore ├── gemfiles ├── rails-7.1.gemfile ├── rails-7.2.gemfile └── rails-8.0.0.gemfile ├── bin └── rails ├── .github └── workflows │ ├── release_please.yml │ └── rubyonrails.yml ├── .release-please-config.json ├── LICENSE ├── oas_rails.gemspec ├── Gemfile ├── README.md ├── .rubocop_todo.yml ├── Gemfile.lock └── CHANGELOG.md /test/helpers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/mailers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/models/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.ruby-version: -------------------------------------------------------------------------------- 1 | 3.4.4 2 | -------------------------------------------------------------------------------- /test/controllers/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/log/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/integration/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/lib/assets/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/assets/images/oas_rails/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/public/favicon.ico: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/assets/images/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/app/models/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/fixtures/files/plain_text.txt: -------------------------------------------------------------------------------- 1 | test -------------------------------------------------------------------------------- /test/dummy/app/controllers/concerns/.keep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | {".":"1.3.2"} 2 | -------------------------------------------------------------------------------- /test/dummy/public/apple-touch-icon-precomposed.png: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /app/views/oas_rails/test/show.html.erb: -------------------------------------------------------------------------------- 1 | <%= @routes %> 2 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/mailer.text.erb: -------------------------------------------------------------------------------- 1 | <%= yield %> 2 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/users_helper.rb: -------------------------------------------------------------------------------- 1 | module UsersHelper 2 | end 3 | -------------------------------------------------------------------------------- /lib/oas_rails/version.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | VERSION = "1.3.2" 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/helpers/application_helper.rb: -------------------------------------------------------------------------------- 1 | module ApplicationHelper 2 | end 3 | -------------------------------------------------------------------------------- /app/assets/config/oas_rails_manifest.js: -------------------------------------------------------------------------------- 1 | //= link_directory ../stylesheets/oas_rails .css 2 | -------------------------------------------------------------------------------- /app/helpers/oas_rails/test_helper.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module TestHelper 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/models/project.rb: -------------------------------------------------------------------------------- 1 | class Project < ApplicationRecord 2 | belongs_to :user 3 | end 4 | -------------------------------------------------------------------------------- /config/routes.rb: -------------------------------------------------------------------------------- 1 | OasRails::Engine.routes.draw do 2 | get '(.:format)', to: 'oas_rails#index' 3 | end 4 | -------------------------------------------------------------------------------- /app/helpers/oas_rails/application_helper.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module ApplicationHelper 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/fixtures/files/avatar.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/a-chacon/oas_rails/HEAD/test/fixtures/files/avatar.jpg -------------------------------------------------------------------------------- /test/dummy/bin/rake: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require_relative "../config/boot" 3 | require "rake" 4 | Rake.application.run 5 | -------------------------------------------------------------------------------- /test/dummy/app/models/application_record.rb: -------------------------------------------------------------------------------- 1 | class ApplicationRecord < ActiveRecord::Base 2 | primary_abstract_class 3 | end 4 | -------------------------------------------------------------------------------- /test/dummy/app/assets/config/manifest.js: -------------------------------------------------------------------------------- 1 | //= link_tree ../images 2 | //= link_directory ../stylesheets .css 3 | //= link oas_rails_manifest.js 4 | -------------------------------------------------------------------------------- /test/dummy/app/channels/application_cable/channel.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Channel < ActionCable::Channel::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/channels/application_cable/connection.rb: -------------------------------------------------------------------------------- 1 | module ApplicationCable 2 | class Connection < ActionCable::Connection::Base 3 | end 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/app/mailers/application_mailer.rb: -------------------------------------------------------------------------------- 1 | class ApplicationMailer < ActionMailer::Base 2 | default from: "from@example.com" 3 | layout "mailer" 4 | end 5 | -------------------------------------------------------------------------------- /test/dummy/bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | APP_PATH = File.expand_path("../config/application", __dir__) 3 | require_relative "../config/boot" 4 | require "rails/commands" 5 | -------------------------------------------------------------------------------- /test/dummy/config/environment.rb: -------------------------------------------------------------------------------- 1 | # Load the Rails application. 2 | require_relative "application" 3 | 4 | # Initialize the Rails application. 5 | Rails.application.initialize! 6 | -------------------------------------------------------------------------------- /test/dummy/test/models/user_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UserTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/views/oas_rails/oas_rails/_rapidoc_style_parts.html.erb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/dummy/test/models/project_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ProjectTest < ActiveSupport::TestCase 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /app/controllers/oas_rails/application_controller.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | class ApplicationController < ActionController::Base 3 | protect_from_forgery with: :exception 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /test/integration/navigation_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class NavigationTest < ActionDispatch::IntegrationTest 4 | # test "the truth" do 5 | # assert true 6 | # end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/test/factories/projects.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :project do 3 | name { Faker::Name.name } 4 | description { Faker::Lorem.paragraph } 5 | user 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/config.ru: -------------------------------------------------------------------------------- 1 | # This file is used by Rack-based servers to start the application. 2 | 3 | require_relative "config/environment" 4 | 5 | run Rails.application 6 | Rails.application.load_server 7 | -------------------------------------------------------------------------------- /test/dummy/test/factories/users.rb: -------------------------------------------------------------------------------- 1 | FactoryBot.define do 2 | factory :user do 3 | name { Faker::Name.name } 4 | email { Faker::Internet.email } 5 | password { "Test123" } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/setup" 2 | 3 | APP_RAKEFILE = File.expand_path("test/dummy/Rakefile", __dir__) 4 | load "rails/tasks/engine.rake" 5 | 6 | load "rails/tasks/statistics.rake" 7 | 8 | require "bundler/gem_tasks" 9 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/projects.yml: -------------------------------------------------------------------------------- 1 | one: 2 | name: project1 3 | description: project1 description 4 | user: one 5 | 6 | two: 7 | name: project2 8 | description: project2 description 9 | user: two 10 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | inherit_from: .rubocop_todo.yml 2 | AllCops: 3 | TargetRubyVersion: 4 | NewCops: enable 5 | SuggestExtensions: false 6 | Exclude: 7 | - "gemfiles/*" 8 | - "vendor/**/*" 9 | - "test/dummy/db/schema.rb" 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /doc/ 3 | /log/*.log 4 | /pkg/ 5 | /tmp/ 6 | /test/dummy/db/*.sqlite3 7 | /test/dummy/db/*.sqlite3-* 8 | /test/dummy/log/*.log 9 | /test/dummy/storage/ 10 | /test/dummy/tmp/ 11 | /docs/book/ 12 | 13 | .archivador* 14 | -------------------------------------------------------------------------------- /test/dummy/config/cable.yml: -------------------------------------------------------------------------------- 1 | development: 2 | adapter: async 3 | 4 | test: 5 | adapter: test 6 | 7 | production: 8 | adapter: redis 9 | url: <%= ENV.fetch("REDIS_URL") { "redis://localhost:6379/1" } %> 10 | channel_prefix: dummy_production 11 | -------------------------------------------------------------------------------- /test/dummy/Rakefile: -------------------------------------------------------------------------------- 1 | # Add your own tasks in files placed in lib/tasks ending in .rake, 2 | # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake. 3 | 4 | require_relative "config/application" 5 | 6 | Rails.application.load_tasks 7 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/crs.rb: -------------------------------------------------------------------------------- 1 | # config/initializers/cors.rb 2 | 3 | Rails.application.config.middleware.insert_before 0, Rack::Cors do 4 | allow do 5 | origins '*' 6 | resource '*', headers: :any, methods: %i[get post patch put] 7 | end 8 | end 9 | -------------------------------------------------------------------------------- /test/dummy/config/boot.rb: -------------------------------------------------------------------------------- 1 | # Set up gems listed in the Gemfile. 2 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__) 3 | 4 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) 5 | $LOAD_PATH.unshift File.expand_path("../../../lib", __dir__) 6 | -------------------------------------------------------------------------------- /lib/oas_rails/engine.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | class Engine < ::Rails::Engine 3 | isolate_namespace OasRails 4 | 5 | config.app_middleware.use( 6 | Rack::Static, 7 | urls: ["/oas-rails-assets"], 8 | root: OasRails::Engine.root.join("public") 9 | ) 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20240712004600_create_users.rb: -------------------------------------------------------------------------------- 1 | class CreateUsers < ActiveRecord::Migration[7.0] 2 | def change 3 | create_table :users do |t| 4 | t.string :name 5 | t.string :email 6 | t.string :password_digest 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/test/fixtures/users.yml: -------------------------------------------------------------------------------- 1 | <% password_digest = BCrypt::Password.create("password") %> 2 | 3 | one: 4 | name: user1 5 | email: user1@gmail.com 6 | password_digest: <%= password_digest %> 7 | 8 | two: 9 | name: Luis 10 | email: luis@gmail.com 11 | password_digest: <%= password_digest %> 12 | -------------------------------------------------------------------------------- /test/helpers/auth_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module AuthHelper 4 | def assign_token(user_id) 5 | token = JsonWebToken.encode(user_id:) 6 | default_headers['Authorization'] = "Bearer #{token}" 7 | end 8 | 9 | def default_headers 10 | @default_headers ||= {} 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/app/jobs/application_job.rb: -------------------------------------------------------------------------------- 1 | class ApplicationJob < ActiveJob::Base 2 | # Automatically retry jobs that encountered a deadlock 3 | # retry_on ActiveRecord::Deadlocked 4 | 5 | # Most jobs are safe to ignore if the underlying records are no longer available 6 | # discard_on ActiveJob::DeserializationError 7 | end 8 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/mailer.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20240718211538_create_projects.rb: -------------------------------------------------------------------------------- 1 | class CreateProjects < ActiveRecord::Migration[7.0] 2 | def change 3 | create_table :projects do |t| 4 | t.string :name 5 | t.text :description 6 | t.references :user, null: false, foreign_key: true 7 | 8 | t.timestamps 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /app/views/layouts/oas_rails/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= OasRails.config.info.title %> 5 | <%= csrf_meta_tags %> 6 | <%= csp_meta_tag %> 7 | 8 | 9 | 10 | 11 | <%= yield %> 12 | 13 | 14 | -------------------------------------------------------------------------------- /lib/generators/oas_rails/config/config_generator.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module Generators 3 | class ConfigGenerator < Rails::Generators::Base 4 | source_root File.expand_path('templates', __dir__) 5 | 6 | def copy_initializer_file 7 | template "oas_rails_initializer.rb", "config/initializers/oas_rails.rb" 8 | end 9 | end 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /test/dummy/app/views/layouts/application.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Dummy 5 | 6 | <%= csrf_meta_tags %> 7 | <%= csp_meta_tag %> 8 | 9 | <%= stylesheet_link_tag "application" %> 10 | 11 | 12 | 13 | <%= yield %> 14 | 15 | 16 | -------------------------------------------------------------------------------- /test/dummy/lib/json_web_token.rb: -------------------------------------------------------------------------------- 1 | class JsonWebToken 2 | SECRET_KEY = Rails.application.credentials.secret_key_base.to_s 3 | 4 | def self.encode(payload, exp = 24.hours.from_now) 5 | payload[:exp] = exp.to_i 6 | JWT.encode(payload, SECRET_KEY) 7 | end 8 | 9 | def self.decode(token) 10 | decoded = JWT.decode(token, SECRET_KEY)[0] 11 | HashWithIndifferentAccess.new decoded 12 | end 13 | end 14 | -------------------------------------------------------------------------------- /test/dummy/config/routes.rb: -------------------------------------------------------------------------------- 1 | Rails.application.routes.draw do 2 | post '/users/login', to: 'users#login' 3 | get "/users/new", to: "users#new" # This route is for testing purpose 4 | resources :users, shallow: true do 5 | resources :projects 6 | resource :avatar, only: [:update], controller: 'users/avatar' 7 | end 8 | match 'users', to: 'users#index', via: [:get, :options] 9 | mount OasRails::Engine => '/docs' 10 | end 11 | -------------------------------------------------------------------------------- /test/controllers/oas_rails/oas_rails_controller_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module OasRails 4 | class OasRailsControllerTest < ActionDispatch::IntegrationTest 5 | include Engine.routes.url_helpers 6 | 7 | test 'should return the front' do 8 | get '/docs' 9 | assert_response :ok 10 | end 11 | 12 | test 'should return the oas' do 13 | get '/docs.json' 14 | assert_response :ok 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /gemfiles/rails-7.1.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | gem 'rails', '~> 7.1.0' 5 | 6 | gem 'oas_core' 7 | 8 | gem 'easy_talk_two', '~> 1.0' 9 | 10 | gem 'puma' 11 | 12 | gem 'sqlite3', "~> 1.4" 13 | 14 | gem 'sprockets-rails' 15 | 16 | gem 'rack-cors' 17 | 18 | group :development, :test do 19 | gem "bcrypt", "~> 3.1.7" 20 | 21 | gem 'factory_bot_rails' 22 | 23 | gem 'jwt' 24 | 25 | gem 'faker' 26 | end 27 | -------------------------------------------------------------------------------- /gemfiles/rails-7.2.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | gem 'rails', '~> 7.2.0' 5 | 6 | gem 'oas_core' 7 | 8 | gem 'easy_talk_two', '~> 1.0' 9 | 10 | gem 'puma' 11 | 12 | gem 'sqlite3', "~> 1.4" 13 | 14 | gem 'sprockets-rails' 15 | 16 | gem 'rack-cors' 17 | 18 | group :development, :test do 19 | gem "bcrypt", "~> 3.1.7" 20 | 21 | gem 'factory_bot_rails' 22 | 23 | gem 'jwt' 24 | 25 | gem 'faker' 26 | end 27 | -------------------------------------------------------------------------------- /gemfiles/rails-8.0.0.gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | gem 'rails', '~> 8.0.0' 5 | 6 | gem 'oas_core' 7 | 8 | gem 'easy_talk_two', '~> 1.0' 9 | 10 | gem 'puma' 11 | 12 | gem 'sqlite3', ">= 2.1" 13 | 14 | gem 'sprockets-rails' 15 | 16 | gem 'rack-cors' 17 | 18 | group :development, :test do 19 | gem "bcrypt", "~> 3.1.7" 20 | 21 | gem 'factory_bot_rails' 22 | 23 | gem 'jwt' 24 | 25 | gem 'faker' 26 | end 27 | -------------------------------------------------------------------------------- /test/dummy/README.md: -------------------------------------------------------------------------------- 1 | # README 2 | 3 | This README would normally document whatever steps are necessary to get the 4 | application up and running. 5 | 6 | Things you may want to cover: 7 | 8 | * Ruby version 9 | 10 | * System dependencies 11 | 12 | * Configuration 13 | 14 | * Database creation 15 | 16 | * Database initialization 17 | 18 | * How to run the test suite 19 | 20 | * Services (job queues, cache servers, search engines, etc.) 21 | 22 | * Deployment instructions 23 | 24 | * ... 25 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/application_controller.rb: -------------------------------------------------------------------------------- 1 | class ApplicationController < ActionController::API 2 | def authorize! 3 | header = request.headers['Authorization'] 4 | header = header.split.last if header 5 | begin 6 | @decoded = JsonWebToken.decode(header) 7 | @current_user = User.find(@decoded[:user_id]) 8 | rescue ActiveRecord::RecordNotFound, JWT::DecodeError => e 9 | render json: { errors: e.message }, status: :unauthorized 10 | end 11 | end 12 | end 13 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/filter_parameter_logging.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Configure parameters to be partially matched (e.g. passw matches password) and filtered from the log file. 4 | # Use this to limit dissemination of sensitive information. 5 | # See the ActiveSupport::ParameterFilter documentation for supported notations and behaviors. 6 | Rails.application.config.filter_parameters += [ 7 | :passw, :secret, :token, :_key, :crypt, :salt, :certificate, :otp, :ssn 8 | ] 9 | -------------------------------------------------------------------------------- /app/controllers/oas_rails/oas_rails_controller.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | class OasRailsController < ApplicationController 3 | # Include URL help if the layout is a user-customized layout. 4 | include Rails.application.routes.url_helpers 5 | 6 | def index 7 | respond_to do |format| 8 | format.html { render "index", layout: OasRails.config.layout } 9 | format.json do 10 | render json: OasRails.build.to_json, status: :ok 11 | end 12 | end 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20250419163914_add_different_kind_of_variables_to_projects.rb: -------------------------------------------------------------------------------- 1 | class AddDifferentKindOfVariablesToProjects < ActiveRecord::Migration[7.0] 2 | def change 3 | add_column :projects, :active, :boolean, default: false 4 | add_column :projects, :start_date, :date 5 | add_column :projects, :end_date, :datetime 6 | add_column :projects, :budget, :decimal, precision: 10, scale: 2 7 | add_column :projects, :priority, :integer 8 | add_column :projects, :status, :string, default: "draft" 9 | end 10 | end 11 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/permissions_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide HTTP permissions policy. For further 4 | # information see: https://developers.google.com/web/updates/2018/06/feature-policy 5 | 6 | # Rails.application.config.permissions_policy do |policy| 7 | # policy.camera :none 8 | # policy.gyroscope :none 9 | # policy.microphone :none 10 | # policy.usb :none 11 | # policy.fullscreen :self 12 | # policy.payment :self, "https://secure.example.com" 13 | # end 14 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/assets.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Version of your assets, change this if you want to expire all your assets. 4 | Rails.application.config.assets.version = "1.0" 5 | 6 | # Add additional assets to the asset load path. 7 | # Rails.application.config.assets.paths << Emoji.images_path 8 | 9 | # Precompile additional assets. 10 | # application.js, application.css, and all non-JS/CSS in the app/assets 11 | # folder are already added. 12 | # Rails.application.config.assets.precompile += %w( admin.js admin.css ) 13 | -------------------------------------------------------------------------------- /bin/rails: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | # This command will automatically be run when you run "rails" with Rails gems 3 | # installed from the root of your application. 4 | 5 | ENGINE_ROOT = File.expand_path("..", __dir__) 6 | ENGINE_PATH = File.expand_path("../lib/oas_rails/engine", __dir__) 7 | APP_PATH = File.expand_path("../test/dummy/config/application", __dir__) 8 | 9 | # Set up gems listed in the Gemfile. 10 | ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__) 11 | require "bundler/setup" if File.exist?(ENV["BUNDLE_GEMFILE"]) 12 | 13 | require "rails/all" 14 | require "rails/engine/commands" 15 | -------------------------------------------------------------------------------- /test/dummy/app/models/user.rb: -------------------------------------------------------------------------------- 1 | class User < ApplicationRecord 2 | has_secure_password 3 | validates :name, presence: true 4 | validates :email, presence: true, uniqueness: true 5 | 6 | has_one_attached :avatar 7 | 8 | validate :valid_avatar 9 | 10 | def valid_avatar 11 | return unless avatar.attached? 12 | 13 | errors.add(:avatar, 'size must be less than 5MB') if avatar.blob.byte_size > 5.megabytes 14 | 15 | return if avatar.content_type.in?(%w[image/jpeg image/png image/gif image/svg+xml image/webp]) 16 | 17 | errors.add(:avatar, 'must be a JPEG, PNG, GIF, SVG or WEBP image') 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /app/views/oas_rails/oas_rails/index.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | <%= OasRails.config.info.title %> 5 | <%= csrf_meta_tags %> 6 | <%= csp_meta_tag %> 7 | 8 | 9 | 10 | 11 | 12 | > 13 | <% if rapidoc_logo_url %> 14 | 18 | <% end %> 19 | 20 | 21 | <%= render partial: 'rapidoc_style_parts' %> 22 | 23 | 24 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/inflections.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Add new inflection rules using the following format. Inflections 4 | # are locale specific, and you may define rules for as many different 5 | # locales as you wish. All of these examples are active by default: 6 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 7 | # inflect.plural /^(ox)$/i, "\\1en" 8 | # inflect.singular /^(ox)en/i, "\\1" 9 | # inflect.irregular "person", "people" 10 | # inflect.uncountable %w( fish sheep ) 11 | # end 12 | 13 | # These inflection rules are supported but not enabled by default: 14 | # ActiveSupport::Inflector.inflections(:en) do |inflect| 15 | # inflect.acronym "RESTful" 16 | # end 17 | -------------------------------------------------------------------------------- /test/dummy/config/database.yml: -------------------------------------------------------------------------------- 1 | # SQLite. Versions 3.8.0 and up are supported. 2 | # gem install sqlite3 3 | # 4 | # Ensure the SQLite 3 gem is defined in your Gemfile 5 | # gem "sqlite3" 6 | # 7 | default: &default 8 | adapter: sqlite3 9 | pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %> 10 | timeout: 5000 11 | 12 | development: 13 | <<: *default 14 | database: storage/development.sqlite3 15 | 16 | # Warning: The database defined as "test" will be erased and 17 | # re-generated from your development database when you run "rake". 18 | # Do not set this db to the same as development or production. 19 | test: 20 | <<: *default 21 | database: storage/test.sqlite3 22 | 23 | production: 24 | <<: *default 25 | database: storage/production.sqlite3 26 | -------------------------------------------------------------------------------- /app/assets/stylesheets/oas_rails/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /test/dummy/app/assets/stylesheets/application.css: -------------------------------------------------------------------------------- 1 | /* 2 | * This is a manifest file that'll be compiled into application.css, which will include all the files 3 | * listed below. 4 | * 5 | * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, 6 | * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path. 7 | * 8 | * You're free to add application-wide styles to this file and they'll appear at the bottom of the 9 | * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS 10 | * files in this directory. Styles in this file should be added after the last require_* statement. 11 | * It is generally better to create a new file per style scope. 12 | * 13 | *= require_tree . 14 | *= require_self 15 | */ 16 | -------------------------------------------------------------------------------- /test/lib/oas_rails/utils_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | module OasRails 4 | class UtilsTest < ActiveSupport::TestCase 5 | test "detect_test_framework" do 6 | result = OasRails::Utils.detect_test_framework 7 | assert_equal :factory_bot, result 8 | end 9 | 10 | test "hash to schema" do 11 | result = OasRails::Utils.hash_to_json_schema({ name: "String", coords: "Array" }) 12 | assert result.key? :properties 13 | assert result.key? :type 14 | end 15 | 16 | test "status to integer with unprocessable entity/content" do 17 | assert_equal 422, OasRails::Utils.status_to_integer(:unprocessable_entity) 18 | assert_equal 422, OasRails::Utils.status_to_integer(:unprocessable_content) if Gem.loaded_specs['rack'].version > Gem::Version.create('3.1') 19 | end 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /.github/workflows/release_please.yml: -------------------------------------------------------------------------------- 1 | on: 2 | push: 3 | branches: 4 | - main 5 | 6 | permissions: 7 | contents: write 8 | pull-requests: write 9 | id-token: write 10 | 11 | name: release-please 12 | 13 | jobs: 14 | release-please: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: googleapis/release-please-action@v4 18 | id: release 19 | with: 20 | token: ${{ secrets.RELEASE_PLEASE_TOKEN }} 21 | config-file: .release-please-config.json 22 | - uses: actions/checkout@v4 23 | if: ${{ steps.release.outputs.release_created }} 24 | - name: Set up Ruby 25 | uses: ruby/setup-ruby@v1 26 | with: 27 | bundler-cache: true 28 | if: ${{ steps.release.outputs.release_created }} 29 | - uses: rubygems/release-gem@v1 30 | if: ${{ steps.release.outputs.release_created }} 31 | -------------------------------------------------------------------------------- /.release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "release-type": "ruby", 3 | "changelog-sections": [ 4 | { "type": "feat", "section": "Features" }, 5 | { "type": "feature", "section": "Features" }, 6 | { "type": "fix", "section": "Bug Fixes" }, 7 | { "type": "perf", "section": "Performance Improvements" }, 8 | { "type": "revert", "section": "Reverts" }, 9 | { "type": "docs", "section": "Documentation" }, 10 | { "type": "style", "section": "Styles", "hidden": true }, 11 | { "type": "chore", "section": "Miscellaneous Chores", "hidden": true }, 12 | { "type": "refactor", "section": "Code Refactoring" }, 13 | { "type": "test", "section": "Tests" }, 14 | { "type": "build", "section": "Build System", "hidden": true }, 15 | { "type": "ci", "section": "Continuous Integration", "hidden": true } 16 | ], 17 | "packages": { 18 | ".": { 19 | "release-type": "ruby", 20 | "package-name": "oas_rails", 21 | "version-file": "lib/oas_rails/version.rb" 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2025 a-chacon 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 4 | 5 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 6 | 7 | THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 8 | -------------------------------------------------------------------------------- /test/dummy/config/locales/en.yml: -------------------------------------------------------------------------------- 1 | # Files in the config/locales directory are used for internationalization and 2 | # are automatically loaded by Rails. If you want to use locales other than 3 | # English, add the necessary files in this directory. 4 | # 5 | # To use the locales, use `I18n.t`: 6 | # 7 | # I18n.t "hello" 8 | # 9 | # In views, this is aliased to just `t`: 10 | # 11 | # <%= t("hello") %> 12 | # 13 | # To use a different locale, set it with `I18n.locale`: 14 | # 15 | # I18n.locale = :es 16 | # 17 | # This would use the information in config/locales/es.yml. 18 | # 19 | # To learn more about the API, please read the Rails Internationalization guide 20 | # at https://guides.rubyonrails.org/i18n.html. 21 | # 22 | # Be aware that YAML interprets the following case-insensitive strings as 23 | # booleans: `true`, `false`, `on`, `off`, `yes`, `no`. Therefore, these strings 24 | # must be quoted to be interpreted as strings. For example: 25 | # 26 | # en: 27 | # "yes": yup 28 | # enabled: "ON" 29 | 30 | en: 31 | hello: "Hello world" 32 | -------------------------------------------------------------------------------- /test/oas_rails_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class OasRailsTest < ActiveSupport::TestCase 4 | test "it has a version number" do 5 | assert OasRails::VERSION 6 | end 7 | 8 | class MockRouteExtractor < OasRails::Extractors::RouteExtractor 9 | def self.host_routes 10 | super.select { |route| route.controller.include?("users") } 11 | end 12 | end 13 | 14 | test "it builds with the configured route extractor" do 15 | config = OasRails::Configuration.new 16 | config.route_extractor = MockRouteExtractor 17 | 18 | OasRails.stub(:config, config) do 19 | custom_routes = OasRails.config.route_extractor.host_routes 20 | assert custom_routes.all? { |route| route.controller.include?("users") }, "Expected to get user routes only" 21 | 22 | oas_core_mock = Minitest::Mock.new 23 | oas_core_mock.expect(:build, nil, [custom_routes, { oas_source: {} }]) 24 | OasCore.stub(:build, ->(*args) { oas_core_mock.build(*args) }) do 25 | OasRails.build 26 | end 27 | oas_core_mock.verify 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /test/dummy/bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | require "fileutils" 3 | 4 | # path to your application root. 5 | APP_ROOT = File.expand_path("..", __dir__) 6 | 7 | def system!(*args) 8 | system(*args, exception: true) 9 | end 10 | 11 | FileUtils.chdir APP_ROOT do 12 | # This script is a way to set up or update your development environment automatically. 13 | # This script is idempotent, so that you can run it at any time and get an expectable outcome. 14 | # Add necessary setup steps to this file. 15 | 16 | puts "== Installing dependencies ==" 17 | system! "gem install bundler --conservative" 18 | system("bundle check") || system!("bundle install") 19 | 20 | # puts "\n== Copying sample files ==" 21 | # unless File.exist?("config/database.yml") 22 | # FileUtils.cp "config/database.yml.sample", "config/database.yml" 23 | # end 24 | 25 | puts "\n== Preparing database ==" 26 | system! "bin/rails db:prepare" 27 | 28 | puts "\n== Removing old logs and tempfiles ==" 29 | system! "bin/rails log:clear tmp:clear" 30 | 31 | puts "\n== Restarting application server ==" 32 | system! "bin/rails restart" 33 | end 34 | -------------------------------------------------------------------------------- /test/dummy/test/system/users_test.rb: -------------------------------------------------------------------------------- 1 | require "application_system_test_case" 2 | 3 | class UsersTest < ApplicationSystemTestCase 4 | setup do 5 | @user = users(:one) 6 | end 7 | 8 | test "visiting the index" do 9 | visit users_url 10 | assert_selector "h1", text: "Users" 11 | end 12 | 13 | test "should create user" do 14 | visit users_url 15 | click_on "New user" 16 | 17 | fill_in "Email", with: @user.email 18 | fill_in "Name", with: @user.name 19 | click_on "Create User" 20 | 21 | assert_text "User was successfully created" 22 | click_on "Back" 23 | end 24 | 25 | test "should update User" do 26 | visit user_url(@user) 27 | click_on "Edit this user", match: :first 28 | 29 | fill_in "Email", with: @user.email 30 | fill_in "Name", with: @user.name 31 | click_on "Update User" 32 | 33 | assert_text "User was successfully updated" 34 | click_on "Back" 35 | end 36 | 37 | test "should destroy User" do 38 | visit user_url(@user) 39 | click_on "Destroy this user", match: :first 40 | 41 | assert_text "User was successfully destroyed" 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/users/avatar_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Users 4 | class AvatarController < ApplicationController 5 | before_action :authorize! 6 | before_action :set_user 7 | 8 | # @summary Upload an avatar 9 | # @tags 3. Third 10 | # 11 | # Uploads an avatar for the user. The avatar must be a valid image file (JPEG, PNG, GIF, SVG or WEBP). 12 | # @request_body Avatar Image (multipart/form-data) [!Hash{avatar: !File}] 13 | # @request_body_example Test Avatar [JSON{ "avatar": 'path/to/avatar.jpg'}] 14 | # @response Avatar uploaded successfully(200) [Hash{success: Boolean, message: String}] 15 | def update 16 | if @user.avatar.attach(avatar_params) 17 | render json: { success: true, message: 'Avatar uploaded successfully' }, status: :ok 18 | else 19 | render json: { success: false, errors: @user.errors }, status: :unprocessable_entity 20 | end 21 | end 22 | 23 | private 24 | 25 | def set_user 26 | @user = User.find(params[:user_id]) 27 | end 28 | 29 | def avatar_params 30 | params.require(:avatar) 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/content_security_policy.rb: -------------------------------------------------------------------------------- 1 | # Be sure to restart your server when you modify this file. 2 | 3 | # Define an application-wide content security policy. 4 | # See the Securing Rails Applications Guide for more information: 5 | # https://guides.rubyonrails.org/security.html#content-security-policy-header 6 | 7 | # Rails.application.configure do 8 | # config.content_security_policy do |policy| 9 | # policy.default_src :self, :https 10 | # policy.font_src :self, :https, :data 11 | # policy.img_src :self, :https, :data 12 | # policy.object_src :none 13 | # policy.script_src :self, :https 14 | # policy.style_src :self, :https 15 | # # Specify URI for violation reports 16 | # # policy.report_uri "/csp-violation-report-endpoint" 17 | # end 18 | # 19 | # # Generate session nonces for permitted importmap, inline scripts, and inline styles. 20 | # config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s } 21 | # config.content_security_policy_nonce_directives = %w(script-src style-src) 22 | # 23 | # # Report violations without enforcing the policy. 24 | # # config.content_security_policy_report_only = true 25 | # end 26 | -------------------------------------------------------------------------------- /oas_rails.gemspec: -------------------------------------------------------------------------------- 1 | require_relative 'lib/oas_rails/version' 2 | 3 | Gem::Specification.new do |spec| 4 | spec.name = 'oas_rails' 5 | spec.version = OasRails::VERSION 6 | spec.authors = ['a-chacon'] 7 | spec.email = ['andres.ch@protonmail.com'] 8 | spec.homepage = 'https://github.com/a-chacon/oas_rails' 9 | spec.summary = 'OasRails is a Rails engine for generating automatic interactive documentation for your Rails APIs.' 10 | spec.description = 11 | 'OasRails is a Rails engine for generating automatic interactive documentation for your Rails APIs. It generates an OAS 3.1 document and displays it using RapiDoc.' 12 | 13 | spec.license = 'MIT' 14 | 15 | spec.metadata['homepage_uri'] = spec.homepage 16 | # spec.metadata['source_code_uri'] = 'https://github.com/a-chacon/oas_rails' 17 | # spec.metadata['changelog_uri'] = 'https://github.com/a-chacon/oas_rails' 18 | 19 | spec.files = Dir.chdir(File.expand_path(__dir__)) do 20 | Dir['{app,config,db,lib,public}/**/*', 'LICENSE', 'Rakefile', 'README.md'] 21 | end 22 | 23 | spec.required_ruby_version = ">= 3.1" 24 | 25 | spec.add_dependency 'easy_talk_two', '~> 1.1.2' 26 | spec.add_dependency 'oas_core', '>= 1.1.0' 27 | end 28 | -------------------------------------------------------------------------------- /test/dummy/test/controllers/users_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class UsersControllerTest < ActionDispatch::IntegrationTest 4 | include AuthHelper 5 | 6 | setup do 7 | @user = users(:one) 8 | assign_token(@user.id) 9 | end 10 | 11 | test "should get index" do 12 | get users_url, headers: default_headers 13 | assert_response :success 14 | end 15 | 16 | test "should create user" do 17 | assert_difference("User.count") do 18 | post users_url, params: { user: { email: "new_user@gmail.com", name: "new_user", password: "password" } }, headers: default_headers 19 | end 20 | 21 | assert_response :success 22 | end 23 | 24 | test "should show user" do 25 | get user_url(@user), headers: default_headers 26 | assert_response :success 27 | end 28 | 29 | test "should update user" do 30 | patch user_url(@user), params: { user: { email: @user.email, name: @user.name } }, headers: default_headers 31 | assert_response :success 32 | end 33 | 34 | test "should destroy user" do 35 | assert_difference("User.count", -1) do 36 | delete user_url(@user), headers: default_headers 37 | end 38 | 39 | assert_redirected_to users_url 40 | end 41 | end 42 | -------------------------------------------------------------------------------- /test/dummy/test/controllers/projects_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | class ProjectsControllerTest < ActionDispatch::IntegrationTest 4 | setup do 5 | @user = users(:one) 6 | @project = projects(:one) 7 | end 8 | 9 | test "should get index" do 10 | get user_projects_url(@user), as: :json 11 | assert_response :success 12 | end 13 | 14 | test "should create project" do 15 | assert_difference("Project.count") do 16 | post user_projects_url(@user), params: { project: { description: "new project", name: "new_project", user_id: @user.id } }, as: :json 17 | end 18 | 19 | assert_response :created 20 | end 21 | 22 | test "should show project" do 23 | get project_url(@project), as: :json 24 | assert_response :success 25 | end 26 | 27 | test "should update project" do 28 | patch project_url(@project), params: { project: { description: @project.description, name: @project.name, user_id: @project.user_id } }, as: :json 29 | assert_response :success 30 | end 31 | 32 | test "should destroy project" do 33 | assert_difference("Project.count", -1) do 34 | delete project_url(@project), as: :json 35 | end 36 | 37 | assert_response :no_content 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /test/dummy/config/storage.yml: -------------------------------------------------------------------------------- 1 | test: 2 | service: Disk 3 | root: <%= Rails.root.join("tmp/storage") %> 4 | 5 | local: 6 | service: Disk 7 | root: <%= Rails.root.join("storage") %> 8 | 9 | # Use bin/rails credentials:edit to set the AWS secrets (as aws:access_key_id|secret_access_key) 10 | # amazon: 11 | # service: S3 12 | # access_key_id: <%= Rails.application.credentials.dig(:aws, :access_key_id) %> 13 | # secret_access_key: <%= Rails.application.credentials.dig(:aws, :secret_access_key) %> 14 | # region: us-east-1 15 | # bucket: your_own_bucket-<%= Rails.env %> 16 | 17 | # Remember not to checkin your GCS keyfile to a repository 18 | # google: 19 | # service: GCS 20 | # project: your_project 21 | # credentials: <%= Rails.root.join("path/to/gcs.keyfile") %> 22 | # bucket: your_own_bucket-<%= Rails.env %> 23 | 24 | # Use bin/rails credentials:edit to set the Azure Storage secret (as azure_storage:storage_access_key) 25 | # microsoft: 26 | # service: AzureStorage 27 | # storage_account_name: your_account_name 28 | # storage_access_key: <%= Rails.application.credentials.dig(:azure_storage, :storage_access_key) %> 29 | # container: your_container_name-<%= Rails.env %> 30 | 31 | # mirror: 32 | # service: Mirror 33 | # primary: local 34 | # mirrors: [ amazon, google, microsoft ] 35 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/projects_controller.rb: -------------------------------------------------------------------------------- 1 | # Management of `Projects` happend here. 2 | class ProjectsController < ApplicationController 3 | before_action :set_project, only: %i[show update destroy] 4 | 5 | # @tags projects 6 | def index 7 | @projects = Project.all 8 | 9 | render json: @projects 10 | end 11 | 12 | def show 13 | render json: @project 14 | end 15 | 16 | # @tags projects 17 | def create 18 | @project = Project.new(project_params) 19 | 20 | if @project.save 21 | render json: @project, status: :created, location: @project 22 | else 23 | render json: @project.errors, status: :unprocessable_entity 24 | end 25 | end 26 | 27 | def update 28 | if @project.update(project_params) 29 | render json: @project 30 | else 31 | render json: @project.errors, status: :unprocessable_entity 32 | end 33 | end 34 | 35 | def destroy 36 | @project.destroy! 37 | end 38 | 39 | private 40 | 41 | # Use callbacks to share common setup or constraints between actions. 42 | def set_project 43 | @project = Project.find(params[:id]) 44 | end 45 | 46 | # Only allow a list of trusted parameters through. 47 | def project_params 48 | params.require(:project).permit(:name, :description, :user_id) 49 | end 50 | end 51 | -------------------------------------------------------------------------------- /test/dummy/test/controllers/users/avatar_controller_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | module Users 4 | class AvatarControllerTest < ActionDispatch::IntegrationTest 5 | include AuthHelper 6 | 7 | setup do 8 | @user = users(:one) 9 | end 10 | 11 | test "should require authentication" do 12 | patch user_avatar_url(@user), params: { avatar: fixture_file_upload('avatar.jpg', 'image/jpeg') }, headers: default_headers 13 | assert_response :unauthorized 14 | assert_not @user.avatar.attached? 15 | end 16 | 17 | test "should update avatar" do 18 | assign_token(@user.id) 19 | patch user_avatar_url(@user), params: { avatar: fixture_file_upload('avatar.jpg', 'image/jpeg') }, headers: default_headers 20 | assert_response :ok 21 | assert_equal 'Avatar uploaded successfully', json_response['message'] 22 | assert @user.avatar.attached? 23 | end 24 | 25 | test "should not update avatar with invalid file" do 26 | assign_token(@user.id) 27 | patch user_avatar_url(@user), params: { avatar: fixture_file_upload('plain_text.txt', 'text/plain') }, headers: default_headers 28 | assert_response :unprocessable_entity 29 | assert_not @user.avatar.attached? 30 | end 31 | 32 | private 33 | 34 | def json_response 35 | JSON.parse(response.body) 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | # Configure Rails Environment 2 | ENV["RAILS_ENV"] = "test" 3 | 4 | require_relative "../test/dummy/config/environment" 5 | ActiveRecord::Migrator.migrations_paths = [File.expand_path("../test/dummy/db/migrate", __dir__)] 6 | ActiveRecord::Migrator.migrations_paths << File.expand_path("../db/migrate", __dir__) 7 | require "rails/test_help" 8 | require "minitest/mock" 9 | require_relative "helpers/auth_helper" 10 | 11 | # Load fixtures from the engine 12 | if ActiveSupport::TestCase.respond_to?(:fixture_paths=) 13 | ActiveSupport::TestCase.fixture_paths = [File.expand_path("fixtures", __dir__), File.expand_path("../test/dummy/test/fixtures", __dir__)] 14 | ActionDispatch::IntegrationTest.fixture_paths = ActiveSupport::TestCase.fixture_paths 15 | ActiveSupport::TestCase.file_fixture_path = File.expand_path("fixtures", __dir__) + "/files" 16 | ActiveSupport::TestCase.fixtures :all 17 | end 18 | 19 | # Helper method to find a Rails route by controller and action 20 | def find_route(controller_name, action_name) 21 | Rails.application.routes.routes.find do |route| 22 | defaults = route.defaults 23 | defaults[:controller] == controller_name && defaults[:action] == action_name 24 | end 25 | end 26 | 27 | def find_oas_route(controller_name, action_name) 28 | OasRails::Builders::OasRouteBuilder.build_from_rails_route(find_route(controller_name, action_name)) 29 | end 30 | -------------------------------------------------------------------------------- /test/dummy/config/application.rb: -------------------------------------------------------------------------------- 1 | require_relative 'boot' 2 | 3 | require 'rails/all' 4 | 5 | # Require the gems listed in Gemfile, including any gems 6 | # you've limited to :test, :development, or :production. 7 | Bundler.require(*Rails.groups) 8 | 9 | require "oas_rails" 10 | 11 | module Dummy 12 | class Application < Rails::Application 13 | config.load_defaults Rails::VERSION::STRING.to_f 14 | 15 | # For compatibility with applications that use this config 16 | config.action_controller.include_all_helpers = false 17 | 18 | # Please, add to the `ignore` list any other `lib` subdirectories that do 19 | # not contain `.rb` files, or that should not be reloaded or eager loaded. 20 | # Common ones are `templates`, `generators`, or `middleware`, for example. 21 | lib = root.join("lib") 22 | 23 | config.autoload_paths << lib 24 | config.eager_load_paths << lib 25 | 26 | Rails.autoloaders.main.ignore( 27 | lib.join("assets"), 28 | lib.join("tasks"), 29 | lib.join("generators") 30 | ) 31 | # Configuration for the application, engines, and railties goes here. 32 | # 33 | # These settings can be overridden in specific environments using the files 34 | # in config/environments, which are processed later. 35 | # 36 | # config.time_zone = "Central Time (US & Canada)" 37 | # config.eager_load_paths << Rails.root.join("extras") 38 | config.api_only = true 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /.github/workflows/rubyonrails.yml: -------------------------------------------------------------------------------- 1 | # This workflow uses actions that are not certified by GitHub. They are 2 | # provided by a third-party and are governed by separate terms of service, 3 | # privacy policy, and support documentation. 4 | # 5 | # This workflow will install a prebuilt Ruby version, install dependencies, and 6 | # run tests and linters. 7 | name: "Ruby on Rails CI" 8 | on: 9 | push: 10 | branches: ["main"] 11 | pull_request: 12 | branches: ["main"] 13 | jobs: 14 | test: 15 | strategy: 16 | fail-fast: false 17 | matrix: 18 | gemfile: [rails-7.1, rails-7.2, rails-8.0.0] 19 | runs-on: ubuntu-latest 20 | env: 21 | RAILS_ENV: test 22 | BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile 23 | steps: 24 | - name: Checkout code 25 | uses: actions/checkout@v4 26 | - name: Install Ruby and gems 27 | uses: ruby/setup-ruby@v1 28 | with: 29 | bundler-cache: true 30 | - name: Run tests 31 | run: bin/rails t 32 | 33 | lint: 34 | runs-on: ubuntu-latest 35 | steps: 36 | - name: Checkout code 37 | uses: actions/checkout@v4 38 | - name: Install Ruby and gems 39 | uses: ruby/setup-ruby@v1 40 | with: 41 | bundler-cache: true 42 | # Add or replace any other lints here 43 | - name: Install Security Scan Gems 44 | run: gem install bundler-audit brakeman rubocop 45 | - name: Security audit dependencies 46 | run: bundler-audit --update 47 | - name: Security audit application code 48 | run: brakeman -q -w2 49 | - name: Lint Ruby files 50 | run: bundle exec rubocop --parallel 51 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | git_source(:github) { |repo| "https://github.com/#{repo}.git" } 3 | 4 | # Specify your gem's dependencies in oas_rails.gemspec. 5 | gemspec 6 | 7 | gem 'puma', '>= 5.0' 8 | 9 | gem 'sqlite3', '~> 2.1' 10 | 11 | gem 'sprockets-rails' 12 | 13 | gem 'rack-cors' 14 | 15 | gem 'rails', '~> 8.0', '>= 8.0.2' 16 | 17 | group :development, :test do 18 | gem "rubocop" 19 | 20 | gem "bcrypt", "~> 3.1.7" 21 | 22 | gem 'factory_bot_rails' 23 | 24 | gem 'jwt' 25 | 26 | gem 'faker' 27 | 28 | # See https://guides.rubyonrails.org/debugging_rails_applications.html#debugging-with-the-debug-gem 29 | gem 'debug', platforms: %i[mri windows] 30 | end 31 | 32 | # Build JSON APIs with ease [https://github.com/rails/jbuilder] 33 | gem 'jbuilder' 34 | 35 | # Windows does not include zoneinfo files, so bundle the tzinfo-data gem 36 | gem 'tzinfo-data', platforms: %i[windows jruby] 37 | 38 | # Reduces boot times through caching; required in config/boot.rb 39 | gem 'bootsnap', require: false 40 | 41 | # Use Active Storage variants [https://guides.rubyonrails.org/active_storage_overview.html#transforming-images] 42 | # gem "image_processing", "~> 1.2" 43 | 44 | group :development do 45 | # Use console on exceptions pages [https://github.com/rails/web-console] 46 | gem 'web-console' 47 | 48 | # Add speed badges [https://github.com/MiniProfiler/rack-mini-profiler] 49 | # gem "rack-mini-profiler" 50 | 51 | # Speed up commands on slow machines / big apps [https://github.com/rails/spring] 52 | # gem "spring" 53 | end 54 | 55 | group :test do 56 | # Use system testing [https://guides.rubyonrails.org/testing.html#system-testing] 57 | gem 'capybara' 58 | gem 'selenium-webdriver' 59 | end 60 | -------------------------------------------------------------------------------- /test/lib/oas_rails/active_record_example_finder_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | module OasRails 4 | class ActiveRecordExampleFinderTest < ActiveSupport::TestCase 5 | def setup 6 | @utils = Minitest::Mock.new 7 | @factory_bot = Minitest::Mock.new 8 | @file = Minitest::Mock.new 9 | @finder = ActiveRecordExampleFinder.new(context: :incoming, utils: @utils, factory_bot: @factory_bot, file: @file) 10 | end 11 | 12 | test "search returns empty hash for unsupported test framework" do 13 | @utils.expect :detect_test_framework, :unknown 14 | assert_equal({}, @finder.search(User)) 15 | end 16 | 17 | test "search delegates to factory_bot when framework is factory_bot" do 18 | @utils.expect :detect_test_framework, :factory_bot 19 | @utils.expect :class_to_symbol, :user, [User] 20 | @factory_bot.expect :build_stubbed_list, [{ name: "Test" }], [:user, 1] 21 | 22 | expected = { user1: { value: { user: { name: "Test" } } } } 23 | assert_equal expected, @finder.search(User) 24 | end 25 | 26 | test "search delegates to fixtures when framework is fixtures" do 27 | @utils.expect :detect_test_framework, :fixtures 28 | @file.expect :read, " 29 | two: 30 | name: <%= 'Luis' %> 31 | email: luis@gmail.com 32 | ", [Pathname] 33 | 34 | expected = { two: { value: { user: { "name" => "Luis", "email" => "luis@gmail.com" } } } } 35 | assert_equal(expected, @finder.search(User)) 36 | end 37 | 38 | test "clean_example_object filters excluded columns" do 39 | obj = { id: 1, name: "Test", password: "secret" } 40 | assert_equal({ name: "Test", password: "secret" }, @finder.clean_example_object(obj: obj)) 41 | end 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /test/dummy/config/puma.rb: -------------------------------------------------------------------------------- 1 | # This configuration file will be evaluated by Puma. The top-level methods that 2 | # are invoked here are part of Puma's configuration DSL. For more information 3 | # about methods provided by the DSL, see https://puma.io/puma/Puma/DSL.html. 4 | 5 | # Puma can serve each request in a thread from an internal thread pool. 6 | # The `threads` method setting takes two numbers: a minimum and maximum. 7 | # Any libraries that use thread pools should be configured to match 8 | # the maximum value specified for Puma. Default is set to 5 threads for minimum 9 | # and maximum; this matches the default thread size of Active Record. 10 | max_threads_count = ENV.fetch("RAILS_MAX_THREADS") { 5 } 11 | min_threads_count = ENV.fetch("RAILS_MIN_THREADS") { max_threads_count } 12 | threads min_threads_count, max_threads_count 13 | 14 | # Specifies that the worker count should equal the number of processors in production. 15 | if ENV["RAILS_ENV"] == "production" 16 | require "concurrent-ruby" 17 | worker_count = Integer(ENV.fetch("WEB_CONCURRENCY") { Concurrent.physical_processor_count }) 18 | workers worker_count if worker_count > 1 19 | end 20 | 21 | # Specifies the `worker_timeout` threshold that Puma will use to wait before 22 | # terminating a worker in development environments. 23 | worker_timeout 3600 if ENV.fetch("RAILS_ENV", "development") == "development" 24 | 25 | # Specifies the `port` that Puma will listen on to receive requests; default is 3000. 26 | port ENV.fetch("PORT") { 3000 } 27 | 28 | # Specifies the `environment` that Puma will run in. 29 | environment ENV.fetch("RAILS_ENV") { "development" } 30 | 31 | # Specifies the `pidfile` that Puma will use. 32 | pidfile ENV.fetch("PIDFILE") { "tmp/pids/server.pid" } 33 | 34 | # Allow puma to be restarted by `bin/rails restart` command. 35 | plugin :tmp_restart 36 | -------------------------------------------------------------------------------- /test/lib/oas_rails/builders/esquema_builder_test.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module Builders 3 | class EsquemaBuilderTest < Minitest::Test 4 | def setup 5 | @user_klass = User 6 | @invalid_klass = Object 7 | @custom_schema_class = EasyTalk 8 | end 9 | 10 | def test_build_incoming_schema 11 | schema = OasRails::Builders::EsquemaBuilder.build_incoming_schema(klass: @user_klass) 12 | 13 | assert_instance_of Hash, schema 14 | refute_includes schema["properties"].keys, "id" 15 | end 16 | 17 | def test_build_outgoing_schema 18 | schema = OasRails::Builders::EsquemaBuilder.build_outgoing_schema(klass: @user_klass) 19 | 20 | assert_instance_of Hash, schema 21 | assert_includes schema["properties"].keys, "id" 22 | end 23 | 24 | def test_build_incoming_schema_with_custom_schema_class 25 | schema = OasRails::Builders::EsquemaBuilder.build_incoming_schema( 26 | klass: @user_klass, 27 | model_to_schema_class: @custom_schema_class 28 | ) 29 | 30 | assert_instance_of Hash, schema 31 | refute_includes schema["properties"].keys, "id" 32 | end 33 | 34 | def test_build_outgoing_schema_with_custom_schema_class 35 | schema = OasRails::Builders::EsquemaBuilder.build_outgoing_schema( 36 | klass: @user_klass, 37 | model_to_schema_class: @custom_schema_class 38 | ) 39 | 40 | assert_instance_of Hash, schema 41 | assert_includes schema["properties"].keys, "id" 42 | end 43 | 44 | def test_build_schema_with_invalid_klass 45 | assert_raises(ArgumentError) do 46 | OasRails::Builders::EsquemaBuilder.build_incoming_schema(klass: @invalid_klass) 47 | end 48 | end 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /test/lib/oas_rails/extractors/route_extractor_test.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module Builders 3 | class RouteExtractorTest < Minitest::Test 4 | def setup 5 | Extractors::RouteExtractor.clear_cache 6 | OasRails.config.ignored_actions = [] 7 | OasRails.config.include_mode = :all 8 | end 9 | 10 | def teardown 11 | Extractors::RouteExtractor.clear_cache 12 | OasRails.config.ignored_actions = [] 13 | OasRails.config.include_mode = :all 14 | end 15 | 16 | def test_without_custom_routes 17 | result = Extractors::RouteExtractor.host_routes 18 | assert_equal 16, result.count 19 | end 20 | 21 | def test_with_custom_controllers_actions 22 | init_count = Extractors::RouteExtractor.host_routes.count 23 | OasRails.config.ignored_actions = ["projects#index"] 24 | Extractors::RouteExtractor.clear_cache 25 | result = Extractors::RouteExtractor.host_routes.count 26 | assert_equal (init_count - 1), result 27 | end 28 | 29 | def test_with_custom_controller_only 30 | OasRails.config.ignored_actions = ["projects"] 31 | routes = Extractors::RouteExtractor.host_routes 32 | result = routes.select { |route| route.controller.include?("projects") } 33 | assert_equal 0, result.count 34 | end 35 | 36 | def test_extract_host_routes_with_tags_mode 37 | OasRails.config.include_mode = :with_tags 38 | result = Extractors::RouteExtractor.host_routes 39 | assert_equal 12, result.count 40 | end 41 | 42 | def test_extract_host_routes_explicit_mode 43 | OasRails.config.include_mode = :explicit 44 | result = Extractors::RouteExtractor.host_routes 45 | assert_equal 1, result.count 46 | end 47 | end 48 | end 49 | end 50 | -------------------------------------------------------------------------------- /test/dummy/public/500.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | We're sorry, but something went wrong (500) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

We're sorry, but something went wrong.

62 |
63 |

If you are the application owner check the logs for more information.

64 |
65 | 66 | 67 | -------------------------------------------------------------------------------- /test/dummy/public/422.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The change you wanted was rejected (422) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The change you wanted was rejected.

62 |

Maybe you tried to change something you didn't have access to.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/public/404.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | The page you were looking for doesn't exist (404) 5 | 6 | 55 | 56 | 57 | 58 | 59 |
60 |
61 |

The page you were looking for doesn't exist.

62 |

You may have mistyped the address or the page may have moved.

63 |
64 |

If you are the application owner check the logs for more information.

65 |
66 | 67 | 68 | -------------------------------------------------------------------------------- /test/dummy/config/initializers/oas_rails.rb: -------------------------------------------------------------------------------- 1 | OasRails.configure do |config| 2 | config.source_oas_path = "lib/oas.json" 3 | config.info.title = 'Dummy API REST+' 4 | config.info.summary = 'Core endpoints of Dummy App.' 5 | config.info.description = <<~HEREDOC 6 | # Adimuntque regni fuerit 7 | 8 | ## Et fuissem licet in vetus hic Rhoetus 9 | 10 | Lorem markdownum currus praetemptanda vocato Canens Tartara. Chaonius demitteret 11 | quem **patulas pares decimae** refluum, honores: ullis nec in iunctas, ora 12 | fratri saeve, vix. 13 | 14 | ## Etenim habebat sinistra 15 | 16 | Docs: 17 | 18 | Teneri celebrant prius! Dedit non culmine, est duorum apertum dicione edere, ibi 19 | cetera Olenos quae: solita. 20 | 21 | - Boum alas malis miranti deum 22 | - Victricia ipse 23 | - Resque velle in adiere Phrygiae fortis postquam 24 | - Remisso agit nobis 25 | - Cyclopis superata ingentibus numquam Lucifero 26 | HEREDOC 27 | config.info.contact.name = 'Peter' 28 | config.info.contact.email = 'peter@gmail.com' 29 | config.info.contact.url = 'https://peter.com' 30 | config.servers = [{ url: 'http://localhost:3000', description: 'Local' }, 31 | { url: 'https://example.rb', description: 'Dev' }] 32 | config.tags = [{ name: "Users", description: "Manage the `amazing` Users table." }] 33 | 34 | config.security_schema = :bearer 35 | # config.security_schemas = { 36 | # } 37 | # 38 | 39 | # Default Errors 40 | # The default errors are setted only if the action allow it. 41 | # Example, forbidden will be setted to the endpoint requires authentication. 42 | # Example: not_found will be setter to the endpoint only if the operation is a show/update/destroy action. 43 | config.set_default_responses = true 44 | # config.possible_default_responses = [:not_found, :unauthorized, :forbidden] 45 | # config.response_body_of_default = "Hash{ message: String, errors: Array }" 46 | config.response_body_of_unauthorized = "Hash{ message: String, error: Array }" 47 | config.response_body_of_internal_server_error = "Hash{ error: String, traceback: String }" 48 | end 49 | -------------------------------------------------------------------------------- /lib/oas_rails.rb: -------------------------------------------------------------------------------- 1 | require "easy_talk" 2 | require "oas_core" 3 | 4 | OasCore.configure_yard! 5 | 6 | module OasRails 7 | require "oas_rails/version" 8 | require "oas_rails/engine" 9 | 10 | autoload :Configuration, "oas_rails/configuration" 11 | autoload :Utils, "oas_rails/utils" 12 | autoload :JsonSchemaGenerator, "oas_rails/json_schema_generator" 13 | autoload :ActiveRecordExampleFinder, "oas_rails/active_record_example_finder" 14 | 15 | module Builders 16 | autoload :EsquemaBuilder, "oas_rails/builders/esquema_builder" 17 | autoload :OasRouteBuilder, "oas_rails/builders/oas_route_builder" 18 | end 19 | 20 | module Extractors 21 | autoload :RenderResponseExtractor, 'oas_rails/extractors/render_response_extractor' 22 | autoload :RouteExtractor, "oas_rails/extractors/route_extractor" 23 | autoload :OasRouteExtractor, "oas_rails/extractors/oas_route_extractor" 24 | end 25 | 26 | class << self 27 | OasCore::JsonSchemaGenerator.register_type_parser( 28 | ->(t) { Utils.active_record_class?(t) }, 29 | ->(type, _required) { Builders::EsquemaBuilder.build_outgoing_schema(klass: type.constantize) } 30 | ) 31 | 32 | def build 33 | clear_cache 34 | OasCore.config = config 35 | 36 | host_routes = config.route_extractor.host_routes 37 | oas_source = config.source_oas_path ? read_source_oas_file : {} 38 | 39 | OasCore.build(host_routes, oas_source: oas_source) 40 | end 41 | 42 | def configure 43 | yield config 44 | end 45 | 46 | def config 47 | @config ||= Configuration.new 48 | end 49 | 50 | private 51 | 52 | def clear_cache 53 | return if Rails.env.production? 54 | 55 | MethodSource.clear_cache 56 | OasRails::Extractors::RouteExtractor.clear_cache 57 | end 58 | 59 | def read_source_oas_file 60 | file_path = Rails.root.join(config.source_oas_path) 61 | JSON.parse(File.read(file_path), symbolize_names: true) 62 | rescue Errno::ENOENT => e 63 | raise "Failed to read source OAS file at #{file_path}: #{e.message}" 64 | rescue JSON::ParserError => e 65 | raise "Failed to parse source OAS file at #{file_path}: #{e.message}" 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /test/lib/oas_rails/builders/oas_route_builder_test.rb: -------------------------------------------------------------------------------- 1 | require 'test_helper' 2 | 3 | module OasRails 4 | module Builders 5 | class OasRouteBuilderTest < ActiveSupport::TestCase 6 | def setup 7 | @users_index_route = find_route("users", "index") 8 | @projects_show_route = find_route("projects", "show") 9 | end 10 | 11 | def test_build_returns_an_oas_route_object 12 | users_index_oas_route = OasRouteBuilder.build_from_rails_route(@users_index_route) 13 | assert_instance_of ::OasCore::OasRoute, users_index_oas_route 14 | end 15 | 16 | def test_build_sets_correct_controller 17 | users_index_oas_route = OasRouteBuilder.build_from_rails_route(@users_index_route) 18 | assert_equal "users", users_index_oas_route.controller 19 | end 20 | 21 | def test_build_sets_correct_method 22 | users_index_oas_route = OasRouteBuilder.build_from_rails_route(@users_index_route) 23 | assert_equal "index", users_index_oas_route.method_name 24 | end 25 | 26 | def test_build_sets_correct_verb 27 | users_index_oas_route = OasRouteBuilder.build_from_rails_route(@users_index_route) 28 | assert_equal "GET", users_index_oas_route.verb 29 | end 30 | 31 | def test_build_sets_correct_path 32 | users_index_oas_route = OasRouteBuilder.build_from_rails_route(@users_index_route) 33 | assert_equal "/users", users_index_oas_route.path 34 | end 35 | 36 | def test_build_sets_correct_docstring 37 | users_index_oas_route = OasRouteBuilder.build_from_rails_route(@users_index_route) 38 | assert_not_nil users_index_oas_route.docstring 39 | end 40 | 41 | def test_build_sets_correct_source_string 42 | users_index_oas_route = OasRouteBuilder.build_from_rails_route(@users_index_route) 43 | assert_not_nil users_index_oas_route.source_string 44 | end 45 | 46 | def test_build_sets_correct_tags 47 | users_index_oas_route = OasRouteBuilder.build_from_rails_route(@projects_show_route) 48 | assert_not_nil users_index_oas_route.tags 49 | assert(users_index_oas_route.tags.all? { |tag| tag.respond_to?(:tag_name) }) 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /lib/oas_rails/configuration.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | class Configuration < OasCore::Configuration 3 | attr_accessor :autodiscover_request_body, :autodiscover_responses, :ignored_actions, :rapidoc_theme, :layout, :source_oas_path, :rapidoc_configuration, :rapidoc_logo_url, :mounted_path 4 | attr_reader :route_extractor, :include_mode 5 | 6 | def initialize 7 | super 8 | @mounted_path = default_mounted_path 9 | @route_extractor = Extractors::RouteExtractor 10 | @include_mode = :all 11 | @autodiscover_request_body = true 12 | @autodiscover_responses = true 13 | @ignored_actions = [] 14 | @layout = nil 15 | @rapidoc_theme = :rails 16 | @rapidoc_configuration = {} 17 | @rapidoc_logo_url = nil 18 | @source_oas_path = nil 19 | 20 | # TODO: implement 21 | # autodiscover_request_body 22 | # autodiscover_responses 23 | end 24 | 25 | def excluded_columns_incoming 26 | %i[id created_at updated_at deleted_at] 27 | end 28 | 29 | def excluded_columns_outgoing 30 | [] 31 | end 32 | 33 | def include_mode=(value) 34 | valid_modes = %i[all with_tags explicit] 35 | raise ArgumentError, "include_mode must be one of #{valid_modes}" unless valid_modes.include?(value) 36 | 37 | @include_mode = value 38 | end 39 | 40 | def route_extractor=(value) 41 | unless value.respond_to?(:host_routes) && 42 | value.respond_to?(:host_routes_by_path) && 43 | value.respond_to?(:clear_cache) && 44 | value.respond_to?(:host_paths) && 45 | value.respond_to?(:clean_route) 46 | raise ArgumentError, 47 | "Route extractor must have the following methods: host_routes, host_routes_by_path, clear_cache, host_paths, and clean_route" 48 | end 49 | 50 | @route_extractor = value 51 | end 52 | 53 | def default_mounted_path 54 | mount_route = Rails.application.routes.routes.detect do |r| 55 | r.app.respond_to?(:app) && r.app.app == OasRails::Engine 56 | end 57 | 58 | if mount_route 59 | mount_route.path.spec.to_s.sub(/\(\.:format\)\z/, '') 60 | else 61 | '/docs' 62 | end 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/oas_rails/builders/oas_route_builder.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module Builders 3 | class OasRouteBuilder 4 | def self.build_from_rails_route(rails_route) 5 | new(rails_route).build 6 | end 7 | 8 | def initialize(rails_route) 9 | @rails_route = rails_route 10 | end 11 | 12 | def build 13 | OasCore::OasRoute.new( 14 | controller: controller, 15 | method_name: method, 16 | verb: verb, 17 | path: path, 18 | docstring: docstring, 19 | source_string: source_string, 20 | tags: tags 21 | ) 22 | end 23 | 24 | private 25 | 26 | def controller_class 27 | "#{@rails_route.defaults[:controller].camelize}Controller" 28 | end 29 | 30 | def controller 31 | @rails_route.defaults[:controller] 32 | end 33 | 34 | def method 35 | @rails_route.defaults[:action] 36 | end 37 | 38 | def verb 39 | @rails_route.verb 40 | end 41 | 42 | def path 43 | OasRails.config.route_extractor.clean_route(@rails_route.path.spec.to_s) 44 | end 45 | 46 | def source_string 47 | controller_class.constantize.instance_method(method).source 48 | end 49 | 50 | def docstring 51 | comment_lines = controller_class.constantize.instance_method(method).comment.lines 52 | processed_lines = comment_lines.map { |line| line.sub(/^# /, '') } 53 | 54 | filtered_lines = processed_lines.reject do |line| 55 | line.include?('rubocop') || 56 | line.include?('TODO') 57 | end 58 | 59 | ::YARD::Docstring.parser.parse(filtered_lines.join).to_docstring 60 | end 61 | 62 | def tags 63 | method_comment = controller_class.constantize.instance_method(method).comment 64 | class_comment = controller_class.constantize.instance_method(method).class_comment 65 | 66 | method_tags = parse_tags(method_comment) 67 | class_tags = parse_tags(class_comment) 68 | 69 | method_tags + class_tags 70 | end 71 | 72 | def parse_tags(comment) 73 | lines = comment.lines.map { |line| line.sub(/^# /, '') } 74 | ::YARD::Docstring.parser.parse(lines.join).tags 75 | end 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /test/dummy/db/migrate/20250528212011_create_active_storage_tables.active_storage.rb: -------------------------------------------------------------------------------- 1 | # This migration comes from active_storage (originally 20170806125915) 2 | class CreateActiveStorageTables < ActiveRecord::Migration[7.0] 3 | def change 4 | # Use Active Record's configured type for primary and foreign keys 5 | primary_key_type, foreign_key_type = primary_and_foreign_key_types 6 | 7 | create_table :active_storage_blobs, id: primary_key_type do |t| 8 | t.string :key, null: false 9 | t.string :filename, null: false 10 | t.string :content_type 11 | t.text :metadata 12 | t.string :service_name, null: false 13 | t.bigint :byte_size, null: false 14 | t.string :checksum 15 | 16 | if connection.supports_datetime_with_precision? 17 | t.datetime :created_at, precision: 6, null: false 18 | else 19 | t.datetime :created_at, null: false 20 | end 21 | 22 | t.index [:key], unique: true 23 | end 24 | 25 | create_table :active_storage_attachments, id: primary_key_type do |t| 26 | t.string :name, null: false 27 | t.references :record, null: false, polymorphic: true, index: false, type: foreign_key_type 28 | t.references :blob, null: false, type: foreign_key_type 29 | 30 | if connection.supports_datetime_with_precision? 31 | t.datetime :created_at, precision: 6, null: false 32 | else 33 | t.datetime :created_at, null: false 34 | end 35 | 36 | t.index [:record_type, :record_id, :name, :blob_id], name: :index_active_storage_attachments_uniqueness, unique: true 37 | t.foreign_key :active_storage_blobs, column: :blob_id 38 | end 39 | 40 | create_table :active_storage_variant_records, id: primary_key_type do |t| 41 | t.belongs_to :blob, null: false, index: false, type: foreign_key_type 42 | t.string :variation_digest, null: false 43 | 44 | t.index [:blob_id, :variation_digest], name: :index_active_storage_variant_records_uniqueness, unique: true 45 | t.foreign_key :active_storage_blobs, column: :blob_id 46 | end 47 | end 48 | 49 | private 50 | 51 | def primary_and_foreign_key_types 52 | config = Rails.configuration.generators 53 | setting = config.options[config.orm][:primary_key_type] 54 | primary_key_type = setting || :primary_key 55 | foreign_key_type = setting || :bigint 56 | [primary_key_type, foreign_key_type] 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /lib/oas_rails/active_record_example_finder.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | class ActiveRecordExampleFinder 3 | def initialize(context: :incoming, utils: Utils, factory_bot: FactoryBot, erb: ERB, yaml: YAML, file: File) 4 | @context = context 5 | @utils = utils 6 | @factory_bot = factory_bot 7 | @erb = erb 8 | @yaml = yaml 9 | @file = file 10 | @factory_examples = {} 11 | end 12 | 13 | def search(klass) 14 | case @utils.detect_test_framework 15 | when :factory_bot 16 | fetch_factory_bot_examples(klass: klass) 17 | when :fixtures 18 | fetch_fixture_examples(klass: klass) 19 | else 20 | {} 21 | end 22 | end 23 | 24 | # Fetches examples from FactoryBot for the provided class. 25 | # 26 | # @param klass [Class] the class to fetch examples for. 27 | # @return [Hash] a hash containing examples data or an empty hash if no examples are found. 28 | def fetch_factory_bot_examples(klass:) 29 | klass_sym = @utils.class_to_symbol(klass) 30 | 31 | begin 32 | @factory_examples[klass_sym] = @factory_bot.build_stubbed_list(klass_sym, 1) if @factory_examples[klass_sym].nil? 33 | 34 | @factory_examples[klass_sym].each_with_index.to_h do |obj, index| 35 | ["#{klass_sym}#{index + 1}", { value: { klass_sym => clean_example_object(obj: obj.as_json) } }] 36 | end.deep_symbolize_keys 37 | rescue KeyError 38 | {} 39 | end 40 | end 41 | 42 | # Fetches examples from fixtures for the provided class. 43 | # 44 | # @param klass [Class] the class to fetch examples for. 45 | # @return [Hash] a hash containing examples data or an empty hash if no examples are found. 46 | def fetch_fixture_examples(klass:) 47 | fixture_file = Rails.root.join('test', 'fixtures', "#{klass.to_s.pluralize.downcase}.yml") 48 | begin 49 | erb_result = @erb.new(@file.read(fixture_file)).result 50 | fixture_data = @yaml.safe_load( 51 | erb_result, 52 | aliases: true, 53 | permitted_classes: [Symbol, ActiveSupport::HashWithIndifferentAccess, Time] 54 | ).with_indifferent_access 55 | rescue Errno::ENOENT 56 | return {} 57 | end 58 | fixture_data.transform_values { |attributes| { value: { klass.to_s.downcase => clean_example_object(obj: attributes) } } }.deep_symbolize_keys 59 | end 60 | 61 | def clean_example_object(obj:) 62 | obj.reject { |key, _| OasRails.config.send("excluded_columns_#{@context}").include?(key.to_sym) } 63 | end 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /lib/oas_rails/builders/esquema_builder.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module Builders 3 | module EsquemaBuilder 4 | class << self 5 | # Builds a schema for a class when it is used as incoming API data. 6 | # 7 | # @param klass [Class] The class for which the schema is built. 8 | # @return [Hash] The schema as a JSON-compatible hash. 9 | def build_incoming_schema(klass:, model_to_schema_class: EasyTalk) 10 | build_schema( 11 | klass: klass, 12 | model_to_schema_class: model_to_schema_class, 13 | excluded_columns: OasRails.config.excluded_columns_incoming, 14 | exclude_primary_key: true 15 | ) 16 | end 17 | 18 | # Builds a schema for a class when it is used as outgoing API data. 19 | # 20 | # @param klass [Class] The class for which the schema is built. 21 | # @return [Hash] The schema as a JSON-compatible hash. 22 | def build_outgoing_schema(klass:, model_to_schema_class: EasyTalk) 23 | build_schema( 24 | klass: klass, 25 | model_to_schema_class: model_to_schema_class, 26 | excluded_columns: OasRails.config.excluded_columns_outgoing, 27 | exclude_primary_key: false 28 | ) 29 | end 30 | 31 | private 32 | 33 | # Builds a schema with the given configuration. 34 | # 35 | # @param klass [Class] The class for which the schema is built. 36 | # @param model_to_schema_class [Class] The schema builder class. 37 | # @param excluded_columns [Array] Columns to exclude from the schema. 38 | # @param exclude_primary_key [Boolean] Whether to exclude the primary key. 39 | # @return [Hash] The schema as a JSON-compatible hash. 40 | def build_schema(klass:, model_to_schema_class:, excluded_columns:, exclude_primary_key:) 41 | configure_common_settings(model_to_schema_class: model_to_schema_class) 42 | model_to_schema_class.configuration.excluded_columns = excluded_columns 43 | model_to_schema_class.configuration.exclude_primary_key = exclude_primary_key 44 | 45 | definition = model_to_schema_class::ActiveRecordSchemaBuilder.new(klass).build_schema_definition 46 | EasyTalk::Builders::ObjectBuilder.new(definition).build.as_json 47 | end 48 | 49 | # Configures common settings for schema building. 50 | # 51 | # Excludes associations and foreign keys from the schema. 52 | def configure_common_settings(model_to_schema_class: EasyTalk) 53 | model_to_schema_class.configuration.exclude_associations = true 54 | end 55 | end 56 | end 57 | end 58 | end 59 | -------------------------------------------------------------------------------- /test/dummy/config/environments/test.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | # The test environment is used exclusively to run your application's 4 | # test suite. You never need to work with it otherwise. Remember that 5 | # your test database is "scratch space" for the test suite and is wiped 6 | # and recreated between test runs. Don't rely on the data there! 7 | 8 | Rails.application.configure do 9 | # Settings specified here will take precedence over those in config/application.rb. 10 | 11 | # While tests run files are not watched, reloading is not necessary. 12 | config.enable_reloading = false 13 | 14 | # Eager loading loads your entire application. When running a single test locally, 15 | # this is usually not necessary, and can slow down your test suite. However, it's 16 | # recommended that you enable it in continuous integration systems to ensure eager 17 | # loading is working properly before deploying your code. 18 | config.eager_load = ENV["CI"].present? 19 | 20 | # Configure public file server for tests with Cache-Control for performance. 21 | config.public_file_server.enabled = true 22 | config.public_file_server.headers = { 23 | "Cache-Control" => "public, max-age=#{1.hour.to_i}" 24 | } 25 | 26 | # Show full error reports and disable caching. 27 | config.consider_all_requests_local = true 28 | config.action_controller.perform_caching = false 29 | config.cache_store = :null_store 30 | 31 | # Render exception templates for rescuable exceptions and raise for other exceptions. 32 | config.action_dispatch.show_exceptions = :rescuable 33 | 34 | # Disable request forgery protection in test environment. 35 | config.action_controller.allow_forgery_protection = false 36 | 37 | # Store uploaded files on the local file system in a temporary directory. 38 | config.active_storage.service = :test 39 | 40 | config.action_mailer.perform_caching = false 41 | 42 | # Tell Action Mailer not to deliver emails to the real world. 43 | # The :test delivery method accumulates sent emails in the 44 | # ActionMailer::Base.deliveries array. 45 | config.action_mailer.delivery_method = :test 46 | 47 | # Print deprecation notices to the stderr. 48 | config.active_support.deprecation = :stderr 49 | 50 | # Raise exceptions for disallowed deprecations. 51 | config.active_support.disallowed_deprecation = :raise 52 | 53 | # Tell Active Support which deprecation messages to disallow. 54 | config.active_support.disallowed_deprecation_warnings = [] 55 | 56 | # Raises error for missing translations. 57 | # config.i18n.raise_on_missing_translations = true 58 | 59 | # Annotate rendered view with file names. 60 | # config.action_view.annotate_rendered_view_with_filenames = true 61 | 62 | # Raise error when a before_action's only/except options reference missing actions 63 | end 64 | -------------------------------------------------------------------------------- /test/dummy/config/environments/development.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # In the development environment your application's code is reloaded any time 7 | # it changes. This slows down response time but is perfect for development 8 | # since you don't have to restart the web server when you make code changes. 9 | config.enable_reloading = true 10 | 11 | # Do not eager load code on boot. 12 | config.eager_load = false 13 | 14 | # Show full error reports. 15 | config.consider_all_requests_local = true 16 | 17 | # Enable server timing 18 | config.server_timing = true 19 | 20 | # Enable/disable caching. By default caching is disabled. 21 | # Run rails dev:cache to toggle caching. 22 | if Rails.root.join("tmp/caching-dev.txt").exist? 23 | config.action_controller.perform_caching = true 24 | config.action_controller.enable_fragment_cache_logging = true 25 | 26 | config.cache_store = :memory_store 27 | config.public_file_server.headers = { 28 | "Cache-Control" => "public, max-age=#{2.days.to_i}" 29 | } 30 | else 31 | config.action_controller.perform_caching = false 32 | 33 | config.cache_store = :null_store 34 | end 35 | 36 | # Store uploaded files on the local file system (see config/storage.yml for options). 37 | config.active_storage.service = :local 38 | 39 | # Don't care if the mailer can't send. 40 | config.action_mailer.raise_delivery_errors = false 41 | 42 | config.action_mailer.perform_caching = false 43 | 44 | # Print deprecation notices to the Rails logger. 45 | config.active_support.deprecation = :log 46 | 47 | # Raise exceptions for disallowed deprecations. 48 | config.active_support.disallowed_deprecation = :raise 49 | 50 | # Tell Active Support which deprecation messages to disallow. 51 | config.active_support.disallowed_deprecation_warnings = [] 52 | 53 | # Raise an error on page load if there are pending migrations. 54 | config.active_record.migration_error = :page_load 55 | 56 | # Highlight code that triggered database queries in logs. 57 | config.active_record.verbose_query_logs = true 58 | 59 | # Highlight code that enqueued background job in logs. 60 | config.active_job.verbose_enqueue_logs = true 61 | 62 | # Suppress logger output for asset requests. 63 | config.assets.quiet = true 64 | 65 | # Raises error for missing translations. 66 | # config.i18n.raise_on_missing_translations = true 67 | 68 | # Annotate rendered view with file names. 69 | # config.action_view.annotate_rendered_view_with_filenames = true 70 | 71 | # Uncomment if you wish to allow Action Cable access from any origin. 72 | # config.action_cable.disable_request_forgery_protection = true 73 | 74 | # Raise error when a before_action's only/except options reference missing actions 75 | end 76 | -------------------------------------------------------------------------------- /test/dummy/db/schema.rb: -------------------------------------------------------------------------------- 1 | # This file is auto-generated from the current state of the database. Instead 2 | # of editing this file, please use the migrations feature of Active Record to 3 | # incrementally modify your database, and then regenerate this schema definition. 4 | # 5 | # This file is the source Rails uses to define your schema when running `bin/rails 6 | # db:schema:load`. When creating a new database, `bin/rails db:schema:load` tends to 7 | # be faster and is potentially less error prone than running all of your 8 | # migrations from scratch. Old migrations may fail to apply correctly if those 9 | # migrations use external dependencies or application code. 10 | # 11 | # It's strongly recommended that you check this file into your version control system. 12 | 13 | ActiveRecord::Schema[7.0].define(version: 2025_05_28_212011) do 14 | create_table "active_storage_attachments", force: :cascade do |t| 15 | t.string "name", null: false 16 | t.string "record_type", null: false 17 | t.bigint "record_id", null: false 18 | t.bigint "blob_id", null: false 19 | t.datetime "created_at", null: false 20 | t.index ["blob_id"], name: "index_active_storage_attachments_on_blob_id" 21 | t.index ["record_type", "record_id", "name", "blob_id"], name: "index_active_storage_attachments_uniqueness", unique: true 22 | end 23 | 24 | create_table "active_storage_blobs", force: :cascade do |t| 25 | t.string "key", null: false 26 | t.string "filename", null: false 27 | t.string "content_type" 28 | t.text "metadata" 29 | t.string "service_name", null: false 30 | t.bigint "byte_size", null: false 31 | t.string "checksum" 32 | t.datetime "created_at", null: false 33 | t.index ["key"], name: "index_active_storage_blobs_on_key", unique: true 34 | end 35 | 36 | create_table "active_storage_variant_records", force: :cascade do |t| 37 | t.bigint "blob_id", null: false 38 | t.string "variation_digest", null: false 39 | t.index ["blob_id", "variation_digest"], name: "index_active_storage_variant_records_uniqueness", unique: true 40 | end 41 | 42 | create_table "projects", force: :cascade do |t| 43 | t.string "name" 44 | t.text "description" 45 | t.integer "user_id", null: false 46 | t.datetime "created_at", null: false 47 | t.datetime "updated_at", null: false 48 | t.boolean "active", default: false 49 | t.date "start_date" 50 | t.datetime "end_date" 51 | t.decimal "budget", precision: 10, scale: 2 52 | t.integer "priority" 53 | t.string "status", default: "draft" 54 | t.index ["user_id"], name: "index_projects_on_user_id" 55 | end 56 | 57 | create_table "users", force: :cascade do |t| 58 | t.string "name" 59 | t.string "email" 60 | t.string "password_digest" 61 | t.datetime "created_at", null: false 62 | t.datetime "updated_at", null: false 63 | end 64 | 65 | add_foreign_key "active_storage_attachments", "active_storage_blobs", column: "blob_id" 66 | add_foreign_key "active_storage_variant_records", "active_storage_blobs", column: "blob_id" 67 | add_foreign_key "projects", "users" 68 | end 69 | -------------------------------------------------------------------------------- /test/lib/oas_rails/configuration_test.rb: -------------------------------------------------------------------------------- 1 | require "test_helper" 2 | 3 | module OasRails 4 | class ConfigurationTest < ActiveSupport::TestCase 5 | def setup 6 | @config = Configuration.new 7 | end 8 | 9 | test "initializes with default values" do 10 | assert_equal "3.1.0", @config.instance_variable_get(:@swagger_version) 11 | assert_equal "/", @config.api_path 12 | assert_equal [], @config.ignored_actions 13 | assert_equal true, @config.autodiscover_request_body 14 | assert_equal true, @config.autodiscover_responses 15 | assert_equal true, @config.authenticate_all_routes_by_default 16 | assert_equal [:get, :post, :put, :patch, :delete], @config.http_verbs 17 | assert_equal "Hash{ status: !Integer, error: String }", @config.response_body_of_default 18 | assert_equal :rails, @config.rapidoc_theme 19 | assert_equal :all, @config.include_mode 20 | end 21 | 22 | test "sets and gets servers" do 23 | servers = [{ url: "https://example.com", description: "Example Server" }] 24 | @config.servers = servers 25 | assert_equal 1, @config.servers.size 26 | assert_equal "https://example.com", @config.servers.first.url 27 | assert_equal "Example Server", @config.servers.first.description 28 | end 29 | 30 | test "sets and gets tags" do 31 | tags = [{ name: "Users", description: "Operations about users" }] 32 | @config.tags = tags 33 | assert_equal 1, @config.tags.size 34 | assert_equal "Users", @config.tags.first.name 35 | assert_equal "Operations about users", @config.tags.first.description 36 | end 37 | 38 | test "validates include_mode" do 39 | assert_nothing_raised { @config.include_mode = :with_tags } 40 | assert_nothing_raised { @config.include_mode = :explicit } 41 | assert_raises(ArgumentError) { @config.include_mode = :invalid_mode } 42 | end 43 | 44 | test "validates response_body_of_default" do 45 | assert_nothing_raised { @config.response_body_of_default = "String" } 46 | 47 | assert_raises(ArgumentError) { @config.response_body_of_default = 123 } 48 | end 49 | 50 | test "sets security_schema" do 51 | @config.security_schema = :api_key_header 52 | assert_equal 1, @config.security_schemas.size 53 | assert_equal "apiKey", @config.security_schemas[:api_key_header][:type] 54 | end 55 | 56 | test "ignores invalid security_schema" do 57 | @config.security_schema = :invalid_schema 58 | assert_empty @config.security_schemas 59 | end 60 | 61 | test "dynamic response_body_of_ setters and getters" do 62 | @config.response_body_of_not_found = "String" 63 | assert_equal "String", @config.response_body_of_not_found 64 | 65 | assert_equal @config.response_body_of_default, @config.response_body_of_unauthorized 66 | 67 | assert_raises(ArgumentError) { @config.response_body_of_forbidden = 123 } 68 | end 69 | 70 | test "all dynamic response_body_of_ methods are defined" do 71 | @config.possible_default_responses.each do |response| 72 | assert_respond_to @config, "response_body_of_#{response}=" 73 | assert_respond_to @config, "response_body_of_#{response}" 74 | end 75 | end 76 | 77 | MockRouteExtractor = Struct.new(:host_routes, :host_routes_by_path, :clear_cache, :host_paths, :clean_route) 78 | 79 | test "validates route_extractor" do 80 | # Test with an object that responds to :routes (should work) 81 | mock_route_extractor = MockRouteExtractor.new([], [], proc {}, [], proc {}) 82 | 83 | @config.route_extractor = mock_route_extractor 84 | 85 | assert_equal mock_route_extractor, @config.route_extractor 86 | 87 | # Test with an object that doesn't respond to :routes (should raise error) 88 | assert_raises(ArgumentError) { @config.route_extractor = "invalid" } 89 | assert_raises(ArgumentError) { @config.route_extractor = 123 } 90 | assert_raises(ArgumentError) { @config.route_extractor = Class.new } 91 | end 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Gem Version](https://img.shields.io/gem/v/oas_rails?color=E9573F) 2 | ![GitHub License](https://img.shields.io/github/license/a-chacon/oas_rails?color=blue) 3 | ![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/a-chacon/oas_rails/.github%2Fworkflows%2Frubyonrails.yml) 4 | ![Gem Total Downloads](https://img.shields.io/gem/dt/oas_rails) 5 | ![Static Badge](https://img.shields.io/badge/Rails-%3E%3D7.0.0-%23E9573F) 6 | ![Static Badge](https://img.shields.io/badge/Ruby-%3E%3D3.1.0-%23E9573F) 7 | 8 | # 📃Open API Specification For Rails 9 | 10 | OasRails is a Rails engine for generating **automatic interactive documentation for your Rails APIs**. It generates an **OAS 3.1** document and displays it using **[RapiDoc](https://rapidocweb.com)**. 11 | 12 | It relies on the [OasCore](https://github.com/a-chacon/oas_core) gem. 13 | 14 | ### 🚀 Demo App 15 | 16 | Explore the interactive documentation live: 17 | 18 | 🔗 **[Open Demo App](https://paso.fly.dev/api/docs)** 19 | 👤 **Username**: `oasrails` 20 | 🔑 **Password**: `oasrails` 21 | 22 | 🎬 A Demo Installation/Usage Video: 23 | 24 | 🎬 25 | 26 | ![Screenshot](https://a-chacon.github.io/oas_core/assets/rails_theme.png) 27 | 28 | ## Related Projects 29 | 30 | - **[ApiPie](https://github.com/Apipie/apipie-rails)**: Doesn't support OAS 3.1, requires learning a DSL, lacks a nice UI 31 | - **[swagger_yard-rails](https://github.com/livingsocial/swagger_yard-rails)**: Seems abandoned, but serves as inspiration 32 | - **[Rswag](https://github.com/rswag/rswag)**: Not automatic, depends on RSpec; Many developers now use Minitest as it's the default test framework 33 | - **[grape-swagger](https://github.com/ruby-grape/grape-swagger)**: Requires Grape 34 | - **[rspec_api_documentation](https://github.com/zipmark/rspec_api_documentation)**: Requires RSpec and a command to generate the docs 35 | 36 | ## What Sets OasRails Apart? 37 | 38 | - **Dynamic**: No command required to generate docs 39 | - **Simple**: Complement default documentation with a few comments; no need to learn a complex DSL 40 | - **Pure Ruby on Rails APIs**: No additional frameworks needed (e.g., Grape, RSpec) 41 | 42 | ## 📽️ Motivation 43 | 44 | After experiencing the interactive documentation in Python's fast-api framework, I sought similar functionality in Ruby on Rails. Unable to find a suitable solution, I [asked on Stack Overflow](https://stackoverflow.com/questions/71947018/is-there-a-way-to-generate-an-interactive-documentation-for-rails-apis) years ago. Now, with some free time while freelancing as an API developer, I decided to build my own tool. 45 | 46 | **Note: This is not yet a production-ready solution. The code may be rough and behave unexpectedly, but I am actively working on improving it. If you like the idea, please consider contributing to its development.** 47 | 48 | The goal is to minimize the effort required to create comprehensive documentation. By following REST principles in Rails, we believe this is achievable. You can enhance the documentation using [Yard](https://yardoc.org/) tags. 49 | 50 | ## Documentation 51 | 52 | For see how to install, configure and use OasRails please refere to the [OasCore MDBook](https://a-chacon.github.io/oas_core) 53 | 54 | ## Contributing 55 | 56 | Contributions are what make the open source community such an amazing place to learn, inspire, and create. Any contributions you make are **greatly appreciated**. If you have a suggestion that would make this better, please fork the repo and create a pull request. You can also simply open an issue with the tag "enhancement". Don't forget to give the project a star⭐! Thanks again! 57 | 58 | If you plan a big feature, first open an issue to discuss it before any development. 59 | 60 | 1. Fork the Project 61 | 2. Create your Feature Branch (`git checkout -b feature/AmazingFeature`) 62 | 3. Commit your Changes (`git commit -m 'Add some AmazingFeature'`) 63 | 4. Push to the Branch (`git push origin feature/AmazingFeature`) 64 | 5. Open a Pull Request 65 | 66 | ## License 67 | 68 | OasRails is released under the [MIT License](https://opensource.org/licenses/MIT). 69 | 70 | ## Star History 71 | 72 | [![Star History Chart](https://api.star-history.com/svg?repos=a-chacon/oas_rails&type=Date)](https://www.star-history.com/#a-chacon/oas_rails&Date) 73 | -------------------------------------------------------------------------------- /test/dummy/lib/oas.json: -------------------------------------------------------------------------------- 1 | { 2 | "components": { 3 | "schemas": { 4 | "User": { 5 | "type": "object", 6 | "properties": { 7 | "name": { 8 | "type": "string", 9 | "description": "The user's full name" 10 | }, 11 | "email": { 12 | "type": "string", 13 | "format": "email", 14 | "description": "The user's email address" 15 | }, 16 | "age": { 17 | "type": "integer", 18 | "description": "The user's age", 19 | "minimum": 0 20 | }, 21 | "password": { 22 | "type": "string", 23 | "description": "The user's password", 24 | "minLength": 6 25 | } 26 | }, 27 | "required": [ 28 | "name", 29 | "email", 30 | "password" 31 | ], 32 | "example": { 33 | "name": "John Doe", 34 | "email": "john.doe@example.com", 35 | "age": 30, 36 | "password": "securepassword123" 37 | } 38 | } 39 | }, 40 | "examples": { 41 | "UserExample": { 42 | "summary": "An example of a User object", 43 | "value": { 44 | "name": "Jane Smith", 45 | "email": "jane.smith@example.com", 46 | "age": 28, 47 | "password": "anothersecurepassword" 48 | } 49 | } 50 | }, 51 | "parameters": { 52 | "userId": { 53 | "name": "userId", 54 | "in": "path", 55 | "description": "ID of the user to retrieve", 56 | "required": true, 57 | "schema": { 58 | "type": "string" 59 | }, 60 | "example": "12345" 61 | } 62 | }, 63 | "requestBodies": { 64 | "LoginRequest": { 65 | "description": "A JSON object containing login credentials", 66 | "content": { 67 | "application/json": { 68 | "schema": { 69 | "type": "object", 70 | "properties": { 71 | "email": { 72 | "type": "string", 73 | "format": "email", 74 | "description": "The user's email address" 75 | }, 76 | "password": { 77 | "type": "string", 78 | "description": "The user's password", 79 | "minLength": 6 80 | } 81 | }, 82 | "required": [ 83 | "email", 84 | "password" 85 | ] 86 | }, 87 | "examples": { 88 | "TestUser": { 89 | "summary": "Test user login credentials", 90 | "value": { 91 | "email": "oas@test.com", 92 | "password": "Test12345" 93 | } 94 | }, 95 | "AdminUser": { 96 | "summary": "Admin user login credentials", 97 | "value": { 98 | "email": "admin@example.com", 99 | "password": "AdminSecure123" 100 | } 101 | }, 102 | "GuestUser": { 103 | "summary": "Guest user login credentials", 104 | "value": { 105 | "email": "guest@example.com", 106 | "password": "GuestPass456" 107 | } 108 | } 109 | } 110 | } 111 | } 112 | } 113 | }, 114 | "responses": { 115 | "UserResponse": { 116 | "description": "A JSON object representing a user", 117 | "content": { 118 | "application/json": { 119 | "schema": { 120 | "$ref": "#/components/schemas/User" 121 | }, 122 | "examples": { 123 | "success": { 124 | "summary": "A successful response", 125 | "value": { 126 | "name": "Bob Brown", 127 | "email": "bob.brown@example.com", 128 | "age": 35, 129 | "password": "bobpassword123" 130 | } 131 | } 132 | } 133 | } 134 | } 135 | } 136 | } 137 | } 138 | } 139 | -------------------------------------------------------------------------------- /lib/oas_rails/extractors/route_extractor.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module Extractors 3 | class RouteExtractor 4 | RAILS_DEFAULT_CONTROLLERS = %w[ 5 | rails 6 | action_mailbox 7 | action_cable 8 | active_storage 9 | turbo/native 10 | ].freeze 11 | 12 | class << self 13 | def host_routes_by_path(path) 14 | @host_routes ||= extract_host_routes 15 | @host_routes.select { |r| r.path == path } 16 | end 17 | 18 | def host_routes 19 | @host_routes ||= extract_host_routes 20 | end 21 | 22 | # Clear Class Instance Variable @host_routes 23 | # 24 | # This method clear the class instance variable @host_routes 25 | # to force a extraction of the routes again. 26 | def clear_cache 27 | @host_routes = nil 28 | end 29 | 30 | def host_paths 31 | @host_paths ||= host_routes.map(&:path).uniq.sort 32 | end 33 | 34 | def clean_route(route) 35 | route.gsub('(.:format)', '').gsub(/:\w+/) { |match| "{#{match[1..]}}" } 36 | end 37 | 38 | private 39 | 40 | def extract_host_routes 41 | routes = valid_routes.map { |r| Builders::OasRouteBuilder.build_from_rails_route(r) } 42 | 43 | routes.select! { |route| route.tags.any? } if OasRails.config.include_mode == :with_tags 44 | routes.select! { |route| route.tags.any? { |t| t.tag_name == "oas_include" } } if OasRails.config.include_mode == :explicit 45 | routes 46 | end 47 | 48 | def valid_routes 49 | Rails.application.routes.routes.select do |route| 50 | valid_api_route?(route) 51 | end 52 | end 53 | 54 | def valid_api_route?(route) 55 | return false unless valid_route_implementation?(route) 56 | return false if RAILS_DEFAULT_CONTROLLERS.any? { |default| route.defaults[:controller].start_with?(default) } 57 | return false unless route.path.spec.to_s.start_with?(OasRails.config.api_path) 58 | return false if ignore_custom_actions?(route) 59 | 60 | true 61 | end 62 | 63 | # Checks if a route has a valid implementation. 64 | # 65 | # This method verifies that both the controller and the action specified 66 | # in the route exist. It checks if the controller class is defined and 67 | # if the action method is implemented within that controller. 68 | # 69 | # @param route [ActionDispatch::Journey::Route] The route to check. 70 | # @return [Boolean] true if both the controller and action exist, false otherwise. 71 | def valid_route_implementation?(route) 72 | controller_name = route.defaults[:controller]&.camelize 73 | action_name = route.defaults[:action] 74 | 75 | return false if controller_name.blank? || action_name.blank? 76 | 77 | controller_class = "#{controller_name}Controller".safe_constantize 78 | 79 | if controller_class.nil? 80 | false 81 | else 82 | controller_class.instance_methods.include?(action_name.to_sym) 83 | end 84 | end 85 | 86 | # Ignore user-specified paths in initializer configuration. 87 | # Sanitize api_path by removing the "/" if it starts with that, and adding "/" if it ends without that. 88 | # Support controller name only to ignore all controller actions. 89 | # Support ignoring "controller#action" 90 | # Ignoring "controller#action" AND "api_path/controller#action" 91 | def ignore_custom_actions?(route) 92 | api_path = "#{OasRails.config.api_path.sub(%r{\A/}, '')}/".sub(%r{/+$}, '/') 93 | ignored_actions = OasRails.config.ignored_actions.flat_map do |custom_route| 94 | if custom_route.start_with?(api_path) 95 | [custom_route] 96 | else 97 | ["#{api_path}#{custom_route}", custom_route] 98 | end 99 | end 100 | 101 | controller_action = "#{route.defaults[:controller]}##{route.defaults[:action]}" 102 | controller_only = route.defaults[:controller] 103 | 104 | ignored_actions.include?(controller_action) || ignored_actions.include?(controller_only) 105 | end 106 | end 107 | end 108 | end 109 | end 110 | -------------------------------------------------------------------------------- /lib/oas_rails/utils.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module Utils 3 | TYPE_MAPPING = { 4 | 'String' => 'string', 5 | 'Integer' => 'number', 6 | 'Float' => 'number', 7 | 'TrueClass' => 'boolean', 8 | 'FalseClass' => 'boolean', 9 | 'Boolean' => 'boolean', 10 | 'NilClass' => 'null', 11 | 'Hash' => 'object', 12 | 'Object' => 'object', 13 | 'DateTime' => 'string' 14 | }.freeze 15 | 16 | HTTP_STATUS_DEFINITIONS = { 17 | 200 => "The request has succeeded.", 18 | 201 => "The request has been fulfilled and resulted in a new resource being created.", 19 | 404 => "The requested resource could not be found.", 20 | 401 => "You are not authorized to access this resource. You need to authenticate yourself first.", 21 | 403 => "You are not allowed to access this resource. You do not have the necessary permissions.", 22 | 500 => "An unexpected error occurred on the server. The server was unable to process the request.", 23 | 422 => "The server could not process the request due to semantic errors. Please check your input and try again." 24 | }.freeze 25 | 26 | class << self 27 | # Method for detect test framework of the Rails App 28 | # It is used for generate examples in operations 29 | def detect_test_framework 30 | if defined?(FactoryBot) 31 | :factory_bot 32 | elsif ActiveRecord::Base.connection.table_exists?('ar_internal_metadata') 33 | :fixtures 34 | else 35 | :unknown 36 | end 37 | end 38 | 39 | def hash_to_json_schema(hash) 40 | { 41 | type: 'object', 42 | properties: hash_to_properties(hash), 43 | required: [] 44 | } 45 | end 46 | 47 | def hash_to_properties(hash) 48 | hash.transform_values do |value| 49 | if value.is_a?(Hash) 50 | hash_to_json_schema(value) 51 | elsif value.is_a?(Class) 52 | { type: ruby_type_to_json_type(value.name) } 53 | else 54 | { type: ruby_type_to_json_type(value.class.name) } 55 | end 56 | end 57 | end 58 | 59 | def ruby_type_to_json_type(ruby_type) 60 | TYPE_MAPPING.fetch(ruby_type, 'string') 61 | end 62 | 63 | # Converts a status symbol or string to an integer. 64 | # 65 | # @param status [String, Symbol, nil] The status to convert. 66 | # @return [Integer] The status code as an integer. 67 | def status_to_integer(status) 68 | return 200 if status.nil? 69 | 70 | if status.to_s =~ /^\d+$/ 71 | status.to_i 72 | else 73 | Rack::Utils.status_code(status.to_sym) 74 | end 75 | end 76 | 77 | # Converts a status code to its corresponding text description. 78 | # 79 | # @param status_code [Integer] The status code. 80 | # @return [String] The text description of the status code. 81 | def get_definition(status_code) 82 | HTTP_STATUS_DEFINITIONS[status_code] || "Definition not found for status code #{status_code}" 83 | end 84 | 85 | def class_to_symbol(klass) 86 | klass.name.underscore.to_sym 87 | end 88 | 89 | def find_model_from_route(path) 90 | parts = path.split('/') 91 | model_name = parts.last.singularize.camelize 92 | 93 | namespace_combinations = (0..parts.size).map do |i| 94 | parts.first(i).map(&:camelize).join('::') 95 | end 96 | 97 | namespace_combinations.reverse.each do |namespace| 98 | full_class_name = [namespace, model_name].reject(&:empty?).join('::') 99 | begin 100 | return full_class_name.constantize 101 | rescue NameError 102 | next 103 | end 104 | end 105 | 106 | nil # Return nil if no matching constant is found 107 | end 108 | 109 | # Checks if a given text refers to an ActiveRecord class. 110 | # @param text [String] The text to check. 111 | # @return [Boolean] True if the text refers to an ActiveRecord class, false otherwise. 112 | def active_record_class?(klass_or_string) 113 | klass = klass_or_string.is_a?(Class) ? klass_or_string : klass_or_string.constantize 114 | klass.ancestors.map(&:to_s).include? 'ActiveRecord::Base' 115 | rescue StandardError 116 | false 117 | end 118 | end 119 | end 120 | end 121 | -------------------------------------------------------------------------------- /test/dummy/config/environments/production.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/integer/time" 2 | 3 | Rails.application.configure do 4 | # Settings specified here will take precedence over those in config/application.rb. 5 | 6 | # Code is not reloaded between requests. 7 | config.enable_reloading = false 8 | 9 | # Eager load code on boot. This eager loads most of Rails and 10 | # your application in memory, allowing both threaded web servers 11 | # and those relying on copy on write to perform better. 12 | # Rake tasks automatically ignore this option for performance. 13 | config.eager_load = true 14 | 15 | # Full error reports are disabled and caching is turned on. 16 | config.consider_all_requests_local = false 17 | config.action_controller.perform_caching = true 18 | 19 | # Ensures that a master key has been made available in ENV["RAILS_MASTER_KEY"], config/master.key, or an environment 20 | # key such as config/credentials/production.key. This key is used to decrypt credentials (and other encrypted files). 21 | # config.require_master_key = true 22 | 23 | # Disable serving static files from `public/`, relying on NGINX/Apache to do so instead. 24 | # config.public_file_server.enabled = false 25 | 26 | # Compress CSS using a preprocessor. 27 | # config.assets.css_compressor = :sass 28 | 29 | # Do not fall back to assets pipeline if a precompiled asset is missed. 30 | config.assets.compile = false 31 | 32 | # Enable serving of images, stylesheets, and JavaScripts from an asset server. 33 | # config.asset_host = "http://assets.example.com" 34 | 35 | # Specifies the header that your server uses for sending files. 36 | # config.action_dispatch.x_sendfile_header = "X-Sendfile" # for Apache 37 | # config.action_dispatch.x_sendfile_header = "X-Accel-Redirect" # for NGINX 38 | 39 | # Store uploaded files on the local file system (see config/storage.yml for options). 40 | config.active_storage.service = :local 41 | 42 | # Mount Action Cable outside main process or domain. 43 | # config.action_cable.mount_path = nil 44 | # config.action_cable.url = "wss://example.com/cable" 45 | # config.action_cable.allowed_request_origins = [ "http://example.com", /http:\/\/example.*/ ] 46 | 47 | # Assume all access to the app is happening through a SSL-terminating reverse proxy. 48 | # Can be used together with config.force_ssl for Strict-Transport-Security and secure cookies. 49 | # config.assume_ssl = true 50 | 51 | # Force all access to the app over SSL, use Strict-Transport-Security, and use secure cookies. 52 | config.force_ssl = true 53 | 54 | # Log to STDOUT by default 55 | config.logger = ActiveSupport::Logger.new(STDOUT) 56 | .tap { |logger| logger.formatter = ::Logger::Formatter.new } 57 | .then { |logger| ActiveSupport::TaggedLogging.new(logger) } 58 | 59 | # Prepend all log lines with the following tags. 60 | config.log_tags = [ :request_id ] 61 | 62 | # "info" includes generic and useful information about system operation, but avoids logging too much 63 | # information to avoid inadvertent exposure of personally identifiable information (PII). If you 64 | # want to log everything, set the level to "debug". 65 | config.log_level = ENV.fetch("RAILS_LOG_LEVEL", "info") 66 | 67 | # Use a different cache store in production. 68 | # config.cache_store = :mem_cache_store 69 | 70 | # Use a real queuing backend for Active Job (and separate queues per environment). 71 | # config.active_job.queue_adapter = :resque 72 | # config.active_job.queue_name_prefix = "dummy_production" 73 | 74 | config.action_mailer.perform_caching = false 75 | 76 | # Ignore bad email addresses and do not raise email delivery errors. 77 | # Set this to true and configure the email server for immediate delivery to raise delivery errors. 78 | # config.action_mailer.raise_delivery_errors = false 79 | 80 | # Enable locale fallbacks for I18n (makes lookups for any locale fall back to 81 | # the I18n.default_locale when a translation cannot be found). 82 | config.i18n.fallbacks = true 83 | 84 | # Don't log any deprecations. 85 | config.active_support.report_deprecations = false 86 | 87 | # Do not dump schema after migrations. 88 | config.active_record.dump_schema_after_migration = false 89 | 90 | # Enable DNS rebinding protection and other `Host` header attacks. 91 | # config.hosts = [ 92 | # "example.com", # Allow requests from example.com 93 | # /.*\.example\.com/ # Allow requests from subdomains like `www.example.com` 94 | # ] 95 | # Skip DNS rebinding protection for the default health check endpoint. 96 | # config.host_authorization = { exclude: ->(request) { request.path == "/up" } } 97 | end 98 | -------------------------------------------------------------------------------- /app/helpers/oas_rails/oas_rails_helper.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module OasRailsHelper # rubocop:disable Metrics/ModuleLength 3 | def rapidoc_configuration_defaults 4 | { 5 | "spec-url" => "#{OasRails.config.mounted_path}.json", 6 | "show-header" => "false", 7 | "font-size" => "largest", 8 | "show-method-in-nav-bar" => "as-colored-text", 9 | "nav-item-spacing" => "relaxed", 10 | "allow-spec-file-download" => "true", 11 | "schema-style" => "table", 12 | "sort-tags" => "true", 13 | "persist-auth" => "true" 14 | } 15 | end 16 | 17 | def rapidoc_configuration_attributes 18 | rapidoc_configuration_defaults.merge( 19 | rapidoc_theme(OasRails.config.rapidoc_theme), 20 | OasRails.config.rapidoc_configuration 21 | ).map { |k, v| %(#{k}=#{ERB::Util.html_escape(v)}) }.join(' ') 22 | end 23 | 24 | def rapidoc_logo_url 25 | OasRails.config.rapidoc_logo_url 26 | end 27 | 28 | THEMES = { 29 | 'dark' => { 30 | 'theme' => 'dark', 31 | 'bg-color' => '#333', 32 | 'text-color' => '#BBB' 33 | }, 34 | 'light' => { 35 | 'theme' => 'light', 36 | 'bg-color' => '#FFF', 37 | 'text-color' => '#444' 38 | }, 39 | 'night' => { 40 | 'theme' => 'dark', 41 | 'bg-color' => '#14191f', 42 | 'text-color' => '#aec2e0' 43 | }, 44 | 'mud' => { 45 | 'theme' => 'dark', 46 | 'bg-color' => '#403635', 47 | 'text-color' => '#c3b8b7' 48 | }, 49 | 'cofee' => { 50 | 'theme' => 'dark', 51 | 'bg-color' => '#36312C', 52 | 'text-color' => '#ceb8a0' 53 | }, 54 | 'forest' => { 55 | 'theme' => 'dark', 56 | 'bg-color' => '#384244', 57 | 'text-color' => '#BDD6DB' 58 | }, 59 | 'olive' => { 60 | 'theme' => 'dark', 61 | 'bg-color' => '#2a2f31', 62 | 'text-color' => '#acc7c8' 63 | }, 64 | 'outerspace' => { 65 | 'theme' => 'dark', 66 | 'bg-color' => '#2D3133', 67 | 'text-color' => '#CAD9E3' 68 | }, 69 | 'ebony' => { 70 | 'theme' => 'dark', 71 | 'bg-color' => '#2B303B', 72 | 'text-color' => '#dee3ec' 73 | }, 74 | 'snow' => { 75 | 'theme' => 'light', 76 | 'bg-color' => '#FAFAFA', 77 | 'text-color' => '#555' 78 | }, 79 | 'green' => { 80 | 'theme' => 'light', 81 | 'bg-color' => '#f9fdf7', 82 | 'text-color' => '#375F1B' 83 | }, 84 | 'blue' => { 85 | 'theme' => 'light', 86 | 'bg-color' => '#ecf1f7', 87 | 'text-color' => '#133863' 88 | }, 89 | 'beige' => { 90 | 'show-header' => 'true', 91 | 'theme' => 'light', 92 | 'bg-color' => '#fdf8ed', 93 | 'text-color' => '#342809' 94 | }, 95 | 'graynav' => { 96 | 'show-header' => 'false', 97 | 'theme' => 'light', 98 | 'nav-bg-color' => '#3e4b54', 99 | 'nav-accent-color' => '#fd6964', 100 | 'primary-color' => '#ea526f' 101 | }, 102 | 'purplenav' => { 103 | 'show-header' => 'false', 104 | 'theme' => 'light', 105 | 'nav-accent-color' => '#ffd8e7', 106 | 'nav-bg-color' => '#666699', 107 | 'primary-color' => '#ea526f', 108 | 'bg-color' => '#fff9fb' 109 | }, 110 | 'lightgraynav' => { 111 | 'show-header' => 'false', 112 | 'theme' => 'light', 113 | 'nav-bg-color' => '#fafafa', 114 | 'nav-hover-text-color' => '#9b0700', 115 | 'nav-hover-bg-color' => '#ffebea', 116 | 'primary-color' => '#F63C41', 117 | 'bg-color' => '#ffffff' 118 | }, 119 | 'darkbluenav' => { 120 | 'show-header' => 'false', 121 | 'theme' => 'light', 122 | 'bg-color' => '#f9f9fa', 123 | 'nav-bg-color' => '#3f4d67', 124 | 'nav-text-color' => '#a9b7d0', 125 | 'nav-hover-bg-color' => '#333f54', 126 | 'nav-hover-text-color' => '#fff', 127 | 'nav-accent-color' => '#f87070', 128 | 'primary-color' => '#5c7096' 129 | }, 130 | 'rails' => { 131 | 'theme' => 'light', 132 | 'bg-color' => '#FFFFFF', 133 | 'nav-bg-color' => '#101828', 134 | 'nav-text-color' => '#fff', 135 | 'nav-hover-bg-color' => '#261B23', 136 | 'nav-hover-text-color' => '#fff', 137 | 'nav-accent-color' => '#D30001', 138 | 'primary-color' => '#D30001' 139 | } 140 | }.freeze 141 | 142 | def rapidoc_theme(theme_name) 143 | THEMES[theme_name] || {} 144 | end 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /test/dummy/app/controllers/users_controller.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Manage Users Here 4 | # 5 | # @response_example Another 405 Error (405) 6 | # [JSON 7 | # { 8 | # "message": "another", 9 | # "data": { 10 | # "availabilities": [ 11 | # "three" 12 | # ], 13 | # "dates": [] 14 | # } 15 | # } 16 | # ] 17 | class UsersController < ApplicationController 18 | before_action :authorize!, except: [:create, :login] 19 | before_action :set_user, only: %i[show update destroy] 20 | 21 | # @summary Login 22 | # @request_body_ref #/components/requestBodies/LoginRequest 23 | # @no_auth 24 | def login 25 | @user = User.find_by_email(params[:email]) 26 | if @user&.authenticate(params[:password]) 27 | token = JsonWebToken.encode(user_id: @user.id) 28 | time = Time.now + 24.hours.to_i 29 | render json: { token:, exp: time.strftime("%m-%d-%Y %H:%M"), 30 | username: @user.name }, status: :ok 31 | else 32 | render json: { error: 'unauthorized' }, status: :unauthorized 33 | end 34 | end 35 | 36 | # Returns a list of Users. 37 | # 38 | # Status can be -1(Deleted), 0(Inactive), 1(Active), 2(Expired) and 3(Cancelled). 39 | # 40 | # 41 | # @parameter offset(query) [Integer] Used for pagination of response data (default: 25 items per response). Specifies the offset of the next block of data to receive. 42 | # @parameter status(query) [!String] Filter by status. (e.g. status=inactive). 43 | # @parameter stages(query) [Array] Filter by stages. (e.g. status=inactive). 44 | # @parameter_ref #/components/parameters/userId 45 | # @response Users list(200) [Array] 46 | def index 47 | @users = User.all 48 | 49 | render json: @users 50 | end 51 | 52 | # @summary Get a user by id. 53 | # 54 | # This method show a User by ID. The id must exist of other way it will be returning a 404. 55 | # @parameter id(path) [!Integer] Used for identify the user. 56 | # @response A nice user(200) [Reference:#/components/schemas/User] 57 | # @response_ref (201) #/components/responses/UserResponse 58 | # @response User not found by the provided Id(404) [Hash{success: Boolean, message: String}] 59 | # @response You dont have the rigth persmissions for access to this reasource(403) [Hash{success: Boolean, message: String}] 60 | # @response A test response from an Issue(405) 61 | # [ 62 | # Hash{ 63 | # message: String, 64 | # data: Hash{ 65 | # availabilities: Array, 66 | # dates: Array 67 | # } 68 | # } 69 | # ] 70 | # 71 | # @response_example Nice 405 Error(405) 72 | # [JSON{ "message": "Hello", "data": { "availabilities": ["one", "two", "three"], "dates": ["10-06-2020"]}}] 73 | def show 74 | render json: @user 75 | end 76 | 77 | # @summary Create a User New 78 | # @no_auth 79 | # @tags 1. First 80 | # 81 | # To act as connected accounts, clients can issue requests using the Stripe-Account special header. Make sure that this header contains a Stripe account ID, which usually starts with the acct_ prefix. 82 | # The value is set per-request as shown in the adjacent code sample. Methods on the returned object reuse the same account ID.ased on the strings 83 | # 84 | # @request_body The user to be created. At least include an `email`. [!User] 85 | # @request_body_example basic user 86 | # [JSON 87 | # { 88 | # "user": { 89 | # "name": "Oas", 90 | # "email": "oas@test.com", 91 | # "password": "Test12345" 92 | # } 93 | # } 94 | # ] 95 | def create 96 | @user = User.new(user_params) 97 | 98 | if @user.save 99 | render json: @user, status: :created 100 | else 101 | render json: { success: false, errors: @user.errors }, status: :unprocessable_entity 102 | end 103 | end 104 | 105 | # Update a user. 106 | # @tags 2. Second 107 | # A `user` can be updated with this method 108 | # - There is no option 109 | # - It must work 110 | # @request_body User to be created [Reference:#/components/schemas/User] 111 | # @request_body_example Update user [Reference:#/components/examples/UserExample] 112 | # @request_body_example Complete User [JSON{"user": {"name": "Luis", "email": "luis@gmail.com", "age": 21}}] 113 | def update 114 | if @user.update(user_params) 115 | render json: @user 116 | else 117 | render json: @user.errors, status: :unprocessable_entity 118 | end 119 | end 120 | 121 | # DELETE /users/1 122 | # @oas_include 123 | def destroy 124 | @user.destroy! 125 | redirect_to users_url, notice: 'User was successfully destroyed.', status: :see_other 126 | end 127 | 128 | private 129 | 130 | # Use callbacks to share common setup or constraints between actions. 131 | def set_user 132 | @user = User.find(params[:id]) 133 | end 134 | 135 | # Only allow a list of trusted parameters through. 136 | def user_params 137 | params.require(:user).permit(:name, :email, :password) 138 | end 139 | 140 | def login_params 141 | params.permit(:email, :password) 142 | end 143 | end 144 | -------------------------------------------------------------------------------- /lib/generators/oas_rails/config/templates/oas_rails_initializer.rb: -------------------------------------------------------------------------------- 1 | # config/initializers/oas_rails.rb 2 | OasRails.configure do |config| 3 | # Basic Information about the API 4 | config.info.title = 'OasRails' 5 | config.info.version = '1.0.0' 6 | config.info.summary = 'OasRails: Automatic Interactive API Documentation for Rails' 7 | config.info.description = <<~HEREDOC 8 | # Welcome to OasRails 9 | 10 | OasRails automatically generates interactive documentation for your Rails APIs using the OpenAPI Specification 3.1 (OAS 3.1) and displays it with a nice UI. 11 | 12 | ## Getting Started 13 | 14 | You've successfully mounted the OasRails engine. This default documentation is based on your routes and automatically gathered information. 15 | 16 | ## Enhancing Your Documentation 17 | 18 | To customize and enrich your API documentation: 19 | 20 | 1. Generate an initializer file: 21 | 22 | ``` 23 | rails generate oas_rails:config 24 | ``` 25 | 2. Edit the created `config/initializers/oas_rails.rb` file to override default settings and add project-specific information. 26 | 27 | 3. Use Yard tags in your controller methods to provide detailed API endpoint descriptions. 28 | 29 | Docs: 30 | 31 | ## Features 32 | 33 | - Automatic OAS 3.1 document generation 34 | - [RapiDoc](https://github.com/rapi-doc/RapiDoc) integration for interactive exploration 35 | - Minimal setup required for basic documentation 36 | - Extensible through configuration and Yard tags 37 | 38 | Explore your API documentation and enjoy the power of OasRails! 39 | 40 | For more information and advanced usage, visit the [OasRails GitHub repository](https://github.com/a-chacon/oas_rails). 41 | HEREDOC 42 | config.info.contact.name = 'a-chacon' 43 | config.info.contact.email = 'andres.ch@proton.me' 44 | config.info.contact.url = 'https://a-chacon.com' 45 | 46 | # Servers Information. For more details follow: https://spec.openapis.org/oas/latest.html#server-object 47 | config.servers = [{ url: 'http://localhost:3000', description: 'Local' }] 48 | 49 | # Tag Information. For more details follow: https://spec.openapis.org/oas/latest.html#tag-object 50 | config.tags = [{ name: "Users", description: "Manage the `amazing` Users table." }] 51 | 52 | # Optional Settings (Uncomment to use) 53 | 54 | # Extract default tags of operations from namespace or controller. Can be set to :namespace or :controller 55 | # config.default_tags_from = :namespace 56 | 57 | # Automatically detect request bodies for create/update methods 58 | # Default: true 59 | # config.autodiscover_request_body = false 60 | 61 | # Automatically detect responses from controller renders 62 | # Default: true 63 | # config.autodiscover_responses = false 64 | 65 | # API path configuration if your API is under a different namespace 66 | # config.api_path = "/" 67 | 68 | # Apply your custom layout. Should be the name of your layout file 69 | # Example: "application" if file named application.html.erb 70 | # Default: false 71 | # config.layout = "application" 72 | 73 | # Override general rapidoc settings 74 | # config.rapidoc_configuration 75 | # default: {} 76 | 77 | # Add a logo to rapidoc 78 | # config.rapidoc_logo_url 79 | # default: nil 80 | 81 | # Override specific rapidoc theme settings 82 | # config.rapidoc_theme_configuration 83 | # default: {} 84 | 85 | # Excluding custom controllers or controllers#action 86 | # Example: ["projects", "users#new"] 87 | # config.ignored_actions = [] 88 | 89 | # ####################### 90 | # Authentication Settings 91 | # ####################### 92 | 93 | # Whether to authenticate all routes by default 94 | # Default is true; set to false if you don't want all routes to include security schemas by default 95 | # config.authenticate_all_routes_by_default = true 96 | 97 | # Default security schema used for authentication 98 | # Choose a predefined security schema 99 | # [:api_key_cookie, :api_key_header, :api_key_query, :basic, :bearer, :bearer_jwt, :mutual_tls] 100 | # config.security_schema = :bearer 101 | 102 | # Custom security schemas 103 | # You can uncomment and modify to use custom security schemas 104 | # Please follow the documentation: https://spec.openapis.org/oas/latest.html#security-scheme-object 105 | # 106 | # config.security_schemas = { 107 | # bearer:{ 108 | # "type": "apiKey", 109 | # "name": "api_key", 110 | # "in": "header" 111 | # } 112 | # } 113 | 114 | # ########################### 115 | # Default Responses (Errors) 116 | # ########################### 117 | 118 | # The default responses errors are set only if the action allow it. 119 | # Example, if you add forbidden then it will be added only if the endpoint requires authentication. 120 | # Example: not_found will be setted to the endpoint only if the operation is a show/update/destroy action. 121 | # config.set_default_responses = true 122 | # config.possible_default_responses = [:not_found, :unauthorized, :forbidden, :internal_server_error, :unprocessable_entity] 123 | # config.response_body_of_default = "Hash{ message: String }" 124 | # config.response_body_of_unprocessable_entity= "Hash{ errors: Array }" 125 | end 126 | -------------------------------------------------------------------------------- /lib/oas_rails/extractors/render_response_extractor.rb: -------------------------------------------------------------------------------- 1 | module OasRails 2 | module Extractors 3 | # Extracts and processes render responses from a given source. 4 | module RenderResponseExtractor 5 | class << self 6 | # Extracts responses from the provided source string. 7 | # 8 | # @param source [String] The source string containing render calls. 9 | # @return [Array] An array of Response objects extracted from the source. 10 | def extract_responses_from_source(specification, source:) 11 | render_calls = extract_render_calls(source) 12 | return [Builders::ResponseBuilder.new(specification).with_description("No content").with_code(204).build] if render_calls.empty? 13 | 14 | render_calls.map { |render_content, status| process_render_content(specification, render_content.strip, status) } 15 | end 16 | 17 | private 18 | 19 | # Extracts render calls from the source string. 20 | # 21 | # @param source [String] The source string containing render calls. 22 | # @return [Array>] An array of arrays, each containing render content and status. 23 | def extract_render_calls(source) 24 | source.scan(/render json: ((?:\{.*?\}|\S+))(?:, status: :(\w+))?(?:,.*?)?$/m) 25 | end 26 | 27 | # Processes the render content and status to build a Response object. 28 | # 29 | # @param content [String] The content extracted from the render call. 30 | # @param status [String] The status code associated with the render call. 31 | # @return [Response] A Response object based on the processed content and status. 32 | def process_render_content(specification, content, status) 33 | schema, examples = build_schema_and_examples(content) 34 | status_int = Utils.status_to_integer(status) 35 | content = Builders::ContentBuilder.new(specification, :outgoing).with_schema(schema).with_examples(examples).build 36 | 37 | Builders::ResponseBuilder.new(specification).with_code(status_int).with_description(Utils.get_definition(status_int)).with_content(content).build 38 | end 39 | 40 | # Builds schema and examples based on the content type. 41 | # 42 | # @param content [String] The content extracted from the render call. 43 | # @return [Array] An array where the first element is the schema and the second is the examples. 44 | def build_schema_and_examples(content) 45 | if content.start_with?('{') 46 | [Utils.hash_to_json_schema(parse_hash_structure(content)), {}] 47 | else 48 | process_non_hash_content(content) 49 | end 50 | rescue StandardError => e 51 | Rails.logger.debug("Error building schema: #{e.message}") 52 | [{}] 53 | end 54 | 55 | # Processes non-hash content (e.g., model or method calls) to build schema and examples. 56 | # 57 | # @param content [String] The content extracted from the render call. 58 | # @return [Array] An array where the first element is the schema and the second is the examples. 59 | def process_non_hash_content(content) 60 | maybe_a_model, errors = content.gsub('@', "").split(".") 61 | klass = maybe_a_model.singularize.camelize(:upper).constantize 62 | 63 | if Utils.active_record_class?(klass) 64 | schema = Builders::EsquemaBuilder.build_outgoing_schema(klass:) 65 | if test_singularity?(maybe_a_model) 66 | build_singular_model_schema_and_examples(maybe_a_model, errors, klass, schema) 67 | else 68 | build_array_model_schema_and_examples(maybe_a_model, klass, schema) 69 | end 70 | else 71 | [{}] 72 | end 73 | end 74 | 75 | # Builds schema and examples for singular models. 76 | # 77 | # @param maybe_a_model [String] The model name or variable. 78 | # @param errors [String, nil] Errors related to the model. 79 | # @param klass [Class] The class associated with the model. 80 | # @param schema [Hash] The schema for the model. 81 | # @return [Array] An array where the first element is the schema and the second is the examples. 82 | def build_singular_model_schema_and_examples(_maybe_a_model, errors, klass, schema) 83 | if errors.nil? 84 | [schema, ActiveRecordExampleFinder.new(context: :outgoing).search(klass)] 85 | else 86 | # TODO: this is not building the real schema. 87 | [ 88 | { 89 | type: "object", 90 | properties: { 91 | success: { type: "boolean" }, 92 | errors: { 93 | type: "object", 94 | additionalProperties: { 95 | type: "array", 96 | items: { type: "string" } 97 | } 98 | } 99 | } 100 | }, 101 | {} 102 | ] 103 | end 104 | end 105 | 106 | # Builds schema and examples for array models. 107 | # 108 | # @param maybe_a_model [String] The model name or variable. 109 | # @param klass [Class] The class associated with the model. 110 | # @param schema [Hash] The schema for the model. 111 | # @return [Array] An array where the first element is the schema and the second is the examples. 112 | def build_array_model_schema_and_examples(maybe_a_model, klass, schema) 113 | examples = { maybe_a_model => { value: ActiveRecordExampleFinder.new(context: :outgoing).search(klass).values.map { |p| p.dig(:value, maybe_a_model.singularize.to_sym) } } } 114 | [{ type: "array", items: schema }, examples] 115 | end 116 | 117 | # Determines if a string represents a singular model. 118 | # 119 | # @param str [String] The string to test. 120 | # @return [Boolean] True if the string is a singular model, false otherwise. 121 | def test_singularity?(str) 122 | str.pluralize != str && str.singularize == str 123 | end 124 | 125 | # Parses a hash literal to determine its structure. 126 | # 127 | # @param hash_literal [String] The hash literal string. 128 | # @return [Hash] A hash representing the structure of the input. 129 | def parse_hash_structure(hash_literal) 130 | structure = {} 131 | 132 | hash_literal.scan(/(\w+):\s*(\S+)/) do |key, value| 133 | structure[key.to_sym] = case value 134 | when 'true', 'false' 135 | 'Boolean' 136 | when /^\d+$/ 137 | 'Number' 138 | else 139 | 'Object' 140 | end 141 | end 142 | 143 | structure 144 | end 145 | end 146 | end 147 | end 148 | end 149 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2024-07-29 15:59:11 UTC using RuboCop version 1.65.0. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 1 10 | # This cop supports safe autocorrection (--autocorrect). 11 | # Configuration parameters: Severity, Include. 12 | # Include: **/*.gemspec 13 | Gemspec/RequireMFA: 14 | Exclude: 15 | - 'oas_rails.gemspec' 16 | 17 | # Offense count: 2 18 | # This cop supports safe autocorrection (--autocorrect). 19 | # Configuration parameters: EnforcedStyle, IndentationWidth. 20 | # SupportedStyles: aligned, indented, indented_relative_to_receiver 21 | Layout/MultilineMethodCallIndentation: 22 | Exclude: 23 | - 'test/dummy/config/environments/production.rb' 24 | 25 | # Offense count: 2 26 | # This cop supports safe autocorrection (--autocorrect). 27 | # Configuration parameters: EnforcedStyle, EnforcedStyleForEmptyBrackets. 28 | # SupportedStyles: space, no_space, compact 29 | # SupportedStylesForEmptyBrackets: space, no_space 30 | Layout/SpaceInsideArrayLiteralBrackets: 31 | Exclude: 32 | - 'test/dummy/config/environments/production.rb' 33 | 34 | # Offense count: 1 35 | # This cop supports safe autocorrection (--autocorrect). 36 | Lint/AmbiguousOperatorPrecedence: 37 | Exclude: 38 | - 'lib/oas_rails/operation.rb' 39 | 40 | # Offense count: 2 41 | # Configuration parameters: IgnoreLiteralBranches, IgnoreConstantBranches. 42 | Lint/DuplicateBranch: 43 | Exclude: 44 | - 'lib/oas_rails/media_type.rb' 45 | - 'lib/oas_rails/oas_route.rb' 46 | 47 | # Offense count: 1 48 | # Configuration parameters: AllowedParentClasses. 49 | Lint/MissingSuper: 50 | Exclude: 51 | - 'lib/oas_rails/server.rb' 52 | 53 | # Offense count: 7 54 | # This cop supports safe autocorrection (--autocorrect). 55 | # Configuration parameters: EnforcedStyle. 56 | # SupportedStyles: strict, consistent 57 | Lint/SymbolConversion: 58 | Exclude: 59 | - 'lib/oas_rails/parameter.rb' 60 | 61 | # Offense count: 4 62 | # Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes. 63 | Metrics/AbcSize: 64 | Max: 32 65 | 66 | # Offense count: 2 67 | # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. 68 | # AllowedMethods: refine 69 | Metrics/BlockLength: 70 | Max: 40 71 | 72 | # Offense count: 1 73 | # Configuration parameters: CountComments, CountAsOne. 74 | Metrics/ClassLength: 75 | Max: 115 76 | 77 | # Offense count: 2 78 | # Configuration parameters: AllowedMethods, AllowedPatterns. 79 | Metrics/CyclomaticComplexity: 80 | Max: 11 81 | 82 | # Offense count: 11 83 | # Configuration parameters: CountComments, CountAsOne, AllowedMethods, AllowedPatterns. 84 | Metrics/MethodLength: 85 | Max: 44 86 | 87 | # Offense count: 1 88 | # Configuration parameters: CountKeywordArgs, MaxOptionalParameters. 89 | Metrics/ParameterLists: 90 | Max: 6 91 | 92 | # Offense count: 1 93 | # Configuration parameters: AllowedMethods, AllowedPatterns. 94 | Metrics/PerceivedComplexity: 95 | Max: 11 96 | 97 | # Offense count: 2 98 | Security/Eval: 99 | Exclude: 100 | - 'lib/oas_rails/yard/oas_yard_factory.rb' 101 | 102 | # Offense count: 33 103 | # Configuration parameters: AllowedConstants. 104 | Style/Documentation: 105 | Enabled: false 106 | 107 | # Offense count: 81 108 | # This cop supports unsafe autocorrection (--autocorrect-all). 109 | # Configuration parameters: EnforcedStyle. 110 | # SupportedStyles: always, always_true, never 111 | Style/FrozenStringLiteralComment: 112 | Enabled: false 113 | 114 | # Offense count: 1 115 | # This cop supports unsafe autocorrection (--autocorrect-all). 116 | Style/GlobalStdStream: 117 | Exclude: 118 | - 'test/dummy/config/environments/production.rb' 119 | 120 | # Offense count: 2 121 | # This cop supports safe autocorrection (--autocorrect). 122 | # Configuration parameters: EnforcedStyle, EnforcedShorthandSyntax, UseHashRocketsWithSymbolValues, PreferHashRocketsForNonAlnumEndingSymbols. 123 | # SupportedStyles: ruby19, hash_rockets, no_mixed_keys, ruby19_no_mixed_keys 124 | # SupportedShorthandSyntax: always, never, either, consistent, either_consistent 125 | Style/HashSyntax: 126 | Exclude: 127 | - 'lib/oas_rails/path_item.rb' 128 | 129 | # Offense count: 1 130 | Style/MultilineBlockChain: 131 | Exclude: 132 | - 'lib/oas_rails/route_extractor.rb' 133 | 134 | # Offense count: 1 135 | # This cop supports unsafe autocorrection (--autocorrect-all). 136 | # Configuration parameters: EnforcedStyle. 137 | # SupportedStyles: literals, strict 138 | Style/MutableConstant: 139 | Exclude: 140 | - 'lib/oas_rails/version.rb' 141 | 142 | # Offense count: 5 143 | # This cop supports safe autocorrection (--autocorrect). 144 | Style/NegatedIfElseCondition: 145 | Exclude: 146 | - 'lib/oas_rails/operation.rb' 147 | - 'lib/oas_rails/yard/oas_yard_factory.rb' 148 | 149 | # Offense count: 1 150 | # This cop supports safe autocorrection (--autocorrect). 151 | # Configuration parameters: MinDigits, Strict, AllowedNumbers, AllowedPatterns. 152 | Style/NumericLiterals: 153 | Exclude: 154 | - 'test/dummy/db/schema.rb' 155 | 156 | # Offense count: 11 157 | # This cop supports safe autocorrection (--autocorrect). 158 | # Configuration parameters: . 159 | # SupportedStyles: same_as_string_literals, single_quotes, double_quotes 160 | Style/QuotedSymbols: 161 | EnforcedStyle: double_quotes 162 | 163 | # Offense count: 1 164 | # This cop supports safe autocorrection (--autocorrect). 165 | Style/RedundantConstantBase: 166 | Exclude: 167 | - 'test/dummy/config/environments/production.rb' 168 | 169 | # Offense count: 2 170 | # This cop supports unsafe autocorrection (--autocorrect-all). 171 | # Configuration parameters: SafeForConstants. 172 | Style/RedundantFetchBlock: 173 | Exclude: 174 | - 'test/dummy/config/puma.rb' 175 | 176 | # Offense count: 1 177 | # This cop supports unsafe autocorrection (--autocorrect-all). 178 | # Configuration parameters: AutoCorrect, AllowComments. 179 | Style/RedundantInitialize: 180 | Exclude: 181 | - 'lib/oas_rails/oas_route.rb' 182 | 183 | # Offense count: 2 184 | # This cop supports unsafe autocorrection (--autocorrect-all). 185 | # Configuration parameters: Mode. 186 | Style/StringConcatenation: 187 | Exclude: 188 | - 'lib/oas_rails/operation.rb' 189 | - 'test/test_helper.rb' 190 | 191 | # Offense count: 181 192 | # This cop supports safe autocorrection (--autocorrect). 193 | # Configuration parameters: EnforcedStyle, ConsistentQuotesInMultiline. 194 | # SupportedStyles: single_quotes, double_quotes 195 | Style/StringLiterals: 196 | Enabled: false 197 | 198 | # Offense count: 5 199 | # This cop supports safe autocorrection (--autocorrect). 200 | # Configuration parameters: . 201 | # SupportedStyles: percent, brackets 202 | Style/SymbolArray: 203 | EnforcedStyle: percent 204 | MinSize: 10 205 | 206 | # Offense count: 22 207 | # This cop supports safe autocorrection (--autocorrect). 208 | # Configuration parameters: AllowHeredoc, AllowURI, URISchemes, IgnoreCopDirectives, AllowedPatterns. 209 | # URISchemes: http, https 210 | Layout/LineLength: 211 | Max: 202 212 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | oas_rails (1.3.2) 5 | easy_talk_two (~> 1.1.2) 6 | oas_core (>= 1.1.0) 7 | 8 | GEM 9 | remote: https://rubygems.org/ 10 | specs: 11 | actioncable (8.0.3) 12 | actionpack (= 8.0.3) 13 | activesupport (= 8.0.3) 14 | nio4r (~> 2.0) 15 | websocket-driver (>= 0.6.1) 16 | zeitwerk (~> 2.6) 17 | actionmailbox (8.0.3) 18 | actionpack (= 8.0.3) 19 | activejob (= 8.0.3) 20 | activerecord (= 8.0.3) 21 | activestorage (= 8.0.3) 22 | activesupport (= 8.0.3) 23 | mail (>= 2.8.0) 24 | actionmailer (8.0.3) 25 | actionpack (= 8.0.3) 26 | actionview (= 8.0.3) 27 | activejob (= 8.0.3) 28 | activesupport (= 8.0.3) 29 | mail (>= 2.8.0) 30 | rails-dom-testing (~> 2.2) 31 | actionpack (8.0.3) 32 | actionview (= 8.0.3) 33 | activesupport (= 8.0.3) 34 | nokogiri (>= 1.8.5) 35 | rack (>= 2.2.4) 36 | rack-session (>= 1.0.1) 37 | rack-test (>= 0.6.3) 38 | rails-dom-testing (~> 2.2) 39 | rails-html-sanitizer (~> 1.6) 40 | useragent (~> 0.16) 41 | actiontext (8.0.3) 42 | actionpack (= 8.0.3) 43 | activerecord (= 8.0.3) 44 | activestorage (= 8.0.3) 45 | activesupport (= 8.0.3) 46 | globalid (>= 0.6.0) 47 | nokogiri (>= 1.8.5) 48 | actionview (8.0.3) 49 | activesupport (= 8.0.3) 50 | builder (~> 3.1) 51 | erubi (~> 1.11) 52 | rails-dom-testing (~> 2.2) 53 | rails-html-sanitizer (~> 1.6) 54 | activejob (8.0.3) 55 | activesupport (= 8.0.3) 56 | globalid (>= 0.3.6) 57 | activemodel (8.0.3) 58 | activesupport (= 8.0.3) 59 | activerecord (8.0.3) 60 | activemodel (= 8.0.3) 61 | activesupport (= 8.0.3) 62 | timeout (>= 0.4.0) 63 | activestorage (8.0.3) 64 | actionpack (= 8.0.3) 65 | activejob (= 8.0.3) 66 | activerecord (= 8.0.3) 67 | activesupport (= 8.0.3) 68 | marcel (~> 1.0) 69 | activesupport (8.0.3) 70 | base64 71 | benchmark (>= 0.3) 72 | bigdecimal 73 | concurrent-ruby (~> 1.0, >= 1.3.1) 74 | connection_pool (>= 2.2.5) 75 | drb 76 | i18n (>= 1.6, < 2) 77 | logger (>= 1.4.2) 78 | minitest (>= 5.1) 79 | securerandom (>= 0.3) 80 | tzinfo (~> 2.0, >= 2.0.5) 81 | uri (>= 0.13.1) 82 | addressable (2.8.7) 83 | public_suffix (>= 2.0.2, < 7.0) 84 | ast (2.4.3) 85 | base64 (0.3.0) 86 | bcrypt (3.1.20) 87 | benchmark (0.4.1) 88 | bigdecimal (3.3.1) 89 | bindex (0.8.1) 90 | bootsnap (1.18.6) 91 | msgpack (~> 1.2) 92 | builder (3.3.0) 93 | capybara (3.40.0) 94 | addressable 95 | matrix 96 | mini_mime (>= 0.1.3) 97 | nokogiri (~> 1.11) 98 | rack (>= 1.6.0) 99 | rack-test (>= 0.6.3) 100 | regexp_parser (>= 1.5, < 3.0) 101 | xpath (~> 3.2) 102 | concurrent-ruby (1.3.5) 103 | connection_pool (2.5.4) 104 | crass (1.0.6) 105 | date (3.4.1) 106 | debug (1.11.0) 107 | irb (~> 1.10) 108 | reline (>= 0.3.8) 109 | deep_merge (1.2.2) 110 | drb (2.2.3) 111 | easy_talk_two (1.1.2) 112 | activemodel (>= 7.0) 113 | activesupport (>= 7.0) 114 | sorbet-runtime (>= 0.5) 115 | erb (5.1.1) 116 | erubi (1.13.1) 117 | factory_bot (6.5.5) 118 | activesupport (>= 6.1.0) 119 | factory_bot_rails (6.5.1) 120 | factory_bot (~> 6.5) 121 | railties (>= 6.1.0) 122 | faker (3.5.2) 123 | i18n (>= 1.8.11, < 2) 124 | globalid (1.3.0) 125 | activesupport (>= 6.1) 126 | i18n (1.14.7) 127 | concurrent-ruby (~> 1.0) 128 | io-console (0.8.1) 129 | irb (1.15.2) 130 | pp (>= 0.6.0) 131 | rdoc (>= 4.0.0) 132 | reline (>= 0.4.2) 133 | jbuilder (2.14.1) 134 | actionview (>= 7.0.0) 135 | activesupport (>= 7.0.0) 136 | json (2.13.2) 137 | jwt (3.1.2) 138 | base64 139 | language_server-protocol (3.17.0.5) 140 | lint_roller (1.1.0) 141 | logger (1.7.0) 142 | loofah (2.24.1) 143 | crass (~> 1.0.2) 144 | nokogiri (>= 1.12.0) 145 | mail (2.8.1) 146 | mini_mime (>= 0.1.1) 147 | net-imap 148 | net-pop 149 | net-smtp 150 | marcel (1.1.0) 151 | matrix (0.4.3) 152 | method_source (1.1.0) 153 | mini_mime (1.1.5) 154 | minitest (5.26.0) 155 | msgpack (1.8.0) 156 | net-imap (0.5.12) 157 | date 158 | net-protocol 159 | net-pop (0.1.2) 160 | net-protocol 161 | net-protocol (0.2.2) 162 | timeout 163 | net-smtp (0.5.1) 164 | net-protocol 165 | nio4r (2.7.4) 166 | nokogiri (1.18.10-arm64-darwin) 167 | racc (~> 1.4) 168 | nokogiri (1.18.10-x86_64-linux-gnu) 169 | racc (~> 1.4) 170 | oas_core (1.2.0) 171 | activesupport (>= 7.0) 172 | deep_merge (~> 1.2, >= 1.2.2) 173 | method_source (~> 1.0) 174 | yard (~> 0.9) 175 | parallel (1.27.0) 176 | parser (3.3.9.0) 177 | ast (~> 2.4.1) 178 | racc 179 | pp (0.6.3) 180 | prettyprint 181 | prettyprint (0.2.0) 182 | prism (1.4.0) 183 | psych (5.2.6) 184 | date 185 | stringio 186 | public_suffix (6.0.2) 187 | puma (7.0.4) 188 | nio4r (~> 2.0) 189 | racc (1.8.1) 190 | rack (3.2.3) 191 | rack-cors (3.0.0) 192 | logger 193 | rack (>= 3.0.14) 194 | rack-session (2.1.1) 195 | base64 (>= 0.1.0) 196 | rack (>= 3.0.0) 197 | rack-test (2.2.0) 198 | rack (>= 1.3) 199 | rackup (2.2.1) 200 | rack (>= 3) 201 | rails (8.0.3) 202 | actioncable (= 8.0.3) 203 | actionmailbox (= 8.0.3) 204 | actionmailer (= 8.0.3) 205 | actionpack (= 8.0.3) 206 | actiontext (= 8.0.3) 207 | actionview (= 8.0.3) 208 | activejob (= 8.0.3) 209 | activemodel (= 8.0.3) 210 | activerecord (= 8.0.3) 211 | activestorage (= 8.0.3) 212 | activesupport (= 8.0.3) 213 | bundler (>= 1.15.0) 214 | railties (= 8.0.3) 215 | rails-dom-testing (2.3.0) 216 | activesupport (>= 5.0.0) 217 | minitest 218 | nokogiri (>= 1.6) 219 | rails-html-sanitizer (1.6.2) 220 | loofah (~> 2.21) 221 | nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0) 222 | railties (8.0.3) 223 | actionpack (= 8.0.3) 224 | activesupport (= 8.0.3) 225 | irb (~> 1.13) 226 | rackup (>= 1.0.0) 227 | rake (>= 12.2) 228 | thor (~> 1.0, >= 1.2.2) 229 | tsort (>= 0.2) 230 | zeitwerk (~> 2.6) 231 | rainbow (3.1.1) 232 | rake (13.3.0) 233 | rdoc (6.15.0) 234 | erb 235 | psych (>= 4.0.0) 236 | tsort 237 | regexp_parser (2.11.3) 238 | reline (0.6.2) 239 | io-console (~> 0.5) 240 | rexml (3.4.4) 241 | rubocop (1.81.1) 242 | json (~> 2.3) 243 | language_server-protocol (~> 3.17.0.2) 244 | lint_roller (~> 1.1.0) 245 | parallel (~> 1.10) 246 | parser (>= 3.3.0.2) 247 | rainbow (>= 2.2.2, < 4.0) 248 | regexp_parser (>= 2.9.3, < 3.0) 249 | rubocop-ast (>= 1.47.1, < 2.0) 250 | ruby-progressbar (~> 1.7) 251 | unicode-display_width (>= 2.4.0, < 4.0) 252 | rubocop-ast (1.47.1) 253 | parser (>= 3.3.7.2) 254 | prism (~> 1.4) 255 | ruby-progressbar (1.13.0) 256 | rubyzip (3.1.1) 257 | securerandom (0.4.1) 258 | selenium-webdriver (4.36.0) 259 | base64 (~> 0.2) 260 | json (<= 2.13.2) 261 | logger (~> 1.4) 262 | prism (~> 1.0, < 1.5) 263 | rexml (~> 3.2, >= 3.2.5) 264 | rubyzip (>= 1.2.2, < 4.0) 265 | websocket (~> 1.0) 266 | sorbet-runtime (0.6.12638) 267 | sprockets (4.2.2) 268 | concurrent-ruby (~> 1.0) 269 | logger 270 | rack (>= 2.2.4, < 4) 271 | sprockets-rails (3.5.2) 272 | actionpack (>= 6.1) 273 | activesupport (>= 6.1) 274 | sprockets (>= 3.0.0) 275 | sqlite3 (2.7.4-arm64-darwin) 276 | sqlite3 (2.7.4-x86_64-linux-gnu) 277 | stringio (3.1.7) 278 | thor (1.4.0) 279 | timeout (0.4.3) 280 | tsort (0.2.0) 281 | tzinfo (2.0.6) 282 | concurrent-ruby (~> 1.0) 283 | unicode-display_width (3.2.0) 284 | unicode-emoji (~> 4.1) 285 | unicode-emoji (4.1.0) 286 | uri (1.0.4) 287 | useragent (0.16.11) 288 | web-console (4.2.1) 289 | actionview (>= 6.0.0) 290 | activemodel (>= 6.0.0) 291 | bindex (>= 0.4.0) 292 | railties (>= 6.0.0) 293 | websocket (1.2.11) 294 | websocket-driver (0.8.0) 295 | base64 296 | websocket-extensions (>= 0.1.0) 297 | websocket-extensions (0.1.5) 298 | xpath (3.2.0) 299 | nokogiri (~> 1.8) 300 | yard (0.9.37) 301 | zeitwerk (2.7.3) 302 | 303 | PLATFORMS 304 | arm64-darwin-24 305 | x86_64-linux 306 | 307 | DEPENDENCIES 308 | bcrypt (~> 3.1.7) 309 | bootsnap 310 | capybara 311 | debug 312 | factory_bot_rails 313 | faker 314 | jbuilder 315 | jwt 316 | oas_rails! 317 | puma (>= 5.0) 318 | rack-cors 319 | rails (~> 8.0, >= 8.0.2) 320 | rubocop 321 | selenium-webdriver 322 | sprockets-rails 323 | sqlite3 (~> 2.1) 324 | tzinfo-data 325 | web-console 326 | 327 | BUNDLED WITH 328 | 2.6.9 329 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | ## [1.3.2](https://github.com/a-chacon/oas_rails/compare/oas_rails/v1.3.1...oas_rails/v1.3.2) (2025-10-15) 4 | 5 | 6 | ### Bug Fixes 7 | 8 | * skip turbo routes for prevent errors ([#173](https://github.com/a-chacon/oas_rails/issues/173)) ([069d75c](https://github.com/a-chacon/oas_rails/commit/069d75c3f5f64b612fcb19e364c984a9adf24772)) 9 | 10 | ## [1.3.1](https://github.com/a-chacon/oas_rails/compare/oas_rails/v1.3.0...oas_rails/v1.3.1) (2025-10-13) 11 | 12 | 13 | ### Bug Fixes 14 | 15 | * take the route from app routes for a better detection of mounted path, otherwise just configure it with config ([#167](https://github.com/a-chacon/oas_rails/issues/167)) ([ebdade5](https://github.com/a-chacon/oas_rails/commit/ebdade51ac06e9fcc6ef35ab28485f7103269dcc)) 16 | 17 | 18 | ### Documentation 19 | 20 | * update oas core documentation url ([e4980c8](https://github.com/a-chacon/oas_rails/commit/e4980c84c2cda51a0013e035bc6e72840aca25b5)) 21 | 22 | ## [1.3.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v1.2.1...oas_rails/v1.3.0) (2025-08-11) 23 | 24 | 25 | ### Features 26 | 27 | * Provide ability to override specific theme attributes ([#159](https://github.com/a-chacon/oas_rails/issues/159)) ([d48c646](https://github.com/a-chacon/oas_rails/commit/d48c646c5b23c400c6a0d094ea725b2cb4c206de)) 28 | 29 | ## [1.2.1](https://github.com/a-chacon/oas_rails/compare/oas_rails/v1.2.0...oas_rails/v1.2.1) (2025-08-08) 30 | 31 | 32 | ### Bug Fixes 33 | 34 | * move to a partial ([#160](https://github.com/a-chacon/oas_rails/issues/160)) ([e2df77a](https://github.com/a-chacon/oas_rails/commit/e2df77ab4cd44cf0dd475d2cd3132ea1f243b775)) 35 | * remove unused rails files for prevent dependency conflicts ([#163](https://github.com/a-chacon/oas_rails/issues/163)) ([f38fe9d](https://github.com/a-chacon/oas_rails/commit/f38fe9d4b5e6bc9ebeedd7d64f96dd6fd35a8a52)) 36 | 37 | ## [1.2.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v1.1.1...oas_rails/v1.2.0) (2025-08-06) 38 | 39 | 40 | ### Features 41 | 42 | * Support providing some rapi-doc config ([#157](https://github.com/a-chacon/oas_rails/issues/157)) ([0604067](https://github.com/a-chacon/oas_rails/commit/06040678255fb6c5def1e5fae8f82721c5e463d3)) 43 | 44 | ## [1.1.1](https://github.com/a-chacon/oas_rails/compare/oas_rails/v1.1.0...oas_rails/v1.1.1) (2025-07-28) 45 | 46 | 47 | ### Bug Fixes 48 | 49 | * add option for persist auth in rapidoc ([f6c01f7](https://github.com/a-chacon/oas_rails/commit/f6c01f7597aa5a6b7c4c4d5f661dc349fc49e72d)) 50 | 51 | ## [1.1.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v1.0.1...oas_rails/v1.1.0) (2025-07-07) 52 | 53 | 54 | ### Features 55 | 56 | * change to MIT license ([#150](https://github.com/a-chacon/oas_rails/issues/150)) ([bd40a66](https://github.com/a-chacon/oas_rails/commit/bd40a662f4d2c316fd6c3771c29fa1a2c9027410)) 57 | 58 | ## [1.0.1](https://github.com/a-chacon/oas_rails/compare/oas_rails/v1.0.0...oas_rails/v1.0.1) (2025-06-25) 59 | 60 | 61 | ### Bug Fixes 62 | 63 | * custom route extractor usage ([#147](https://github.com/a-chacon/oas_rails/issues/147)) ([99976a8](https://github.com/a-chacon/oas_rails/commit/99976a8a6ac92db10cad44cbac85d5eb2dcc1e2d)) 64 | 65 | ## [1.0.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.17.1...oas_rails/v1.0.0) (2025-06-22) 66 | 67 | 68 | ### ⚠ BREAKING CHANGES 69 | 70 | * implement source oas and bumping to oas_core 1.0.0 ([#143](https://github.com/a-chacon/oas_rails/issues/143)) 71 | 72 | ### Features 73 | 74 | * implement source oas and bumping to oas_core 1.0.0 ([#143](https://github.com/a-chacon/oas_rails/issues/143)) ([c02889b](https://github.com/a-chacon/oas_rails/commit/c02889b7d6fb948a6a69a5c5b438f987e230c507)) 75 | 76 | ## [0.17.1](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.17.0...oas_rails/v0.17.1) (2025-06-14) 77 | 78 | 79 | ### Bug Fixes 80 | 81 | * remove unused oas route variables ([a386b9e](https://github.com/a-chacon/oas_rails/commit/a386b9e35c37569616c93f61971d7ce168a98861)) 82 | 83 | 84 | ### Documentation 85 | 86 | * update screenshot of readme ([0fc582a](https://github.com/a-chacon/oas_rails/commit/0fc582a54afdab2cfd27608ba2e88538232a15b9)) 87 | 88 | ## [0.17.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.16.0...oas_rails/v0.17.0) (2025-06-09) 89 | 90 | 91 | ### Features 92 | 93 | * migrate to oas_core gem ([#136](https://github.com/a-chacon/oas_rails/issues/136)) ([5c38011](https://github.com/a-chacon/oas_rails/commit/5c3801110f77d38891bf644ec0ca9e0191195d83)) 94 | 95 | ## [0.16.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.15.0...oas_rails/v0.16.0) (2025-06-06) 96 | 97 | 98 | ### Features 99 | 100 | * Add support for file uploads ([#137](https://github.com/a-chacon/oas_rails/issues/137)) ([5c79a84](https://github.com/a-chacon/oas_rails/commit/5c79a8475f70b0eab69964067efc1b7824ada14d)) 101 | 102 | ## [0.15.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.14.0...oas_rails/v0.15.0) (2025-05-28) 103 | 104 | 105 | ### Features 106 | 107 | * add configurable route extractor to configuration ([#134](https://github.com/a-chacon/oas_rails/issues/134)) ([7e6d36c](https://github.com/a-chacon/oas_rails/commit/7e6d36cf8dd47095add4d5552be011214162444a)) 108 | 109 | ## [0.14.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.13.0...oas_rails/v0.14.0) (2025-05-18) 110 | 111 | 112 | ### Features 113 | 114 | * implement support for class level tags and refactors ([#121](https://github.com/a-chacon/oas_rails/issues/121)) ([6e19ef1](https://github.com/a-chacon/oas_rails/commit/6e19ef1a90e7b34522a1a0bdebff6baa9f7f2c36)) 115 | 116 | 117 | ### Bug Fixes 118 | 119 | * filter out rubocop and todo annotations ([#127](https://github.com/a-chacon/oas_rails/issues/127)) ([f46101c](https://github.com/a-chacon/oas_rails/commit/f46101c00429ffe0313e83901918dcb491f525b5)) 120 | 121 | 122 | ### Documentation 123 | 124 | * add demo app ([#118](https://github.com/a-chacon/oas_rails/issues/118)) ([101d233](https://github.com/a-chacon/oas_rails/commit/101d233b913b3c4fd3378d3f20bbf1e3e3a30e62)) 125 | * add star history ([4ac26ed](https://github.com/a-chacon/oas_rails/commit/4ac26ede932e9b2930c83af91ea1b65fb9e269b1)) 126 | * fix tags, it was just tag and that dont works with oas_rails ([#123](https://github.com/a-chacon/oas_rails/issues/123)) ([9c39510](https://github.com/a-chacon/oas_rails/commit/9c39510431a6f5f42d71d621c6ecffb15108da8b)) 127 | * update llms urls ([975a1ff](https://github.com/a-chacon/oas_rails/commit/975a1ffdf3e4eef84dd3c5d9de48d5530c23e590)) 128 | 129 | 130 | ### Code Refactoring 131 | 132 | * move logic from media type to example finder ([#114](https://github.com/a-chacon/oas_rails/issues/114)) ([8334b04](https://github.com/a-chacon/oas_rails/commit/8334b0431c16b40e42b9f817f5d0d1221f06f428)) 133 | 134 | ## [0.13.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.12.0...oas_rails/v0.13.0) (2025-04-19) 135 | 136 | 137 | ### Features 138 | 139 | * add support to active record classes in compound string structures ([#112](https://github.com/a-chacon/oas_rails/issues/112)) ([da0bba6](https://github.com/a-chacon/oas_rails/commit/da0bba6c8c2d822c1fff57968410c35752301831)) 140 | 141 | 142 | ### Bug Fixes 143 | 144 | * add validation to configuration default response body ([#108](https://github.com/a-chacon/oas_rails/issues/108)) ([7cd3cc4](https://github.com/a-chacon/oas_rails/commit/7cd3cc4bd9fac0406d41fd28f352c7a865a1eb50)) 145 | * update easy_tal to easy_talk_two fixing conversions of boolean and decimal fields ([#113](https://github.com/a-chacon/oas_rails/issues/113)) ([36cb39b](https://github.com/a-chacon/oas_rails/commit/36cb39bca1d377782da39439f92468d1d78fae04)) 146 | 147 | ## [0.12.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.11.0...oas_rails/v0.12.0) (2025-04-12) 148 | 149 | 150 | ### Features 151 | 152 | * add include_mode for handle what include in final oas ([#106](https://github.com/a-chacon/oas_rails/issues/106)) ([988b5d0](https://github.com/a-chacon/oas_rails/commit/988b5d04ce55639709481ea12f5c07c2fb127aea)) 153 | * order endpoints by tags in rapidoc. ([#105](https://github.com/a-chacon/oas_rails/issues/105)) ([f2bd784](https://github.com/a-chacon/oas_rails/commit/f2bd784aec4aa9252179e2b72ea00d7d0330ec55)) 154 | 155 | 156 | ### Documentation 157 | 158 | * make the book works in the root of the site ([312a0ca](https://github.com/a-chacon/oas_rails/commit/312a0ca7044679d118b17f39b5ea5bd6fa981470)) 159 | 160 | ## [0.11.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.10.1...oas_rails/v0.11.0) (2025-04-07) 161 | 162 | 163 | ### Features 164 | 165 | * add rapidoc assets to the gem. So will load locally without need CDN ([#94](https://github.com/a-chacon/oas_rails/issues/94)) ([ab0c684](https://github.com/a-chacon/oas_rails/commit/ab0c684e9b91cd5c2cf78b63fd13d8d29edda317)) 166 | * Ebook ([#98](https://github.com/a-chacon/oas_rails/issues/98)) ([e680d6b](https://github.com/a-chacon/oas_rails/commit/e680d6b8d9231162d6d9fbfbec600742bb89b27e)) 167 | * pre defined themes for rapidoc ([#96](https://github.com/a-chacon/oas_rails/issues/96)) ([6c5b75b](https://github.com/a-chacon/oas_rails/commit/6c5b75bbc40379242210f7bf59f6ec922b2706da)) 168 | 169 | 170 | ### Bug Fixes 171 | 172 | * finish to process a json schema from a active record class with easy talk ([#95](https://github.com/a-chacon/oas_rails/issues/95)) ([f3b4401](https://github.com/a-chacon/oas_rails/commit/f3b440195198b667db865435fb0701d9467a193e)) 173 | * small text fix in initializer template file ([#97](https://github.com/a-chacon/oas_rails/issues/97)) ([a31bfa0](https://github.com/a-chacon/oas_rails/commit/a31bfa063c285b98a35f33f173acf984e99e30cc)) 174 | 175 | 176 | ### Documentation 177 | 178 | * **readme:** add documentation for configure project's license ([#89](https://github.com/a-chacon/oas_rails/issues/89)) ([19fdb97](https://github.com/a-chacon/oas_rails/commit/19fdb972265099872cbb5e23f55b52947b2a5864)) 179 | 180 | ## [0.10.1](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.10.0...oas_rails/v0.10.1) (2025-03-29) 181 | 182 | 183 | ### Bug Fixes 184 | 185 | * include full web page structure in index views becouse the layout was not in use. ([2eec88a](https://github.com/a-chacon/oas_rails/commit/2eec88a88d67caa3680aae67b209644b7e347aea)) 186 | * **parameter builder:** return empty string when trying to extract word before path param ([c5fcb00](https://github.com/a-chacon/oas_rails/commit/c5fcb00cc85d5bacb234c612c7289f8acb747f9a)) 187 | 188 | ## [0.10.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.9.0...oas_rails/v0.10.0) (2025-03-26) 189 | 190 | 191 | ### Features 192 | 193 | * add support for multiline tag definition ([#83](https://github.com/a-chacon/oas_rails/issues/83)) ([4a7b3cf](https://github.com/a-chacon/oas_rails/commit/4a7b3cf3d29b6278843a4acaa79da418eddf6a72)) 194 | * set brackets to query params when they are defined as array. ([#79](https://github.com/a-chacon/oas_rails/issues/79)) ([6367528](https://github.com/a-chacon/oas_rails/commit/63675284d3359d3ef78d5678318e93c87bbc41f9)) 195 | 196 | 197 | ### Bug Fixes 198 | 199 | * error when fixture has erb ([#81](https://github.com/a-chacon/oas_rails/issues/81)) ([b42a637](https://github.com/a-chacon/oas_rails/commit/b42a63712c74e52d74fed67db7278447311a6ea8)) 200 | 201 | 202 | ### Code Refactoring 203 | 204 | * replace squema with easy_talk gem for convert active record classes to json squema ([#82](https://github.com/a-chacon/oas_rails/issues/82)) ([ced2f4f](https://github.com/a-chacon/oas_rails/commit/ced2f4f8adca791f0bce2cdf39cf6cb9ce8e0f72)) 205 | 206 | ## [0.9.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.8.4...oas_rails/v0.9.0) (2025-01-29) 207 | 208 | 209 | ### Features 210 | 211 | * Allow it to be used without ActiveRecord being present ([#72](https://github.com/a-chacon/oas_rails/issues/72)) ([340157d](https://github.com/a-chacon/oas_rails/commit/340157db4e3742a47d1c254dc218d0c4c93252cb)) 212 | 213 | ## [0.8.4](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.8.3...oas_rails/v0.8.4) (2025-01-19) 214 | 215 | 216 | ### Bug Fixes 217 | 218 | * block concurrent-ruby for rails 7 ([4a42e3c](https://github.com/a-chacon/oas_rails/commit/4a42e3c14ea2fe24d7f9d3f89b7502425492d7e2)) 219 | * Update with style tag to fix rapidoc button styling ([19e9ba1](https://github.com/a-chacon/oas_rails/commit/19e9ba137dd6063713233193d89ff655496b0ff1)) 220 | 221 | ## [0.8.3](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.8.2...oas_rails/v0.8.3) (2024-12-21) 222 | 223 | 224 | ### Bug Fixes 225 | 226 | * update dependencies and release changes not following conventional commits ([e97ea5b](https://github.com/a-chacon/oas_rails/commit/e97ea5b05716bd1d21956ad834a06af03246f105)) 227 | 228 | ## [0.8.2](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.8.1...oas_rails/v0.8.2) (2024-10-11) 229 | 230 | 231 | ### Bug Fixes 232 | 233 | * remove dependency of Rails 7 so it could run in Rails 8 too. ([#61](https://github.com/a-chacon/oas_rails/issues/61)) ([a57fd16](https://github.com/a-chacon/oas_rails/commit/a57fd1644406b63263538a38a503bc05e10bbe4f)) 234 | 235 | ## [0.8.1](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.8.0...oas_rails/v0.8.1) (2024-10-01) 236 | 237 | 238 | ### Documentation 239 | 240 | * **readme:** add demo video link ([48732e0](https://github.com/a-chacon/oas_rails/commit/48732e0d70789471d09596981e99c7bbfdc08eb5)) 241 | * **readme:** add some emojis and fix the indication for a required param ([2f0fca1](https://github.com/a-chacon/oas_rails/commit/2f0fca17f9668b0c77c8ff9bdc33911b0cc6ed0c)) 242 | 243 | ## [0.8.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.7.0...oas_rails/v0.8.0) (2024-08-31) 244 | 245 | 246 | ### Features 247 | 248 | * **route_extractor:** feature ignore custom route ([#54](https://github.com/a-chacon/oas_rails/issues/54)) ([e34b3ee](https://github.com/a-chacon/oas_rails/commit/e34b3ee4d0de3161f6af2cd890ffafc385701285)) 249 | * **view:** support custom layout ([#52](https://github.com/a-chacon/oas_rails/issues/52)) ([abef8d3](https://github.com/a-chacon/oas_rails/commit/abef8d3abe772514b71c49aaa298ef1fe0c8aa37)) 250 | 251 | 252 | ### Bug Fixes 253 | 254 | * **yard_oas_rails_factory:** set different expresion for request body examples ([ceea6eb](https://github.com/a-chacon/oas_rails/commit/ceea6eb3572006c2a0222f86625fc755d3c856db)) 255 | 256 | 257 | ### Tests 258 | 259 | * feature ignore custom route ([#55](https://github.com/a-chacon/oas_rails/issues/55)) ([aedbe7b](https://github.com/a-chacon/oas_rails/commit/aedbe7b610c3f5db5cadf8e59495351f8e83eddf)) 260 | 261 | ## [0.7.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.6.0...oas_rails/v0.7.0) (2024-08-27) 262 | 263 | 264 | ### Features 265 | 266 | * **view:** Adding the title from the config initializer in the views title ([#48](https://github.com/a-chacon/oas_rails/issues/48)) ([61cf843](https://github.com/a-chacon/oas_rails/commit/61cf84330fb66d0f400a9aa51ff3bfdd5cdde957)) 267 | 268 | 269 | ### Documentation 270 | 271 | * **readme:** Fix 'Hash' missing ([#49](https://github.com/a-chacon/oas_rails/issues/49)) ([aed623d](https://github.com/a-chacon/oas_rails/commit/aed623d5c28853ebbc2c021f96b3093b288fa4d2)) 272 | 273 | ## [0.6.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.5.0...oas_rails/v0.6.0) (2024-08-26) 274 | 275 | 276 | ### Features 277 | 278 | * add [@response](https://github.com/response)_example tag for document examples of responses. ([#43](https://github.com/a-chacon/oas_rails/issues/43)) ([16f37c4](https://github.com/a-chacon/oas_rails/commit/16f37c42a73ce6d3b29b80c9c643fea84253193a)) 279 | 280 | 281 | ### Bug Fixes 282 | 283 | * default body response following the new syntax ([a397555](https://github.com/a-chacon/oas_rails/commit/a3975556d3c4ba20a51636999e65c33944a8d3b7)) 284 | 285 | ## [0.5.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.4.5...oas_rails/v0.5.0) (2024-08-23) 286 | 287 | 288 | ### Features 289 | 290 | * disable autodiscover responses for endpoints with at least one documented response. ([ecd1318](https://github.com/a-chacon/oas_rails/commit/ecd13187a9f65d7b103095890a24bf7747268ca3)) 291 | 292 | 293 | ### Code Refactoring 294 | 295 | * redefine the way request body and responses are described. BREAKING. ([ed5eef5](https://github.com/a-chacon/oas_rails/commit/ed5eef53bd10010b2c801a6b30a76084535ee27b)) 296 | 297 | ## [0.4.5](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.4.4...oas_rails/v0.4.5) (2024-08-21) 298 | 299 | 300 | ### Bug Fixes 301 | 302 | * return self when default responses and autodiscover is disabled. ([0d26fcc](https://github.com/a-chacon/oas_rails/commit/0d26fccf2cda3bcc2b0414fdef9dbcaaaccc4dbd)) 303 | 304 | ## [0.4.4](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.4.3...oas_rails/v0.4.4) (2024-08-20) 305 | 306 | 307 | ### Bug Fixes 308 | 309 | * make the test works with rake under 3.1 ([7220e26](https://github.com/a-chacon/oas_rails/commit/7220e26ad8579a7f08bd188c88c61fad230c1e20)) 310 | * manage when same route has two or more verbs ([cb99f31](https://github.com/a-chacon/oas_rails/commit/cb99f318cffd308fb4d78d1fc3286c63667a954a)) 311 | * use Rack::Utils.status_code instead of Rack::Utils::SYMBOL_TO_STATUS_CODE ([0c9dccb](https://github.com/a-chacon/oas_rails/commit/0c9dccba9da269580d69c6e5e9601bfbb4ee70f2)) 312 | 313 | ## [0.4.3](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.4.2...oas_rails/v0.4.3) (2024-08-19) 314 | 315 | 316 | ### Bug Fixes 317 | 318 | * rails version was ~>7.0.0 which was not allowing to use 7.2 ([8e9e6e2](https://github.com/a-chacon/oas_rails/commit/8e9e6e2c8f140422e2d55ea581ecb5ed80c70791)) 319 | 320 | ## [0.4.2](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.4.1...oas_rails/v0.4.2) (2024-08-15) 321 | 322 | 323 | ### Bug Fixes 324 | 325 | * openapi document errors. ([df4fb94](https://github.com/a-chacon/oas_rails/commit/df4fb94ede29dd37d2eaadbe63a49466af6244ee)) 326 | 327 | 328 | ### Documentation 329 | 330 | * **readme:** add badges for rails and ruby version ([9125108](https://github.com/a-chacon/oas_rails/commit/912510888a984b1d8f81c4ef0fe846d1b5f6f78c)) 331 | 332 | 333 | ### Code Refactoring 334 | 335 | * move json schema builder to builders module ([#21](https://github.com/a-chacon/oas_rails/issues/21)) ([8d32054](https://github.com/a-chacon/oas_rails/commit/8d320541cbe8e0fad9d3a1085ac5bf79f473b09f)) 336 | 337 | ## [0.4.1](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.4.0...oas_rails/v0.4.1) (2024-08-11) 338 | 339 | 340 | ### Bug Fixes 341 | 342 | * engine mounted path was not working on the index view. ([52e7936](https://github.com/a-chacon/oas_rails/commit/52e793645521ab7b8074092233f69a303d04e0bc)) 343 | * try to detect model class from all the namespaces of the controller ([d029862](https://github.com/a-chacon/oas_rails/commit/d0298622a39e61e12db06129de33235e5bfcb923)) 344 | 345 | ## [0.4.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.3.0...oas_rails/v0.4.0) (2024-08-10) 346 | 347 | 348 | ### Features 349 | 350 | * add default responses configuration (errors). ([e4b3666](https://github.com/a-chacon/oas_rails/commit/e4b36665a2902bd73a447f762bb6aa007db925db)) 351 | 352 | 353 | ### Code Refactoring 354 | 355 | * move OAS creation logic to builders and replace common objects ([64d7922](https://github.com/a-chacon/oas_rails/commit/64d792256ef9c7a46e54e901ca3bb74cd2ed1f8a)) 356 | * organize OAS models into Spec module. ([#13](https://github.com/a-chacon/oas_rails/issues/13)) ([71a1515](https://github.com/a-chacon/oas_rails/commit/71a15150fb093a6402cfed26f4605cba9facd479)) 357 | 358 | ## [0.3.0](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.2.3...oas_rails/v0.3.0) (2024-08-02) 359 | 360 | 361 | ### Features 362 | 363 | * **media_type:** generate examples with factory bot if it is available and a factory is defined for detected class. ([4c904a8](https://github.com/a-chacon/oas_rails/commit/4c904a878dc3ae168c256821db9e14446fe26c2c)) 364 | 365 | 366 | ### Bug Fixes 367 | 368 | * **media_type:** models and examples identify by default was equal. But in the practice there are some fields that are different ex: created_at is sended in a response but not in a create/update operation. ([56a7b4c](https://github.com/a-chacon/oas_rails/commit/56a7b4c08bb8f7ac6680de607ddf66fc0437219f)) 369 | * **route_extractor:** rubocop offense route_extractor.rb:84:11: C: Style/MultilineBlockChain: Avoid multi-line chains of blocks. ([cd8af2e](https://github.com/a-chacon/oas_rails/commit/cd8af2ebf3c79769beb744639ee2f7a491f881f7)) 370 | 371 | 372 | ### Documentation 373 | 374 | * **readme:** cover from my website ([aa7f4aa](https://github.com/a-chacon/oas_rails/commit/aa7f4aa1f9f5de05ba05abc66734291aa06364e0)) 375 | * **readme:** update example image ([59f43b3](https://github.com/a-chacon/oas_rails/commit/59f43b387448b3ce18b4a1bc507612e69ddd3a4a)) 376 | 377 | 378 | ### Code Refactoring 379 | 380 | * **route_extractor:** move the RouteExtrator class into de Extractors module. ([d06cf8a](https://github.com/a-chacon/oas_rails/commit/d06cf8a482732b58795079f827d3b6f91a5f4e4b)) 381 | 382 | ## [0.2.3](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.2.2...oas_rails/v0.2.3) (2024-08-01) 383 | 384 | 385 | ### Bug Fixes 386 | 387 | * **specification:** clear_cache is an instance method, not class method. ([7a30667](https://github.com/a-chacon/oas_rails/commit/7a30667b73ace8fd6692b31d670ccba15b274700)) 388 | * **specification:** Make the reload works. ([be86263](https://github.com/a-chacon/oas_rails/commit/be8626391e24c0ff51fec1abd5dcdab14d1a3335)) 389 | 390 | ## [0.2.2](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.2.1...oas_rails/v0.2.2) (2024-07-31) 391 | 392 | 393 | ### Bug Fixes 394 | 395 | * **media_types:** fixtures could be not present when looking for examples. It was raising an Errno::ENOENT. Now will be rescue and returned a default {}. ([03ff14d](https://github.com/a-chacon/oas_rails/commit/03ff14de0ad60751f01a94efcd7097d982821001)) 396 | 397 | ## [0.2.1](https://github.com/a-chacon/oas_rails/compare/oas_rails/v0.2.0...oas_rails/v0.2.1) (2024-07-31) 398 | 399 | 400 | ### Bug Fixes 401 | 402 | * **route_extractor:** Verify the correct implementation of a route. If ([e8ab706](https://github.com/a-chacon/oas_rails/commit/e8ab70668b32708f3c82ad69e7064da9adece5cf)) 403 | 404 | ## [0.2.0](https://github.com/a-chacon/oas_rails/compare/oas_rails-v0.1.1...oas_rails/v0.2.0) (2024-07-30) 405 | 406 | 407 | ### Features 408 | 409 | * add a configuration for filter routes. So if you have your api in the namespace /api/v1 you could add config.api_path='/api/v1' for filter the routes to be included in the documentation. ([22eaa9c](https://github.com/a-chacon/oas_rails/commit/22eaa9c1966e74230d5ff15566ecd07df40b9b7e)) 410 | * Add methods for documenting authorization. ([f543146](https://github.com/a-chacon/oas_rails/commit/f5431469ed5a4965ab536ae55a67503097fd0bfa)) 411 | * add release please workflow and release all changes ([1e137a4](https://github.com/a-chacon/oas_rails/commit/1e137a468a2b479aee32d9ff92ef18d08015aafc)) 412 | * add ruby version to repo ([0ac81a3](https://github.com/a-chacon/oas_rails/commit/0ac81a36c8d4cd0d45295e4c60270072752a393c)) 413 | * preparing first version of gem ([849922a](https://github.com/a-chacon/oas_rails/commit/849922a02d40cf38f3a29cf6a802d964b5020b14)) 414 | * release version 0.1.0 ([ca16692](https://github.com/a-chacon/oas_rails/commit/ca16692fc61acb8366a635bcebe3d365244bef3c)) 415 | * release version 0.1.1 ([2c2c5c6](https://github.com/a-chacon/oas_rails/commit/2c2c5c623a7c9ef8b59f4429c03ef003091dcbf5)) 416 | 417 | 418 | ### Bug Fixes 419 | 420 | * `protect_from_forgery` should be called in `OasRails::ApplicationController` ([76af772](https://github.com/a-chacon/oas_rails/commit/76af77242153f5e4b1dfff22d24f0a0bef698597)) 421 | * `protect_from_forgery` should be configured with `with: :exception` ([11fd990](https://github.com/a-chacon/oas_rails/commit/11fd99090f8e3653843b6cbea72600c0e40e46e5)) 422 | * extract functions from OasRails module to Utils module for fix offense of lenght module ([f740dca](https://github.com/a-chacon/oas_rails/commit/f740dca528ee9214e12afc292a7b7d71a3b037b1)) 423 | * make the same route respond with the RapiDoc view or the json file ([7fe53f3](https://github.com/a-chacon/oas_rails/commit/7fe53f37739cc38faaf675d1a09234651f303b4f)) 424 | * release plase config names ([c54f35f](https://github.com/a-chacon/oas_rails/commit/c54f35fa55895490643a056e7ce2cf1504e4f0ac)) 425 | * remove unused style import ([291efdf](https://github.com/a-chacon/oas_rails/commit/291efdf8dd8bb793690fe02f3dc94d15049a1729)) 426 | * rename configuration request_body_automatically to autodiscover_request_body ([266828e](https://github.com/a-chacon/oas_rails/commit/266828ee2c4257298ad8e942096062da3ae4f045)) 427 | * rubocop configuration and gemspec definition ([2b05b3d](https://github.com/a-chacon/oas_rails/commit/2b05b3df3f4e041bdbcc738505618a98bd45e678)) 428 | * set sqlite version in gemfile ([559d993](https://github.com/a-chacon/oas_rails/commit/559d99397c9ea8f20e3f7f3c37cd2e9e36e04329)) 429 | --------------------------------------------------------------------------------