module MetaRuby::ModelAsModule

Extend in modules that are used as models

@example

module MyBaseModel
  extend MetaRuby::ModelAsModule
end

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

@example

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

Attributes

definition_location[RW]

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

@return [Array] a list of (file,line,method)

tuples as returned by #call_stack (from the facet gem)
supermodel[RW]

Set or get the root model

Public Class Methods

create_and_register_submodel(namespace, name, base_model, *args, &block) click to toggle source

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

@param [Module,Class] namespace @param [String] name the model name, it must be valid for a Ruby

constant name

@param [Module] base_model the base model, which should include

{ModelAsModule} itself

@param [Array] args additional arguments to pass to base_model's

#setup_submodel

@param [#call] block block passed to base_model's setup_submodel @return [Module] the new model

# 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) click to toggle source
Calls superclass method
# 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) click to toggle source

Validate that a string can be used as a constant name

@param [String] name the name to validate @raise [ArgumentError] if the name cannot be used as a constant name

# 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

Public Instance Methods

apply_block(&block) click to toggle source

Called to apply a model definition block on this model

The definition class-eval's it

@return [void]

# File lib/metaruby/model_as_module.rb, line 163
def apply_block(&block)
    class_eval(&block)
end
clear_model() click to toggle source

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.

Calls superclass method MetaRuby::Registration#clear_model
# 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
name() click to toggle source
Calls superclass method
# File lib/metaruby/model_as_module.rb, line 100
def name
    if @name then @name
    else super
    end
end
name=(name) click to toggle source

@!attribute [rw] name

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

@return [String] the assigned name

# File lib/metaruby/model_as_module.rb, line 96
def name=(name)
    @name = name
end
new_submodel(name: nil, type: self.class, **submodel_options, &block) click to toggle source

Creates a new DataServiceModel that is a submodel of self

@param [String] name the submodel name. Use this option

only for "anonymous" models, i.e. models that won't be
registered on a Ruby constant

@param [Class] type (self.class) the type of the submodel

# 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) click to toggle source

Declares that this model also provides this other given model

# 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) click to toggle source

Tests whether self provides the given model

@param [Module] model

# File lib/metaruby/model_as_module.rb, line 170
def provides?(model)
    self <= model
end
setup_submodel(submodel, options = Hash.new, &block) click to toggle source

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

# 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