Class: Democritus::ClassBuilder

Inherits:
Object
  • Object
show all
Defined in:
lib/democritus/class_builder.rb,
lib/democritus/class_builder/command.rb,
lib/democritus/class_builder/commands/attribute.rb,
lib/democritus/class_builder/commands/attributes.rb

Overview

Responsible for building a class based on the customization’s applied through the #customize method.

:reek:UnusedPrivateMethod: { exclude: [ !ruby/regexp /(method_missing|respond_to_missing)/ ] }

See Also:

  • /spec/lib/democritus/class_builder_spec./spec/lib/democritus/class_builder_spec.rb

Defined Under Namespace

Modules: Commands Classes: Command

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(command_namespaces: default_command_namespaces) ⇒ ClassBuilder

Returns a new instance of ClassBuilder.

Parameters:

  • command_namespaces (Array<Module>) (defaults to: default_command_namespaces)

    the sequential list of namespaces you want to check for each registered command.



10
11
12
13
14
15
16
# File 'lib/democritus/class_builder.rb', line 10

def initialize(command_namespaces: default_command_namespaces)
  self.customization_module = Module.new
  self.generation_module = Module.new
  self.class_operations = []
  self.instance_operations = []
  self.command_namespaces = command_namespaces
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(method_name, *args, **kargs, &block) ⇒ Object (private)

The guts of the Democritus plugin system. The ClassBuilder brokers missing methods to registered commands within the CommandNamespace.

Parameters:

  • method_name (Symbol)

    Name of the message being sent to this object

  • args

    Non-keyword arguments for the message sent to this object

  • kargs

    Keyword arguments for the message sent to this object

  • block

    Block argument for the message sent to this object

Returns:

  • void if there is a Command object that is called

  • unknown if no Command object is found



172
173
174
175
176
177
178
179
180
181
# File 'lib/democritus/class_builder.rb', line 172

def method_missing(method_name, *args, **kargs, &block)
  command_name = self.class.command_name_for_method(method_name)
  command_namespace = command_namespace_for(command_name)
  if command_namespace
    command_class = command_namespace.const_get(command_name)
    command_class.new(*args, **kargs.merge(builder: self), &block).call
  else
    super
  end
end

Class Method Details

.command_name_for_method(method_name) ⇒ Object

Convert the given :method_name into a “constantized” method name.

Examples:

Democritus::ClassBuilder.command_name_for_method(:test_command) == 'TestCommand'

Parameters:

  • method_name (#to_s)

Returns:

  • String



219
220
221
# File 'lib/democritus/class_builder.rb', line 219

def command_name_for_method(method_name)
  method_name.to_s.gsub(/(?:^|_)([a-z])/) { Regexp.last_match[1].upcase }
end

Instance Method Details

#customize {|Democritus::ClassBuilder| ... } ⇒ Object

Responsible for executing the customization block against the customization module with the builder class as a parameter.

Examples:

ClassBuilder.new.customize do |builder|
  builder.command('paramter')
  def to_s; 'parameter'; end
end

Yields:

Returns:

  • nil

See Also:

  • Democritus::ClassBuilder./spec/lib/democritus/class_builder_spec./spec/lib/democritus/class_builder_spec.rb


87
88
89
90
91
# File 'lib/democritus/class_builder.rb', line 87

def customize(&customization_block)
  return unless customization_block
  customization_module.module_exec(self, &customization_block)
  return nil
end

#defer(**options, &deferred_operation) ⇒ Object

When configuring the class that is being built, we don’t want to apply all of the modifications at once, instead allowing them to be applied in a specified order.

Examples:

Democritus::ClassBuilder.new.defer(prepend: true) do
  define_method(:help) { 'Did you try turning it off and on again?' }
end

Parameters:

  • options (Hash)
  • deferred_operation (#call)

    The operation that will be applied to the generated class

Options Hash (**options):

  • :prepend (Boolean)

    Is there something about this deferred_operation that should happen first?

Returns:

  • void



140
141
142
143
144
145
146
# File 'lib/democritus/class_builder.rb', line 140

def defer(**options, &deferred_operation)
  if options[:prepend]
    instance_operations.unshift(deferred_operation)
  else
    instance_operations << deferred_operation
  end
end

#generate_classObject

Responsible for generating a Class object based on the customizations applied via a customize block.

rubocop:disable MethodLength :reek:TooManyStatements: { exclude: [ ‘Democritus::ClassBuilder#generate_class’ ] }

Examples:

dynamic_class = Democritus::ClassBuilder.new.generate_class
an_instance_of_the_dynamic_class = dynamic_class.new

Returns:

  • Class object



106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
# File 'lib/democritus/class_builder.rb', line 106

def generate_class
  generation_mod = generation_module # get a local binding
  customization_mod = customization_module # get a local binding
  apply_operations(instance_operations, generation_mod)
  generated_class = Class.new do
    # requires the local binding from above
    const_set :GeneratedMethods, generation_mod
    const_set :Customizations, customization_mod
    # because == and case operators are useful
    include DemocritusObjectTag
    include generation_mod

    # customization should be applied last as it allows for "overrides" of generated methods
    include customization_mod
  end
  generated_class
end