Skip to content

Mixins #79

@bajtos

Description

@bajtos

LoopBack provides two kinds of mixins which we need to support in loopback-boot too:

  • A regular LoopBack model - mixing process copies all properties and methods to the target model.
  • A function - mixing process calls the function after the model has been attached.

Mixin as a model - NOT IMPLEMENTED

loopback-boot already has mechanism for defining arbitrary models, we should reuse this mechanism for defining mixins too. Benefits: developers can apply their knowledge about how to create models to create mixins too. Tools like yo loopback:model and Studio will support mixin definitions OOTB.

// common/models/timestamp-mixin.json
{
  "name": "TimeStampMixin",
  "properties": {
    "timestamp": "Date"
  }
}

// common/models/timestamp-mixin.js
module.exports = function(TimeStampMixin) {
  // setup auto-update of timestamp on save
}

To support this kind of mixins, we need to extend addAllBaseModels() and sortByInheritance() to take into account mixed-in models in addition to base models. Once that is done, model-like mixins will work both on the server and in the browserified client.

Note that this is can be implemented independently (and should be).

Mixin as a function

This is the part where we need to come up with a new convention.

Ultimately, I'd like to see a solution similar to what we have for models, where the compiler understands relations between model definitions and mixin definitions, and can build optimised instructions to load only those mixins that are actually used in the app.

Another thing to consider is the new component architecture as outlined here.

I am trying to come up with a small solution that can be incrementally extended in next PRs.

How about this:

  • compiler takes two new options: mixinScripts that contains a list of source files to load, normalizeNames that defines the algorithm for converting file names into mixin/model names.
  • mixin names are built from file names by the compiler, using normalizeNames
  • executor is extended to define mixins per instructions from the compiler

Usage:

boot(app, {
  appRootDir: __dirname,
  mixins: [ './mixins/timestamp.js' ]
});

The next step is to implement clever auto-loading of mixins from directories, as described earlier. That way the usage would become

// explicit
boot(app, {
  appRootDir: __dirname,
  mixinSources: [ './mixins' ]
});

// relying on the default mixinSources value
boot(app, __dirname)

To summarise the next steps as I see them now:

  1. Support mixins defined as models
  2. Support mixins defined as functions via mixins option
  3. Support mixins defined as functions via mixinSources option

Random notes:

  • It seems to me that the setup is not handled correctly by the current mixing architecture. Imagine a TimeStampMixin as outlined above that adds a setup method to configure hooks to update the timestamp. Such setup method would override any setup method provided by the target class. This is most likely a problem of Model hooks in general as the same applies to beforeSave.
  • How can a Model that is being mixed in access the mixin options provided by the app? It seems to me these options are discarded when the mixin is a Model instead of a function.

This issue is a follow-up for the unfinished pull request #33, the discussion there contains useful bits of information too.

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions