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