module ApiRegulator class Api attr_reader :controller_class, :controller_path, :controller_name, :action_name, :description, :title, :params, :responses def initialize(controller_class, action_name, desc: nil, title: nil, &block) @controller_class = controller_class @controller_name = controller_class.name @controller_path = controller_class.controller_path @action_name = action_name.to_s @description = desc @title = title @params = [] @responses = {} instance_eval(&block) if block_given? end def param(name, type = nil, item_type: nil, desc: "", location: :body, **options, &block) param = Param.new(name, type, item_type: item_type, desc: desc, location: location, **options, &block) @params << param end def ref(ref_name, except: [], only: []) shared_schema = ApiRegulator.shared_schema(ref_name) raise "Shared schema #{ref_name} not found" unless shared_schema # Filter parameters based on `only` or `except` options filtered_params = shared_schema.params if only.any? filtered_params = filtered_params.select { |param| only.include?(param.name) } elsif except.any? filtered_params = filtered_params.reject { |param| except.include?(param.name) } end filtered_params.each do |shared_param| @params << shared_param end end def response(status_code, description_or_options, &block) if description_or_options.is_a?(Hash) && description_or_options[:ref] # Reference to a shared schema @responses[status_code] = Param.new(:root, :object, ref: description_or_options[:ref], &block) else # Inline schema definition schema = Param.new(:root, :object, desc: description_or_options, &block) @responses[status_code] = schema end end def path rails_route.path.spec.to_s .sub("(.:format)", "") # Remove optional format .gsub(/:([\w_]+)/, '{\1}') # Replace `:param` with `{param}` end def http_method rails_route.verb&.downcase end def rails_route route = Rails.application.routes.routes.find do |r| r.defaults[:controller] == controller_path && r.defaults[:action] == action_name end raise "HTTP method not found for #{controller_name}##{action_name}" unless route route end def operation_id "#{controller_path.gsub("/", "-")}-#{action_name}" end def tags [ controller_name .demodulize .sub("Controller", "") .underscore .humanize ] end def allows_body? http_method != "get" end end class << self def api_definitions @api_definitions ||= [] end def reset_api_definitions @api_definitions = [] end end end