Module: FeatureEnvy::FinalClass

Defined in:
lib/feature_envy/final_class.rb

Overview

Final classes.

### Definition

A final class is a class that cannot be inherited from. In other words, a final class enforces the invariant that it has no subclasses.

### Applications

Preventing subclassing of classes that weren’t specifically designed for handling it.

### Usage

  1. Enable the feature in a specific class via ‘extend Feature::FinalClass`. The class has been marked final and there’s nothing else to do. Alternatively, …

  2. Enable the feature in a specific scope using a refinement via ‘using FeatureEnvy::FinalClass` and call final! in all classes that should be marked final.

### Discussion

A class in Ruby can be made final by raising an error in its inherited hook. This is what this module does. However, this is not enough to guarantee that no subclasses will be created. Due to Ruby’s dynamic nature it’d be possible to define a class, subclass, and then reopen the class and mark it final. This edge is taken care of and would result in an exception.

Examples:

module Models
  # Use the refinement within the module, so that all classes defined
  # within support the final! method.
  using FeatureEnvy::FinalClass

  class User < Base
    # Mark the User class final.
    final!
  end
end

Defined Under Namespace

Classes: Error

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.extended(final_class) ⇒ Object

Raises:



71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/feature_envy/final_class.rb', line 71

def extended final_class
  # The class must be marked final first, before we check whether there
  # are already existing subclasses. If the error were raised first then
  # .final? would return +false+ causing confusion: if the class isn't
  # final then why was the error raised?
  @classes << final_class

  subclasses = Internal.subclasses final_class
  return if subclasses.empty?

  raise Error.new(final_class:, subclasses:)
end

.final?(klass) ⇒ Boolean

Determines whether a given class is marked final.

Parameters:

  • klass (Class)

    The class whose finality should be checked.

Returns:

  • (Boolean)

    true if klass is final, false otherwise.



88
89
90
# File 'lib/feature_envy/final_class.rb', line 88

def final? klass
  @classes.include? klass
end

Instance Method Details

#inherited(klass) ⇒ Object

Raises:



94
95
96
# File 'lib/feature_envy/final_class.rb', line 94

def inherited klass # rubocop:disable Lint/MissingSuper
  raise Error.new(final_class: self, subclasses: [klass])
end