Class: Farce::Extension

Inherits:
Module
  • Object
show all
Defined in:
lib/farce/extension.rb

Overview

Defines a reusable extension for adding methods to target classes. These methods can either be activated as refinements, or installed globally to extend the target classes.

DoubleSize = Farce::Extension.define(String) do
  def double_size = size * 2
end

module Namespace
  using DoubleSize
  "hello".double_size # => 10
end

"hello".respond_to?(:double_size) # => false

DoubleSize.install
"hello".respond_to?(:double_size) # => true

Extending multiple classes

An extension can also modify multiple target classes simultaneously:

DoubleSize = Farce::Extension.new do
  define(String) { def double_size = size * 2 }
  define(Array) { def double_size = sum(&:double_size) }
end

using DoubleSize
["hello", "foo"].double_size # => 16

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize { ... } ⇒ Extension

Creates a new extension.

Yields:

  • Optional block for define calls or adding methods to the extension itself.

Yield Receiver:

  • (Extension) —

    the extension instance itself.



70
71
72
73
74
75
76
# File 'lib/farce/extension.rb', line 70

def initialize
  check_state!
  @definitions = Internal::StrictMap.new
  @lock        = Lock.new
  @installed   = Flag.new
  super
end

Class Method Details

.define(*targets) { ... } ⇒ Extension

Defines an extension for the given target classes.

Parameters:

  • targets (Array<Class>) —

    the target classes to define methods for

Yields:

  • the block containing method definitions

Returns:

  • (Extension) —

    the newly created extension instance

See Also:

Yield Receiver:

  • (Module, Refinement) —

    the module/refinement used for extending the target class.



65
# File 'lib/farce/extension.rb', line 65

def self.define(...) = new { define(...) }

Instance Method Details

#define(*targets) { ... } ⇒ self

Defines additional methods for a given target class.

The given block will be invoked at least twice. Once in the context of a refinement for the target class, and once in the context of the mixin module that will be prepended to the target class.

rubocop:disable-next Naming/BlockForwarding, Style/ArgumentsForwarding

Examples:

extension = Farce::Extension.new
extension.define(String) { def double_size = size * 2 }

module Namespace
  using extension
  String.method_defined?(:double_size) # => true
end

String.method_defined?(:double_size) # => false
extension.install
String.method_defined?(:double_size) # => true

Parameters:

  • targets (Array<Class>) —

    the target classes to define methods for

Yields:

  • the block containing method definitions

Returns:

  • (self) —

    the extension instance

Yield Receiver:

  • (Module, Refinement) —

    the module/refinement used for extending the target class.



104
105
106
107
108
109
110
111
112
113
114
115
116
# File 'lib/farce/extension.rb', line 104

def define(*targets, &block)
  # JRuby rejects repeated anonymous block forwarding inside a block.
  @lock.synchronize do
    check_state!
    targets.each do |target|
      refine(target, &block)
      mixin = @definitions.store_if_absent(target) { Mixin.new(self, target) }
      mixin.module_eval(&block)
      mixin.install if installed?
    end
  end
  self
end

#freeze ⇒ self

Freezes the extension.

No definitions can be added after freezing the extension.

Returns:

  • (self) —

    the frozen extension



137
138
139
140
141
142
143
# File 'lib/farce/extension.rb', line 137

def freeze
  return self if frozen?
  @lock.synchronize do
    @definitions.freeze
    super
  end
end

#install

This method returns an undefined value.

Installs the extension globally.



120
121
122
123
124
125
126
127
128
# File 'lib/farce/extension.rb', line 120

def install
  @lock.synchronize do
    return false if installed?
    check_state!(allow_frozen: true)
    @definitions.each_value(&:install)
    yield if block_given?
    @installed.set
  end
end

#installed? ⇒ Boolean

Returns whether the extension has been installed.

Returns:

  • (Boolean) —

    whether the extension has been installed



131
# File 'lib/farce/extension.rb', line 131

def installed? = @installed.set?