├── 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 | 
2 | 
3 | 
4 | 
5 | 
6 | 
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 | 
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 | [](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 |
--------------------------------------------------------------------------------