最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

javascript - How to create a normal sails model without being in the models folder - Stack Overflow

programmeradmin4浏览0评论

So,

I'm in the middle of implementing a plugin api for my application, and the plugins can have their own models, imagine this.

SimplePlugin = {
    pluginName: 'simple',

    pluginConfig: {},

    SimpleModel: {

        attributes: {
            name: 'string'
        }

    } 

}

So I need to be able to create the "one-time" model with a function whenever it's needed, it needs to have exactly the same functionality as other models so you automatically get the urls like /simplePlugin/:id for find ..etc

Thanks

So,

I'm in the middle of implementing a plugin api for my application, and the plugins can have their own models, imagine this.

SimplePlugin = {
    pluginName: 'simple',

    pluginConfig: {},

    SimpleModel: {

        attributes: {
            name: 'string'
        }

    } 

}

So I need to be able to create the "one-time" model with a function whenever it's needed, it needs to have exactly the same functionality as other models so you automatically get the urls like /simplePlugin/:id for find ..etc

Thanks

Share Improve this question asked Jan 13, 2014 at 7:03 iConnoriConnor 20.2k14 gold badges66 silver badges97 bronze badges 4
  • sails.models.yourmodel – NDBoost Commented Jan 31, 2014 at 15:41
  • @gorelative How would sails know about that? to be able to setup the model... – iConnor Commented Feb 6, 2014 at 15:46
  • Are you using v0.9.x or v0.10? How do you implement the plugin api, using a custom hook? – marionebl Commented Feb 7, 2014 at 9:25
  • Did you checkut github.com/balderdashy/waterline – HaNdTriX Commented Feb 7, 2014 at 12:06
Add a comment  | 

5 Answers 5

Reset to default 12 +100

What are you trying to do is not easy and a bit messy with Sails in the current state of the project. I'm referring to the v0.10 version. What you'll have to do is

  1. Inject the model definition found in SimplePlugin.SimpleModel into sails.models
  2. Inject a dummy controller for the model with _config: { rest: true }

Please note that the code examples I posted are taken from a custom Sails hook I am working on and assume access to sails and the code examples to be executed during the loadHooks phase of Sails initialization / before the MiddlewareRegistry phase (compare: lib/app/load.js).

1. Inject model definition

Following the hints in the orm hook in Sails v0.10 you have to:

  • Get the models and adapters defined in api, merge your new model into the dictionary
  • Normalize the model definitions via sails.hooks.orm.normalizeModelDef
  • Load the normalized model definitions into Waterline
  • Unload exisisting adapter connections via teardown
  • Reinitialize Waterline
  • Expose the initialized Waterline collections to sails and the global scope via sails.hooks.orm.prepareModels (previously: sails.hooks.orm.exposeModels, changed with: 8d96895662)

Because you have to reinitialize Waterline and reload all model definitions I'd recommend to collect all model definitions to inject and pass them to the inject function once. The example code below reflects this.

...

function injectPluginModels(pluginModels, cb) {
  // copy sails/lib/hooks/orm/loadUserModules to make it accessible here
  var loadUserModelsAndAdapters = require('./loadUserModules')(sails);

  async.auto({
    // 1. load api/models, api/adapters
    _loadModules: loadUserModelsAndAdapters,

    // 2. Merge additional models,  3. normalize model definitions
    modelDefs: ['_loadModules', function(next){
      _.each(additionModels, function(aditionModel) {
         _.merge(sails.models, additionalModel);
      });

      _.each(sails.models, sails.hooks.orm.normalizeModelDef);
      next(null, sails.models);
    }],

    // 4. Load models into waterline, 5. tear down connections, 6. reinitialize waterline
    instantiatedCollections: ['modelDefs', function(next, stack){
      var modelDefs = stack.modelDefs;

      var waterline = new Waterline();
      _.each(modelDefs, function(modelDef, modelID){
        waterline.loadCollection(Waterline.Collection.extend(modelDef));
      });

      var connections = {};

      _.each(sails.adapters, function(adapter, adapterKey) {
        _.each(sails.config.connections, function(connection, connectionKey) {
          if (adapterKey !== connection.adapter) return;
          connections[connectionKey] = connection;
        });
      });

      var toTearDown = [];

      _.each(connections, function(connection, connectionKey) {
        toTearDown.push({ adapter: connection.adapter, connection: connectionKey });
      });

      async.each(toTearDown, function(tear, callback) {
         sails.adapters[tear.adapter].teardown(tear.connection, callback);
      }, function(){
         waterline.initialize({
           adapters: sails.adapters,
           connections: connections
         }, next)
      });
    }],

    // 7. Expose initialized models to global scope and sails
    _prepareModels: ['instantiatedCollections', sails.hooks.orm.prepareModels]

  }, cb);
};

...

Would allow you to:

// Read your plugins
...

var pluginModels = // Get all the plugin models
injectPluginModels(pluginModels, function(){
  // Plugin models now available via global[pluginModel.globalId] and sails.models[pluginModel.identity]
});

2. Inject controller

For each model that should be exposed via blueprint methods you have to:

  • Create a controller definition with matching identity and enabled blueprints
  • Save controller to sails.controllers[controllerId]
  • Save controller to sails.hooks.controllers.middleware[controllerId]

The Sails MiddlewareRegistry will automatically pick up the controllers found in these objects.

function mountBlueprintsForModels(pluginModels) {
  _.each(pluginModels, function(pluginModel){
    var controller = _.cloneDeep(pluginModel);
    controller._config = { rest: true };

    var controllerId = pluginModel.identity;

    if (!_.isObject(sails.controllers[controllerId])) {
      sails.controllers[controllerId] = controller;
    }

    if (!_.isObject(sails.hooks.controllers.middleware[controllerId])) {
      sails.hooks.controllers.middleware[controllerId] = controller;
    }
  });
}

3. In action

// E.g. in /api/hooks/plugins/index.js
/*
 * Module dependencies
 */

var async = require('async'),
    _ = require('lodash'),
    waterline = require('waterline');

module.exports = function(sails) {

  // injectPluginModels and mountBlueprintsForModels defined here
  ...

  return {

    initialize: function(cb) {
      sails.after('hook:orm:loaded', function() {
        yourNiftyPluginLoader(function(err, plugins) {
          // assuming plugin.models holds array of models for this plugin
          // customize for your use case
          var pluginModels = _.pluck(plugins, 'models');
          injectPluginModels(pluginModels, cb);
          mountBlueprintsForModels(pluginModels);
        });
      });
    }

  }

}

EDIT: not working completely since collections are assigned to connections at initialization.

Seems that there is a better solution, with 3 lines of code and without disconnecting/reconnecting databases. I just studied the source code of Waterline (see https://github.com/balderdashy/waterline/blob/master/lib/waterline.js#L109). It's possible to do something like:

var Waterline = require('waterline');

// Other dependencies
var Schema = require('waterline-schema');
var CollectionLoader = require('waterline/lib/waterline/collection/loader');

var orm = new Waterline();

var config = {
    // Setup Adapters
    // Creates named adapters that have have been required
    adapters: {
        'default': 'mongo',
        mongo: require('sails-mongo')
    },

    // Build Connections Config
    // Setup connections using the named adapter configs
    connections: {
        'default': {
            adapter: 'mongo',
            url: 'mongodb://localhost:27017/sausage'
        }
    }
};

orm.initialize(config, function(err, data) {
    if (err) {
        throw err;
    }

    // ORM initialized, let's add another model dynamically
    var User = Waterline.Collection.extend({
       identity: 'user',
        connection: 'default',

        attributes: {
            first_name: 'string',
            last_name: 'string'
        }
    });
    orm.loadCollection(User);

    var defaults = config.defaults || {};

    // This is where the magic happens
    var loader = new CollectionLoader(User, orm.connections, defaults);
    var collection = loader.initialize(orm);
    orm.collections[collection.identity.toLowerCase()] = collection;

    // Done! You can now use orm.collections.user :-D
});

In v0.12 sails.hooks.orm.normalizeModelDef doesn't exists anymore. Also sails/lib/hooks/orm/loadUserModules went to the sails-hook-orm npm module and is not longer part of sails.

Try this: "Load models, controllers, services, policies and config from specified directories and inject them into the main Sails app."

https://github.com/leeroybrun/sails-util-mvcsloader

surprised sails doesn't support this in 2018: I have continued the above package with a fork ( @eyn answer) with updates that work for sails v1.x.x.

https://github.com/emahuni/sails-util-micro-apps

I changed it to that coz I am changing a lot of thing in that package. Instead of loading just models and controllers i want to it to load whole apps, mini-apps for a micro service architecture in sails. This is such that you can make mini-apps that can be joined together to form one large app out of reusable apis code.

发布评论

评论列表(0)

  1. 暂无评论