Module: MetaRuby::ModelAsModule

Extended by:
Attributes
Includes:
Attributes, Registration
Defined in:
lib/metaruby/model_as_module.rb

Overview

Extend in modules that are used as models

Alternatively, one can create a module to describe the metamodel for our base model and then include it in the actual root model

Examples:

module MyBaseModel
  extend MetaRuby::ModelAsModule
end
module MyBaseMetamodel
  include MetaRuby::ModelAsModule
end
module MyBaseModel
  extend MyBaseMetamodel
end

Constant Summary

Constants included from Attributes

Attributes::ANCESTORS_ACCESS

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from Attributes

define_single_value_with_promotion, define_single_value_without_promotion, included, inherited_attribute, inherited_single_value_attribute, map_with_promotion, map_without_promotion, nomap_with_promotion, nomap_without_promotion

Methods included from Registration

accessible_by_name?, #accessible_by_name?, #clear_registration_as_constant, #clear_submodels, deregister_constant, #deregister_submodels, #each_submodel, #has_submodel?, #permanent_definition_context?, #permanent_model?, #register_submodel, #submodels

Instance Attribute Details

#definition_locationArray[(String,Integer,Symbol)]

The call trace at the point of definition. It is usually used to report to the user in which file this model got defined

Returns:

  • (Array[(String,Integer,Symbol)])

    a list of (file,line,method) tuples as returned by #call_stack (from the facet gem)



80
81
82
# File 'lib/metaruby/model_as_module.rb', line 80

def definition_location
  @definition_location
end

#nameString

Sets a name on this model

Only use this on ‘anonymous models’, i.e. on models that are not meant to be assigned on a Ruby constant

Returns:

  • (String)

    the assigned name



96
97
98
# File 'lib/metaruby/model_as_module.rb', line 96

def name=(name)
    @name = name
end

#supermodelObject

Set or get the root model



112
113
114
# File 'lib/metaruby/model_as_module.rb', line 112

def supermodel
  @supermodel
end

Class Method Details

.create_and_register_submodel(namespace, name, base_model, *args, &block) ⇒ Module

Common method that can be used to create and register a submodel-as-a-module on a provided namespace

It is usually used to create specific DSL-like methods that allow to create these models

Parameters:

  • namespace (Module, Class)
  • name (String)

    the model name, it must be valid for a Ruby constant name

  • base_model (Module)

    the base model, which should include MetaRuby::ModelAsModule itself

  • args (Array)

    additional arguments to pass to base_model’s #setup_submodel

  • block (#call)

    block passed to base_model’s #setup_submodel

Returns:



58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
# File 'lib/metaruby/model_as_module.rb', line 58

def self.create_and_register_submodel(namespace, name, base_model, *args, &block)
    ModelAsModule.validate_constant_name(name)

    if namespace.const_defined?(name, false)
        model = namespace.const_get(name)
        base_model.setup_submodel(model, *args, &block)
    else 
        namespace.const_set(name, model = base_model.new_submodel(*args, &block))
        model.permanent_model = if !namespace.respond_to?(:permanent_model?)
                                    Registration.accessible_by_name?(namespace)
                                else namespace.permanent_model?
                                end
    end

    model
end

.extend_object(obj) ⇒ Object



106
107
108
109
# File 'lib/metaruby/model_as_module.rb', line 106

def self.extend_object(obj)
    obj.instance_variable_set :@name, nil
    super
end

.validate_constant_name(name) ⇒ Object

Validate that a string can be used as a constant name

Parameters:

  • name (String)

    the name to validate

Raises:

  • (ArgumentError)

    if the name cannot be used as a constant name



37
38
39
40
41
# File 'lib/metaruby/model_as_module.rb', line 37

def self.validate_constant_name(name)
    if name !~ /^[A-Z]\w+$/
        raise ArgumentError, "#{name} is not a valid model name"
    end
end

Instance Method Details

#apply_block(&block) ⇒ void

This method returns an undefined value.

Called to apply a model definition block on this model

The definition class-eval’s it



163
164
165
# File 'lib/metaruby/model_as_module.rb', line 163

def apply_block(&block)
    class_eval(&block)
end

#clear_modelObject

In the case of model-as-modules, we always deregister (regardless of the fact that self is permanent or not). The reason for this is that the model-as-module hierarchy is much more dynamic than model-as-class. Who provides what can be changed after a #clear_model call.



149
150
151
152
153
154
155
156
# File 'lib/metaruby/model_as_module.rb', line 149

def clear_model
    super
    if supermodel
        supermodel.deregister_submodels([self])
    end
    @supermodel = nil
    parent_models.clear
end

#docString #doc(new_doc) ⇒ String

Overloads:

  • #docString

    Returns the documentation text for this model.

    Returns:

    • (String)

      the documentation text for this model

  • #doc(new_doc) ⇒ String

    Returns the documentation text for this model.

    Parameters:

    • new_doc (String)

      the new documentation

    Returns:

    • (String)

      the documentation text for this model



31
# File 'lib/metaruby/model_as_module.rb', line 31

inherited_single_value_attribute :doc

#new_submodel(name: nil, type: self.class, **submodel_options, &block) ⇒ Object

Creates a new DataServiceModel that is a submodel of self

Parameters:

  • name (String) (defaults to: nil)

    the submodel name. Use this option only for “anonymous” models, i.e. models that won’t be registered on a Ruby constant

  • type (Class) (defaults to: self.class)

    (self.class) the type of the submodel



121
122
123
124
125
126
127
128
129
130
131
132
# File 'lib/metaruby/model_as_module.rb', line 121

def new_submodel(name: nil, type: self.class, **submodel_options, &block)
    model = type.new
    model.extend ModelAsModule
    model.name = name.dup if name
    model.definition_location = 
        if MetaRuby.keep_definition_location?
            caller_locations
        else Array.new
        end
    setup_submodel(model, submodel_options, &block)
    model
end

#provides(model) ⇒ Object

Declares that this model also provides this other given model



175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
# File 'lib/metaruby/model_as_module.rb', line 175

def provides(model)
    include model

    model_root =
        if model.root? then model
        else model.supermodel
        end

    if !supermodel
        self.supermodel = model_root
        self.supermodel.register_submodel(self)
    elsif supermodel != model_root
        if model_root.provides?(supermodel)
            self.supermodel = model_root
        elsif !supermodel.provides?(model_root)
            raise ArgumentError, "#{model}'s root is #{model_root} while #{self} is #{supermodel}, which are unrelated"
        end
        self.supermodel.register_submodel(self)
    end

    self.parent_models.merge(model.parent_models)
    self.parent_models << model
end

#provides?(model) ⇒ Boolean

Tests whether self provides the given model

Parameters:

Returns:

  • (Boolean)


170
171
172
# File 'lib/metaruby/model_as_module.rb', line 170

def provides?(model)
    self <= model
end

#setup_submodel(submodel, options = Hash.new, &block) ⇒ Object

Called when a new submodel has been created, on the newly created submodel



136
137
138
139
140
141
142
# File 'lib/metaruby/model_as_module.rb', line 136

def setup_submodel(submodel, options = Hash.new, &block)
    submodel.provides self

    if block_given?
        submodel.apply_block(&block)
    end
end