Skip to content

Instantly share code, notes, and snippets.

@Morgul
Last active January 6, 2016 16:03
Show Gist options
  • Save Morgul/06a730582ac65579bb38 to your computer and use it in GitHub Desktop.
Save Morgul/06a730582ac65579bb38 to your computer and use it in GitHub Desktop.
TrivialModels API

Proposal for TrivialModels API

We've got a lot of choices for how we implement TrivialModels. This is my attempt to make it through some of them.

ES6 class-based syntax

import { Model, types, drivers } from 'trivialmodels';

class UserModel extends Model
{
    // This is required, or it throws an error.
    get $schema()
    {
        return {
            name: types.String(),
            email: types.String({ validator: this.checkEmail }),
            admin: types.Boolean({ default: false })
        };
    } // end $schema
    
    // Set the database for this model instance
    // This is required, or it throws an error.
    get $db(){ return drivers.trivialdb('users', { pk: 'email' }); }
    
    // Custom property
    get displayName(){ return this.name || this.email; }
    
    // Custom Validation Function
    checkEmail(email)
    {
        var re = /\S+@\S+\.\S+/;
        return re.test(email);
    } // end checkEmail
    
    testFunc()
    {
        console.log('custom function');
    } // end testFunc
} // end UserModel

Technical Details

After a lot of experimentation, it turns out you can access things defined on the prototype (aka, in the class declaration) from inside static functions. In this example, accessing the db from inside a static function would be: this.prototype.$db. That makes the whole thing work.

Benefits

The nice thing about this is that it's intuitive to extend the models, or add logic to them. They're more than just a pure data object, now they contain logic. Your model is all inclusive, from the very get-go.

Also, your database is super decoupled here; it's a model, and as long as what you return from the $db property conforms to the driver API, it doesn't care where it came from.

Drawbacks

The class structure is awkward for this, since in ES6 you can't declare member properties. (The best you get is the ability to set getter/setters, which could be used but are very, very verbose.) So, while this would work, there's unavoidable boilerplate that makes it awkward.

Additionally, this is pretty non-standard for Javascript, and a hige departure from other model systems.

Vue-like declaration syntax

import { Model, types, drivers } from 'trivialmodels';

var UserModel = Model.define({
    name: 'UserModel',
    driver: drivers.trivialdb({ database: 'some-namespace:users', pk: 'name' }),
    schema: {
        name: types.String(),
        email: types.String({ validator: this.checkEmail }),
        admin: types.Boolean({ default: false }),
        
        // Properties
        get displayName(){ return this.name || this.email; }
        
        // Functions
        testFunc: function()
        {
            console.log('custom function');
        },
        checkEmail: function(email)
        {
            var re = /\S+@\S+\.\S+/;
            return re.test(email);
        }
    }
});

Technical Details

This is, esentially calling a factory function to get back an instantiable mode class. (See this for how to build dynamically names functions, so we can name the model correctly.) This will basically be a custom model instance that is named correctly.

Benefits

This is a much simpler, and in some ways more elegant way to specify everything we want to be in the generated model. It's an easily extended format, and attempts to maintain all the benefit from the class based method. Additionally, it removes boilerplate, making the definition of the model very easy to read.

A second benefit, which can't be overstated is the fact that the contract between definition and resulting model is explicit. You know exactly what will be done by TrivialModels for any given structure, whereas with a class based approach, you're building a class from scratch, which could introduce subtle behavioral differences, or at the very least is much less easy to intuit about.

Drawbacks

It's not using classes, so visually it does not conform to the same patterns as classes. You also have to know what sections of the object are supported; if you, say, want to add a method to the model, you must know to add it under methods. The format is, by necessity arbitrary and proprietary; there is no standard to learn.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment