module Terror def errors(from: ::StandardError, &blk) ErrorBuilder.new(self, from).instance_exec(&blk) end def const_missing(name) return super unless name.to_s.end_with?("Error") const_set(name, Class.new(StandardError)) end class ErrorBuilder < BasicObject def initialize(klass, from) @klass = klass @from = from end def method_missing(name, **kwargs, &blk) error = ::Class.new(kwargs.fetch(:from, @from), &blk) if kwargs.has_key?(:message) error.class_eval("def message; '#{kwargs.fetch(:message)}'; end") end @klass.const_set(name, error) end end end