From e2fed5d464caa87e9d8a040feb1f17c621c1a18c Mon Sep 17 00:00:00 2001 From: Alexander Repnikov Date: Thu, 28 Dec 2023 16:53:24 +0100 Subject: [PATCH] Virtual & Pure methods macros raises an error if concrete method exists --- lib/virtual.rb | 1 + lib/virtual/method_defined.rb | 15 +++++++++++++++ lib/virtual/pure_method.rb | 5 +++++ lib/virtual/virtual_method.rb | 5 +++++ ...verrides_concrete.rb => overrides_concrete.rb} | 0 ...verrides_concrete.rb => overrides_concrete.rb} | 0 6 files changed, 26 insertions(+) create mode 100644 lib/virtual/method_defined.rb rename test/automated/pure_virtual/{_overrides_concrete.rb => overrides_concrete.rb} (100%) rename test/automated/virtual/{_overrides_concrete.rb => overrides_concrete.rb} (100%) diff --git a/lib/virtual.rb b/lib/virtual.rb index 03acce1..f82283f 100644 --- a/lib/virtual.rb +++ b/lib/virtual.rb @@ -1,3 +1,4 @@ +require 'virtual/method_defined' require 'virtual/virtual_method' require 'virtual/pure_method' require 'virtual/protocol_method' diff --git a/lib/virtual/method_defined.rb b/lib/virtual/method_defined.rb new file mode 100644 index 0000000..dcf5e52 --- /dev/null +++ b/lib/virtual/method_defined.rb @@ -0,0 +1,15 @@ +module Virtual + module MethodDefined + def self.call(target_class, method_name) + target_class.method_defined?(method_name, true) || + target_class.private_method_defined?(method_name, true) + end + + module SourceLocation + def self.get(target_class, method_name) + method = target_class.instance_method(method_name) + method.source_location.join(':') + end + end + end +end diff --git a/lib/virtual/pure_method.rb b/lib/virtual/pure_method.rb index e710a0d..3ac1b46 100644 --- a/lib/virtual/pure_method.rb +++ b/lib/virtual/pure_method.rb @@ -3,6 +3,11 @@ module PureMethod Error = Class.new(RuntimeError) def self.define(target_class, method_name) + if MethodDefined.(target_class, method_name) + method_source_location = MethodDefined::SourceLocation.get(target_class, method_name) + raise Error, "Pure virtual (abstract) method #{method_name} of #{target_class.name} must not already be implemented (Source Location: #{method_source_location})" + end + target_class.send(:define_method, method_name) do |*args| raise Error, "Pure virtual (abstract) method #{method_name} of #{self.class.name} must be implemented" end diff --git a/lib/virtual/virtual_method.rb b/lib/virtual/virtual_method.rb index 38c63de..e600278 100644 --- a/lib/virtual/virtual_method.rb +++ b/lib/virtual/virtual_method.rb @@ -6,6 +6,11 @@ def self.define(target_class, method_name, &blk) blk ||= proc do |*| end + if MethodDefined.(target_class, method_name) + method_source_location = MethodDefined::SourceLocation.get(target_class, method_name) + raise Error, "Virtual (abstract) method #{method_name} of #{target_class.name} must not already be implemented (Source Location: #{method_source_location})" + end + target_class.send(:define_method, method_name, &blk) end end diff --git a/test/automated/pure_virtual/_overrides_concrete.rb b/test/automated/pure_virtual/overrides_concrete.rb similarity index 100% rename from test/automated/pure_virtual/_overrides_concrete.rb rename to test/automated/pure_virtual/overrides_concrete.rb diff --git a/test/automated/virtual/_overrides_concrete.rb b/test/automated/virtual/overrides_concrete.rb similarity index 100% rename from test/automated/virtual/_overrides_concrete.rb rename to test/automated/virtual/overrides_concrete.rb