require_relative "../../../app/helpers/api/open_api_helper" module FactoryBot module ExampleBot # Anonymous argument forwarding is a new feature in Ruby 3.2 and trying to use it breaks # things when people are using an older verison of Ruby. So for now we're silencing the # linter complaints and are using named arguments. # standard:disable Style/ArgumentsForwarding def example(model, **options) FactoryBot.build(factory(model), **options) end def example_list(model, quantity, **options) FactoryBot.build_list(factory(model), quantity, **options) end # standard:enable Style/ArgumentsForwarding REST_METHODS = %i[get_examples get_example post_example post_parameters put_example put_parameters patch_example patch_parameters] REST_METHODS.each do |method| define_method(method) do |model, **options| _path_examples(method.to_s, model, **options) end end private def factory(model) factories = FactoryBot.factories.instance_variable_get(:@items).keys factories.include?("#{model}_example") ? :"#{model}_example" : model end include ::Api::OpenApiHelper def _path_examples(method, model, **options) version = options.delete(:version) || "v1" case method.split("_").first when "get" count = (options.delete(:count) || method == "get_examples") ? 2 : 1 template, class_name, var_name, values = _set_values(method, model, count) else template, class_name, var_name, values = _set_values("get_example", model) if method.end_with?("parameters") if has_strong_parameters?("::Api::#{version.upcase}::#{class_name.pluralize}Controller") strong_parameter_keys = strong_parameter_keys_for(class_name, version) output = _json_output(template, version, class_name, var_name, values) parameters_output = JSON.parse(output) parameters_output&.select! { |key| strong_parameter_keys.include?(key.to_sym) } # Wrapping the example as parameters should be wrapped with the model name: parameters_output = {model.to_s => parameters_output} return indent(parameters_output.to_yaml.delete_prefix("---\n"), 6).html_safe end return nil end end _yaml_output(template, version, class_name, var_name, values) end def _set_values(method, model, count = 1) model_name = ActiveRecord::Base.descendants.find { |klass| klass.model_name.param_key == model.to_s }&.model_name factory_path = "test/factories/#{model_name.collection}.rb" model_class = model_name.name.constantize if count > 1 cache_key = [:example_list, model_name.param_key, File.ctime(factory_path)] values = if model_class.singleton_methods.any? FactoryBot.example_list(model, count) else Rails.cache.fetch(cache_key) { FactoryBot.example_list(model, count) } end var_name = model_name.element.pluralize else cache_key = [:example, model_name.param_key, File.ctime(factory_path)] values = if model_class.singleton_methods.any? FactoryBot.example(model) else Rails.cache.fetch(cache_key) { FactoryBot.example(model) } end var_name = model_name.element end template = (method == "get_examples") ? "index" : "show" [template, model_name.name, var_name, values] end def _json_output(template, version, class_name, var_name, values) ActionController::Base.render( template: "api/#{version}/#{class_name.underscore.pluralize}/#{template}", assigns: {"#{var_name}": values}, formats: :json ) end def _yaml_output(template, version, class_name, var_name, values) indent( JSON.parse( _json_output(template, version, class_name, var_name, values) ).to_yaml .delete_prefix("---\n"), 7 ).html_safe end end extend ExampleBot end