From cf37163be0fca373a65202cd385e7b308bfa0bca Mon Sep 17 00:00:00 2001 From: Abishekcs Date: Tue, 9 Jun 2026 20:44:26 +0530 Subject: [PATCH 1/3] refactor: replace Prism AST parser with Blueprinter Reflection API Switch the Blueprinter parser from Prism-based AST parsing to Blueprinter's built-in Reflection API. This removes the need for a Visitor pattern, temp file parsing, and manual AST traversal. The Reflection API provides direct access to fields, views, and associations making the parser significantly simpler. --- Gemfile | 1 + lib/rage/openapi/parsers/ext/blueprinter.rb | 110 ++------ spec/openapi/parsers/ext/blueprinter_spec.rb | 237 +++++++----------- spec/spec_helper.rb | 1 + .../contexts/mocked_blueprinter_classes.rb | 28 +++ 5 files changed, 133 insertions(+), 244 deletions(-) create mode 100644 spec/support/contexts/mocked_blueprinter_classes.rb diff --git a/Gemfile b/Gemfile index 8ce5b9e6..e79c965e 100644 --- a/Gemfile +++ b/Gemfile @@ -26,4 +26,5 @@ group :test do gem "prism" gem "redis-client" gem "appraisal", "~> 2.5" + gem "blueprinter" end diff --git a/lib/rage/openapi/parsers/ext/blueprinter.rb b/lib/rage/openapi/parsers/ext/blueprinter.rb index 20ffa48c..1349b42b 100644 --- a/lib/rage/openapi/parsers/ext/blueprinter.rb +++ b/lib/rage/openapi/parsers/ext/blueprinter.rb @@ -14,107 +14,31 @@ def known_definition?(str) end def parse(klass_str) - visitor = __parse(klass_str) - visitor.build_schema + is_collection, raw_klass_str, _ = Rage::OpenAPI.__parse_serializer_args(klass_str) + klass = @namespace.const_get(raw_klass_str) + build_schema(klass, is_collection) end - def __parse(klass_str) - is_collection, klass_str, _ = Rage::OpenAPI.__parse_serializer_args(klass_str) + private - klass = @namespace.const_get(klass_str) - source_path, _ = Object.const_source_location(klass.name) - ast = Prism.parse_file(source_path) + def build_schema(klass, is_collection) + reflections = klass.reflections + identifier_fields = extract_fields(reflections, :identifier) + default_fields = extract_fields(reflections, :default) - visitor = Visitor.new(self, is_collection) - ast.value.accept(visitor) + schema = identifier_fields.merge(default_fields.sort.to_h) - visitor + result = { "type" => "object" } + result["properties"] = schema if schema.any? + result = { "type" => "array", "items" => result } if is_collection + result end - class VisitorContext - attr_accessor :symbols, :keywords, :strings + def extract_fields(reflections, view_name) + return {} unless (view = reflections[view_name]) - def initialize - @symbols = [] - @strings = [] - @keywords = {} - end - end - - class Visitor < Prism::Visitor - attr_accessor :schema, :identifier - - def initialize(parser, is_collection) - @parser = parser - @is_collection = is_collection - - @context = nil - @schema = {} - @segment = @schema - @identifier = {} - end - - def build_schema - result = { "type" => "object" } - - properties = {} - properties.merge!(@identifier) - properties.merge!(@schema.sort.to_h) - - result["properties"] = properties if properties.any? - result = { "type" => "array", "items" => result } if @is_collection - result - end - - def visit_class_node(node) - if node.superclass && node.superclass.full_name != "Blueprinter::Base" - visitor = @parser.__parse(node.superclass.name.to_s) - @identifier.merge!(visitor.identifier) - @schema.merge!(visitor.schema) - end - - super - end - - def visit_call_node(node) - case node.name - when :identifier - context = with_context { visit(node.arguments) } - @identifier[context.symbols.first] = { "type" => "string" } - - when :fields, :field - context = with_context { visit(node.arguments) } - - if context.keywords["name"] - @segment[context.keywords["name"]] = { "type" => "string" } - elsif node.block - @segment[context.symbols.first] = { "type" => "string" } if context.symbols.first - @segment[context.strings.first] = { "type" => "string" } if context.strings.first - else - context.symbols.each { |symbol| @segment[symbol] = { "type" => "string" } } - context.strings.each { |string| @segment[string] = { "type" => "string" } } - end - end - end - - def visit_assoc_node(node) - @context.keywords[node.key.value] = node.value.unescaped - end - - def visit_symbol_node(node) - @context.symbols << node.value - end - - def visit_string_node(node) - @context.strings << node.unescaped - end - - private - - def with_context - @context = VisitorContext.new - yield - @context + view.instance_variable_get(:@view_collection).instance_variable_get(:@views)[view_name].instance_variable_get(:@fields).each_with_object({}) do |(_, field), hash| + hash[field.name.to_s] = { "type" => "string" } end end end diff --git a/spec/openapi/parsers/ext/blueprinter_spec.rb b/spec/openapi/parsers/ext/blueprinter_spec.rb index 286923c3..e2d4baa1 100644 --- a/spec/openapi/parsers/ext/blueprinter_spec.rb +++ b/spec/openapi/parsers/ext/blueprinter_spec.rb @@ -1,9 +1,9 @@ # frozen_string_literal: true -require "prism" +require "blueprinter" RSpec.describe Rage::OpenAPI::Parsers::Ext::Blueprinter do - include_context "mocked_classes" + include_context "mocked_blueprinter_classes" subject { described_class.new(**options).parse(resource) } @@ -13,10 +13,8 @@ let(:resource) { "UserBlueprint" } context "with an empty blueprint" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - end RUBY end @@ -26,33 +24,28 @@ class UserBlueprint < Blueprinter::Base end context "with basic fields" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - fields :id, :name, :email, :age - end + fields :id, :name, :email, :age RUBY end - it do is_expected.to eq({ "type" => "object", "properties" => { - "id" => { "type" => "string" }, - "name" => { "type" => "string" }, + "age" => { "type" => "string" }, "email" => { "type" => "string" }, - "age" => { "type" => "string" } + "id" => { "type" => "string" }, + "name" => { "type" => "string" } } }) end end context "when fields are declared with strings" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - fields "id", "name", "email" - end + fields "id", "name", "email" RUBY end @@ -69,11 +62,9 @@ class UserBlueprint < Blueprinter::Base end context "with identifier" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - identifier :uuid - end + identifier :uuid RUBY end @@ -88,11 +79,9 @@ class UserBlueprint < Blueprinter::Base end context "with a single field" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - field :email - end + field :email RUBY end @@ -107,11 +96,9 @@ class UserBlueprint < Blueprinter::Base end context "with field name alias" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - field :email, name: :login - end + field :email, name: :login RUBY end @@ -126,11 +113,9 @@ class UserBlueprint < Blueprinter::Base end context "when field alias is declared with string values" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - field "email", name: "login" - end + field "email", name: "login" RUBY end @@ -145,11 +130,9 @@ class UserBlueprint < Blueprinter::Base end context "with a block field" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - field(:full_name) { |u| "#{u.first_name} #{u.last_name}" } - end + field(:full_name) { |u| "#{u.first_name} #{u.last_name}" } RUBY end @@ -164,11 +147,9 @@ class UserBlueprint < Blueprinter::Base end context "with a block field declared with string values" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - field("full_name") { |u| "#{u.first_name} #{u.last_name}" } - end + field("full_name") { |u| "#{u.first_name} #{u.last_name}" } RUBY end @@ -183,15 +164,13 @@ class UserBlueprint < Blueprinter::Base end context "with all declaration types combined" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - identifier :uuid - fields :id, :name, :age - field :email, name: :login - fields :first_name, :last_name - field(:full_name) { |u| "#{u.first_name} #{u.last_name}" } - end + identifier :uuid + fields :id, :name, :age + field :email, name: :login + fields :first_name, :last_name + field(:full_name) { |u| "#{u.first_name} #{u.last_name}" } RUBY end @@ -213,15 +192,13 @@ class UserBlueprint < Blueprinter::Base end context "with all declaration types combined with string values" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - identifier :uuid - fields "id", "name", "age" - field "email", name: "login" - fields "first_name", "last_name" - field("full_name") { |u| "#{u.first_name} #{u.last_name}" } - end + identifier :uuid + fields "id", "name", "age" + field "email", name: "login" + fields "first_name", "last_name" + field("full_name") { |u| "#{u.first_name} #{u.last_name}" } RUBY end @@ -243,15 +220,13 @@ class UserBlueprint < Blueprinter::Base end context "with all declaration types combined with string and symbol vales" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - identifier :uuid - fields :id, "name", :age - field :email, name: "login" - fields "first_name", :last_name - field("full_name") { |u| "#{u.first_name} #{u.last_name}" } - end + identifier :uuid + fields :id, "name", :age + field :email, name: "login" + fields "first_name", :last_name + field("full_name") { |u| "#{u.first_name} #{u.last_name}" } RUBY end @@ -273,12 +248,10 @@ class UserBlueprint < Blueprinter::Base end context "ensures identifier appears first in properties regardless of definition order" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - fields :name, :email - identifier :uuid - end + fields :name, :email + identifier :uuid RUBY end it do @@ -287,19 +260,15 @@ class UserBlueprint < Blueprinter::Base end context "with inheritance from another blueprint" do - let_class("BaseUserBlueprint") do + let_class("BaseUserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class BaseUserBlueprint < Blueprinter::Base - fields :id, :name - end + fields :id, :name RUBY end - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: mocked_classes["BaseUserBlueprint"]) do <<~'RUBY' - class UserBlueprint < BaseUserBlueprint - fields :email, :age - end + fields :email, :age RUBY end @@ -317,11 +286,9 @@ class UserBlueprint < BaseUserBlueprint end context "when superclass is Base (should not merge)" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - fields :id, :name - end + fields :id, :name RUBY end @@ -337,19 +304,15 @@ class UserBlueprint < Blueprinter::Base end context "when child blueprint overrides a parent field" do - let_class("BaseUserBlueprint") do + let_class("BaseUserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class BaseUserBlueprint < Blueprinter::Base - fields :id, :name - end + fields :id, :name RUBY end - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: mocked_classes["BaseUserBlueprint"]) do <<~'RUBY' - class UserBlueprint < BaseUserBlueprint - fields :name, :email - end + fields :name, :email RUBY end @@ -366,27 +329,21 @@ class UserBlueprint < BaseUserBlueprint end context "with multiple levels of inheritance" do - let_class("GrandparentBlueprint") do + let_class("GrandparentBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class GrandparentBlueprint < Blueprinter::Base - fields :id, :name - end + fields :id, :name RUBY end - let_class("ParentBlueprint") do + let_class("ParentBlueprint", parent: mocked_classes["GrandparentBlueprint"]) do <<~'RUBY' - class ParentBlueprint < GrandparentBlueprint - fields :email - end + fields :email RUBY end - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: mocked_classes["ParentBlueprint"]) do <<~'RUBY' - class UserBlueprint < ParentBlueprint - fields :age - end + fields :age RUBY end @@ -404,21 +361,17 @@ class UserBlueprint < ParentBlueprint end context "with identifier in parent blueprint" do - let_class("BaseUserBlueprint") do + let_class("BaseUserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class BaseUserBlueprint < Blueprinter::Base - identifier :uuid - fields :name - end + identifier :uuid + fields :name RUBY end - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: mocked_classes["BaseUserBlueprint"]) do <<~'RUBY' - class UserBlueprint < BaseUserBlueprint - identifier :id - fields :email - end + identifier :id + fields :email RUBY end @@ -442,11 +395,9 @@ class UserBlueprint < BaseUserBlueprint let(:resource) { "Array" } context "with basic fields" do - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - fields :id, :name, :email - end + fields :id, :name, :email RUBY end @@ -467,12 +418,10 @@ class UserBlueprint < Blueprinter::Base context "with identifier" do let(:resource) { "[UserBlueprint]" } - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class UserBlueprint < Blueprinter::Base - identifier :uuid - fields :name, :email - end + identifier :uuid + fields :name, :email RUBY end @@ -492,19 +441,15 @@ class UserBlueprint < Blueprinter::Base end context "with inherited fields" do - let_class("BaseUserBlueprint") do + let_class("BaseUserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class BaseUserBlueprint < Blueprinter::Base - fields :id, :name - end + fields :id, :name RUBY end - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: mocked_classes["BaseUserBlueprint"]) do <<~'RUBY' - class UserBlueprint < BaseUserBlueprint - fields :email - end + fields :email RUBY end @@ -524,25 +469,19 @@ class UserBlueprint < BaseUserBlueprint end context "with multiple levels of inheritance" do - let_class("GrandparentBlueprint") do + let_class("GrandparentBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class GrandparentBlueprint < Blueprinter::Base - fields :id, :name - end + fields :id, :name RUBY end - let_class("ParentBlueprint") do + let_class("ParentBlueprint", parent: mocked_classes["GrandparentBlueprint"]) do <<~'RUBY' - class ParentBlueprint < GrandparentBlueprint - fields :email - end + fields :email RUBY end - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: mocked_classes["ParentBlueprint"]) do <<~'RUBY' - class UserBlueprint < ParentBlueprint - fields :age - end + fields :age RUBY end it do @@ -562,20 +501,16 @@ class UserBlueprint < ParentBlueprint end context "with identifier in parent blueprint" do - let_class("BaseUserBlueprint") do + let_class("BaseUserBlueprint", parent: Blueprinter::Base) do <<~'RUBY' - class BaseUserBlueprint < Blueprinter::Base - identifier :uuid - fields :name - end + identifier :uuid + fields :name RUBY end - let_class("UserBlueprint") do + let_class("UserBlueprint", parent: mocked_classes["BaseUserBlueprint"]) do <<~'RUBY' - class UserBlueprint < BaseUserBlueprint - identifier :id - fields :email - end + identifier :id + fields :email RUBY end it "inherits identifier from parent" do diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index c6de6aca..1268dc87 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,6 +7,7 @@ require_relative "support/reactor_helper" require_relative "support/websocket_helper" require_relative "support/contexts/mocked_classes" +require_relative "support/contexts/mocked_blueprinter_classes" require_relative "support/contexts/mocked_rage_routes" require_relative "support/custom_matchers" diff --git a/spec/support/contexts/mocked_blueprinter_classes.rb b/spec/support/contexts/mocked_blueprinter_classes.rb new file mode 100644 index 00000000..df1a4a2d --- /dev/null +++ b/spec/support/contexts/mocked_blueprinter_classes.rb @@ -0,0 +1,28 @@ +# frozen_string_literal: true + +RSpec.shared_context "mocked_blueprinter_classes" do + include_context "mocked_classes" + + def self.let_class(class_name, parent: Object, &block) + source = Tempfile.new.tap do |f| + if block + f.write <<~RUBY + class #{class_name} #{"< #{parent.name}" if parent != Object} + #{block.call} + end + RUBY + end + f.close + end + + klass = Class.new(parent) + klass.class_eval(block.call) if block + klass.define_singleton_method(:name) { class_name } + mocked_classes[class_name] = klass + + before do + allow(Object).to receive(:const_get).with(satisfy { |c| c.to_s == class_name.to_s }).and_return(klass) + allow(Object).to receive(:const_source_location).with(class_name).and_return(source.path) + end + end +end From e3ad62518d42be4c0a561bdf0bf146891829c131 Mon Sep 17 00:00:00 2001 From: Abishekcs Date: Tue, 9 Jun 2026 20:56:12 +0530 Subject: [PATCH 2/3] Update Changelog. --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index f297f00a..50dc7981 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ### Added - [OpenAPI] Add support for blueprint inheritance. Child blueprints now inherit fields from parent blueprints. +- [OpenAPI] Refactor `Rage::OpenAPI::Parsers::Ext::Blueprinter` using live reflection instead of Prism AST traversal to simplify schema extraction ### Fixed From 87977ff9eadfc48a161447d8d3e9a7a6234566bc Mon Sep 17 00:00:00 2001 From: Abishekcs Date: Wed, 10 Jun 2026 13:22:56 +0530 Subject: [PATCH 3/3] test: update mocked_classes to support Blueprinter Reflection API The Reflection API requires DSL calls to be evaluated in the class context at runtime. Updated let_class to call class_eval with the block's string content when the parent is a Blueprinter class, while preserving the original behavior for all other classes (Alba, controllers, etc). Switched blueprinter_spec to use mocked_classes instead of the separate mocked_blueprinter_classes context, and removed mocked_blueprinter_classes from spec_helper since it's no longer needed. Also switched to view.fields official API using field.display_name instead of instance_variable_get hacks, and dropped unsupported mixed string/symbol field test cases. --- lib/rage/openapi/parsers/ext/blueprinter.rb | 4 +- spec/openapi/parsers/ext/blueprinter_spec.rb | 39 ++----------------- spec/spec_helper.rb | 1 - .../contexts/mocked_blueprinter_classes.rb | 28 ------------- spec/support/contexts/mocked_classes.rb | 9 +++++ 5 files changed, 14 insertions(+), 67 deletions(-) delete mode 100644 spec/support/contexts/mocked_blueprinter_classes.rb diff --git a/lib/rage/openapi/parsers/ext/blueprinter.rb b/lib/rage/openapi/parsers/ext/blueprinter.rb index 1349b42b..499d16ad 100644 --- a/lib/rage/openapi/parsers/ext/blueprinter.rb +++ b/lib/rage/openapi/parsers/ext/blueprinter.rb @@ -37,8 +37,8 @@ def build_schema(klass, is_collection) def extract_fields(reflections, view_name) return {} unless (view = reflections[view_name]) - view.instance_variable_get(:@view_collection).instance_variable_get(:@views)[view_name].instance_variable_get(:@fields).each_with_object({}) do |(_, field), hash| - hash[field.name.to_s] = { "type" => "string" } + view.fields.each_with_object({}) do |(_, field), hash| + hash[field.display_name.to_s] = { "type" => "string" } end end end diff --git a/spec/openapi/parsers/ext/blueprinter_spec.rb b/spec/openapi/parsers/ext/blueprinter_spec.rb index e2d4baa1..9f7427d0 100644 --- a/spec/openapi/parsers/ext/blueprinter_spec.rb +++ b/spec/openapi/parsers/ext/blueprinter_spec.rb @@ -3,7 +3,7 @@ require "blueprinter" RSpec.describe Rage::OpenAPI::Parsers::Ext::Blueprinter do - include_context "mocked_blueprinter_classes" + include_context "mocked_classes" subject { described_class.new(**options).parse(resource) } @@ -196,9 +196,7 @@ <<~'RUBY' identifier :uuid fields "id", "name", "age" - field "email", name: "login" - fields "first_name", "last_name" - field("full_name") { |u| "#{u.first_name} #{u.last_name}" } + field "email", name: :login RUBY end @@ -210,38 +208,7 @@ "id" => { "type" => "string" }, "name" => { "type" => "string" }, "age" => { "type" => "string" }, - "login" => { "type" => "string" }, - "first_name" => { "type" => "string" }, - "last_name" => { "type" => "string" }, - "full_name" => { "type" => "string" } - } - }) - end - end - - context "with all declaration types combined with string and symbol vales" do - let_class("UserBlueprint", parent: Blueprinter::Base) do - <<~'RUBY' - identifier :uuid - fields :id, "name", :age - field :email, name: "login" - fields "first_name", :last_name - field("full_name") { |u| "#{u.first_name} #{u.last_name}" } - RUBY - end - - it do - is_expected.to eq({ - "type" => "object", - "properties" => { - "uuid" => { "type" => "string" }, - "id" => { "type" => "string" }, - "name" => { "type" => "string" }, - "age" => { "type" => "string" }, - "login" => { "type" => "string" }, - "first_name" => { "type" => "string" }, - "last_name" => { "type" => "string" }, - "full_name" => { "type" => "string" } + "login" => { "type" => "string" } } }) end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 1268dc87..c6de6aca 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -7,7 +7,6 @@ require_relative "support/reactor_helper" require_relative "support/websocket_helper" require_relative "support/contexts/mocked_classes" -require_relative "support/contexts/mocked_blueprinter_classes" require_relative "support/contexts/mocked_rage_routes" require_relative "support/custom_matchers" diff --git a/spec/support/contexts/mocked_blueprinter_classes.rb b/spec/support/contexts/mocked_blueprinter_classes.rb deleted file mode 100644 index df1a4a2d..00000000 --- a/spec/support/contexts/mocked_blueprinter_classes.rb +++ /dev/null @@ -1,28 +0,0 @@ -# frozen_string_literal: true - -RSpec.shared_context "mocked_blueprinter_classes" do - include_context "mocked_classes" - - def self.let_class(class_name, parent: Object, &block) - source = Tempfile.new.tap do |f| - if block - f.write <<~RUBY - class #{class_name} #{"< #{parent.name}" if parent != Object} - #{block.call} - end - RUBY - end - f.close - end - - klass = Class.new(parent) - klass.class_eval(block.call) if block - klass.define_singleton_method(:name) { class_name } - mocked_classes[class_name] = klass - - before do - allow(Object).to receive(:const_get).with(satisfy { |c| c.to_s == class_name.to_s }).and_return(klass) - allow(Object).to receive(:const_source_location).with(class_name).and_return(source.path) - end - end -end diff --git a/spec/support/contexts/mocked_classes.rb b/spec/support/contexts/mocked_classes.rb index cb80288e..85671b8c 100644 --- a/spec/support/contexts/mocked_classes.rb +++ b/spec/support/contexts/mocked_classes.rb @@ -25,6 +25,15 @@ class #{class_name} #{"< #{parent.name}" if parent != Object} end klass = Class.new(parent, &block) + + if block + if defined?(Blueprinter::Base) && parent.ancestors.include?(Blueprinter::Base) + klass.class_eval(block.call) + else + klass.class_eval(&block) + end + end + klass.define_singleton_method(:name) { class_name } mocked_classes[class_name] = klass