Skip to content

Instantly share code, notes, and snippets.

@Bongs
Created December 21, 2017 10:25
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Bongs/d6a477dabf829dc8451d7df4f7c8b8f7 to your computer and use it in GitHub Desktop.
Save Bongs/d6a477dabf829dc8451d7df4f7c8b8f7 to your computer and use it in GitHub Desktop.
Ruby on Rails - Abstract class and STI. Make any class an Abstract class at any level of STI. Raise error when abstract class instantiated.
module Abstractable
extend ActiveSupport::Concern
included do
class << self
attr_accessor :isabstract
end
@isabstract = true
end
def initialize(*args)
# Raise custom error
raise CustomError::AbstractClass.new(self.class) if self.class.isabstract
super
end
module ClassMethods
def inherited(subklass)
subklass.instance_variable_set('@isabstract', instance_variable_get('@isabstract'))
super
end
end
end
module CustomError
class Error < StandardError; end
class AbstractClass < CustomError::Error
attr_reader :klass_object, :message
attr_writer :default_message
def initialize(klass_object = nil, message = nil)
@message = message
@klass_object = klass_object
@default_message = "Can not instantiate abstract class. Type has to be specified."
if klass_object.present? && Object.const_defined?(klass_object.to_s)
descendants_list = klass_object.try(:descendants).try(:reject, &:isabstract).try(:map, &:name).try(:map, &:demodulize).try(:join, ', ')
@default_message += " #{klass_object.to_s} can be one of type: #{descendants_list}."
end
end
def to_s
@message || @default_message
end
end
end
module TopLevelModule
class SomeBaseClass < ApplicationRecord
# By default it will make this class and its child classes an Abstract class
# after including the Abstractable module
include Abstractable
end
module SomeNameSpace1
class SomeChildClass1 < TopLevelModule::SomeBaseClass
# Makes the child class non-abstract
@isabstract = false
end
class SomeChildClass2 < TopLevelModule::SomeBaseClass
@isabstract = false
end
class SomeChildClass3 < TopLevelModule::SomeBaseClass
@isabstract = false
end
end
class AnotherChildClass < TopLevelModule::SomeBaseClass
# An abstract class.
end
module SomeNameSpace2
class SomeOtherChild1 < TopLevelModule::AnotherChildClass
@isabstract = false
end
class SomeOtherChild2 < TopLevelModule::AnotherChildClass
@isabstract = false
end
end
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment