C2FO / patio

Idiomatic database toolkit

Home Page:http://c2fo.github.io/patio

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Synchronous add model?

drewhamlett opened this issue · comments

It would be great to have a synchronous add model function. This is something I do on startup(not sure what everyone else does) one time. I can't configure the rest of my application until the add models are done. As of now I'm chaining a couple of callbacks. I could use the sync module for nodejs but I think it makes more sense if it's built in. Thanks!

I agree this would be a great feature!

Right now it does not do this because we load the schema information from the database at the time of model creation which allow us to check values being assigned to as properties, set up table inheritance among models etc.

One way around this could be to delay all schema loading until an action is performed on a model, this drawback is that it will behave differently from the current implementation as property assignment checks will not occur until an action occurs on a model. This may not be a a bad thing.

The pattern that we currently use placing all of our model in a directory like

 - models
    - Model1.js
    - Model2.js
 - index.js

Then in our index file

var path = require("path"),
     comb = require("comb");
var CONNECT_URI = "mysql://test:testpass@localhost:3306/db?minConnections=1&maxConnections=5";
patio.connect(CONNECT_URI);

exports.load = (function(){
    //create a shared promise so we only load the models once, and a loaded var to store if
    //we have called load before
    var loadPromise = new comb.Promise(), loaded = false; 
    return function(cb){       
        if(!loaded){
            //we havent loaded yet so load the models directory at once then call the loadPromise
            patio.import(path.resolve(_dirname, "./models")).then(loadPromise);
            loaded = true;
        }
        if(cb){
             //classic accepts a callback like a normal node callback i.e. function(err, result){};
             loadPromise.classic(cb);
        }
        return loadPromise;
    }
})();

In every other module you can just call it like so

var models = require("my-models"), patio = require("patio");

models.load(function(err){
    if(err){
         console.error("Something went wrong");
         console.error(err.stack);
    }else{
          var Model1 = patio.getModel("model1");
          //and so on
    }
});

I hope this helps!

Hi Doug, thanks for looking into this. This pattern seems good but is it really the best way to handle model loading in a web application? I wouldn't think I need to keep checking if models have been loaded every time they need to be called. I'm more curious then anything.

What I've been doing is:

 var dbConfig = require('./db.json');
 patio.createConnection(dbConfig);
 patio.import(__dirname + "/models").then(function() { //load all models

        // Configuration
        app.configure(function() {
            app.set('views', __dirname + '/views');
            app.set('view options', {
                layout: false
            });
 });

Then in controller

var patio = require('patio');

module.exports = function(app) {

    var User = patio.getModel("user");
    var Post = patio.getModel("post");

    UserController = function() {
        console.log("UserController created");
    };

UserController.prototype.index = function(req, res, next) {

        var id = req.params.id;

        User.findblah.....

    };

  return new UserController();
};

You certainly do not need to keep checking if the models have been loaded every time, the pattern I provided is just one we use. In fact we split our models into their own module so we can use them in whatever package needs them. With a webapp we typically load the models then create the app and assume that if a route is being hit that the model is loaded.

I also wrote a blog post on working with patio models at http://blog.dougamartin.com, which hopefully gives some more insight into why we did it the way we did initially.

Also I thought about the asynchronous loading and how it is a little odd that you cannot export the model from within the JS file, and started some work on it last night and hopefully will have a decent solution in the coming weeks (~2 or so). But essentially I will do a lazy load of the model information whenever an action occurs on a model.

Hey Doug sounds great. I was able to read your blog post last night. As a side note I just want to say I love the dynamic loading of models(I forgot to mention that earlier).

As far as the blog post, I really liked the section on inter-model dependencies, which I was struggling with. I did find examples in the repo of this pattern, which is different then what you explained in the blog.

patio.addModel("post", {

    static: {

        findPostByUserId: function(userId, cb) {

            this.User //this is defined.
        },


        getters: {
            User: function() {
                if (!this.__User) {
                    this.__User = this.patio.getModel("user");
                }
                return this.__User;
            }
        }
    }
});

My other question was why get the patio property of the model using

this.patio.getModel

instead of the global variable

patio.getModel

From my test this does the same thing. Thanks

I found that in the code I was writing I used that pattern i presented in the blog post more often than what I did in the example and it was a little cleaner because I dont have to write a bunch of getter methods inorder to access models.

The patio variable attached to the model is the same thing as requiring patio in your code. You should actually just require patio as it it will be more efficient than looking up the patio member each time you want to reference it.

Since you pointed that out Im going to change the example to use the pattern I used in the blog post as it is the better solution.

I've pushed up some code to my for that is the initial syncmodel work https://github.com/doug-martin/patio/tree/sync_model.

Here is an example of using them.

Assuming there are two table blog and user.

//As of right now you need to connect to the database before you can add models, this may change

var DB = patio.connect("mysql://test:testpass@localhost:3306/sandbox");

You define models just as you would before. Except instead of a promise you get a model back

var User = patio.addModel("user"),
      Blog = patio.addModel("blog");

Before you use the models you need to call the sync method which will sync all schema information. The best way to sync your models is to call patio.syncModels

patio.syncModels().then(function(){
    //user your models
}. function(err){
   console.log(error);
});

You can also just call sync of each model individually also.

User.sync().then(function(){
    //user your models
}. function(err){
   console.log(error);
});

You can check out the code in my repo Im in the process of updating the examples but you should be able to start using it. Let me know what you think.

Wow this is great. Thanks for these changes. Just got around to trying it out. This is much better way of doing it IMO. I'll let you know more after using it a while longer.

Ok v0.0.8 is published and synchronous models are in!

-Doug