Model Adapters: a pull request so we can discuss model adapters and their implementation. #78

Merged
merged 16 commits into from May 1, 2012

Conversation

Projects
None yet
4 participants
@Techwraith
Contributor

Techwraith commented Feb 17, 2012

Model-Adapters act as the mediator between your data-store and your models. Geddy will eventually have some default adapters for developers to use, but for now they'll have to make their own. The purpose of this branch is to make that a little easier for them.

I'd like to have model-adapters work in a way that falls back to defaults:

  • First, it will assign the default model adapter (as defined in the environment.js file) to all models
  • Then, it will look through each model and see if they've defined an adapter property. If they have, they'll use that model adapter.
  • Lastly, it will look at the app/models/adapters directory and assign model-adapters to any models that have corresponding adapters.
@mde

This comment has been minimized.

Show comment Hide comment
@mde

mde Feb 17, 2012

Contributor

modelAdapter is redundant -- 'adapter' is just fine. There's a bit of prior art in the old models that actually should be useful defining the API let's go over that old code before we do more.

Contributor

mde commented Feb 17, 2012

modelAdapter is redundant -- 'adapter' is just fine. There's a bit of prior art in the old models that actually should be useful defining the API let's go over that old code before we do more.

@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith Feb 17, 2012

Contributor

Good call, I'll take a look at how geddy used to handle this and we'll talk.

Contributor

Techwraith commented Feb 17, 2012

Good call, I'll take a look at how geddy used to handle this and we'll talk.

@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith Mar 30, 2012

Contributor

Model Adapter API

.load()

geddy.model.ModelName.load(id || query, opts, callback)

Loads a specific instance from the DB. If an object is passed, geddy assumes that you've passed it a query and will return the first result it finds from the DB. If you pass it something that looks like an id it will pull that instance from the DB.

.all()

geddy.model.ModelName.all(query, opts, callback)

Loads a specific set of instances from the DB. If no query is provided, this is load all instances in the db.

.remove()

geddy.model.ModelName.remove(id, opts, callback)

Removes a specific instance from the DB

.save()

geddy.model.ModelName.save(instance, opts, callback)

Saves an instance to the DB.

Optional methods

// distinct
geddy.model.ModelName.distinct(query, opts, callback)

// count
geddy.model.ModelName.count(query, opts, callback)
Contributor

Techwraith commented Mar 30, 2012

Model Adapter API

.load()

geddy.model.ModelName.load(id || query, opts, callback)

Loads a specific instance from the DB. If an object is passed, geddy assumes that you've passed it a query and will return the first result it finds from the DB. If you pass it something that looks like an id it will pull that instance from the DB.

.all()

geddy.model.ModelName.all(query, opts, callback)

Loads a specific set of instances from the DB. If no query is provided, this is load all instances in the db.

.remove()

geddy.model.ModelName.remove(id, opts, callback)

Removes a specific instance from the DB

.save()

geddy.model.ModelName.save(instance, opts, callback)

Saves an instance to the DB.

Optional methods

// distinct
geddy.model.ModelName.distinct(query, opts, callback)

// count
geddy.model.ModelName.count(query, opts, callback)
@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith Mar 30, 2012

Contributor

Query API

Standard Load by Property

You can load any instance by any property of an instance. Given an instance with these properties:

this.defineProperties({
  id: {type: 'string'}
, name: {type: 'string'}
, band: {type: 'string'}
, age: {type: 'number'}
})

You can load all instances that match a given property:

geddy.model.ModelName.all({band: "Rush"}, callback)

Or you can load a specific instance:

geddy.model.ModelName.load({name: "Geddy Lee"}, callback)

Advanced Queries

You can also perform some advanced queries:

// Greater than
geddy.model.ModelName.all({age: {"$gt": 30}}, callback)

// Less than
geddy.model.ModelName.all({age: {"$lt": 30}}, callback)

// Not equal
geddy.model.ModelName.all({age: {"$ne": 30}}, callback)

// In array
geddy.model.ModelName.all({age: {"$in": [2112, 21, 58]}}, callback)

// and a lot more... not in, exists, etc.

You can combine these with standard queries too if you'd like:

// Return all the instances where the 
// band == "Rush" and the age is greater than 30
geddy.model.ModelName.all({band: "Rush", age: {"$gt": 30}}, callback)

Sorts and Limits

You can sort and limit things by passing them into the optional options object:

// Sort
// note 1 mean ascending, -1 means descending 
geddy.model.ModelName.all({band: "Rush"}, {sort: {age: 1}}, callback)

// Limit
geddy.model.ModelName.all({band: "Rush"}, {limit: 10}, callback)
Contributor

Techwraith commented Mar 30, 2012

Query API

Standard Load by Property

You can load any instance by any property of an instance. Given an instance with these properties:

this.defineProperties({
  id: {type: 'string'}
, name: {type: 'string'}
, band: {type: 'string'}
, age: {type: 'number'}
})

You can load all instances that match a given property:

geddy.model.ModelName.all({band: "Rush"}, callback)

Or you can load a specific instance:

geddy.model.ModelName.load({name: "Geddy Lee"}, callback)

Advanced Queries

You can also perform some advanced queries:

// Greater than
geddy.model.ModelName.all({age: {"$gt": 30}}, callback)

// Less than
geddy.model.ModelName.all({age: {"$lt": 30}}, callback)

// Not equal
geddy.model.ModelName.all({age: {"$ne": 30}}, callback)

// In array
geddy.model.ModelName.all({age: {"$in": [2112, 21, 58]}}, callback)

// and a lot more... not in, exists, etc.

You can combine these with standard queries too if you'd like:

// Return all the instances where the 
// band == "Rush" and the age is greater than 30
geddy.model.ModelName.all({band: "Rush", age: {"$gt": 30}}, callback)

Sorts and Limits

You can sort and limit things by passing them into the optional options object:

// Sort
// note 1 mean ascending, -1 means descending 
geddy.model.ModelName.all({band: "Rush"}, {sort: {age: 1}}, callback)

// Limit
geddy.model.ModelName.all({band: "Rush"}, {limit: 10}, callback)
@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith Mar 30, 2012

Contributor

Model Relationships

Models can be defined in relation to one another. To illustrate how to do this, here are two models:

var User = function () {

  this.defineProperties({
    name:      {type: 'string', required: true}
  , password:  {type: 'string', required: true}
  , id:        {type: 'string', required: true}
  });

  this.hasMany('Todos');

};

User = geddy.model.register('User', User);
var Todo = function () {

  this.defineProperties({
    title:    {type: 'string', required: true}
  , status:   {type: 'string', required: true}
  , id:       {type: 'string', required: true}
  });

  this.belongsTo('User');

};

Todo = geddy.model.register('Todo', Todo);

Notice that the User now has many Todos and the Todo now belongs to a User. You can now query them just like you could any model:

// get all of a user's todos
user.todos(callback)

// get a subset of a user's todos
user.todos({status: 'done'}, callback)

// limit the number of todos returned
user.todos({status: 'done'}, {limit: 10}, callback

// sort the results
user.todos({status: 'done'}, {limit: 10, sort: {title: 1}}, callback)

// get the todo's user
todo.user(callback)
Contributor

Techwraith commented Mar 30, 2012

Model Relationships

Models can be defined in relation to one another. To illustrate how to do this, here are two models:

var User = function () {

  this.defineProperties({
    name:      {type: 'string', required: true}
  , password:  {type: 'string', required: true}
  , id:        {type: 'string', required: true}
  });

  this.hasMany('Todos');

};

User = geddy.model.register('User', User);
var Todo = function () {

  this.defineProperties({
    title:    {type: 'string', required: true}
  , status:   {type: 'string', required: true}
  , id:       {type: 'string', required: true}
  });

  this.belongsTo('User');

};

Todo = geddy.model.register('Todo', Todo);

Notice that the User now has many Todos and the Todo now belongs to a User. You can now query them just like you could any model:

// get all of a user's todos
user.todos(callback)

// get a subset of a user's todos
user.todos({status: 'done'}, callback)

// limit the number of todos returned
user.todos({status: 'done'}, {limit: 10}, callback

// sort the results
user.todos({status: 'done'}, {limit: 10, sort: {title: 1}}, callback)

// get the todo's user
todo.user(callback)
@MichaelMartinez

This comment has been minimized.

Show comment Hide comment
@MichaelMartinez

MichaelMartinez Apr 5, 2012

Is this ready now? ...Geddy Needs this!!!

Is this ready now? ...Geddy Needs this!!!

@mde

This comment has been minimized.

Show comment Hide comment
@mde

mde Apr 5, 2012

Contributor

We had some good discussion about final details of the API during JSConf. I think @Techwraith is going to land a first cut at the Mongo stuff in the next week or so.

Contributor

mde commented Apr 5, 2012

We had some good discussion about final details of the API during JSConf. I think @Techwraith is going to land a first cut at the Mongo stuff in the next week or so.

@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith Apr 8, 2012

Contributor

The first pass at a universal mongo adapter is up. Queries aren't done yet, and I still don't have the relational stuff done yet, but we're getting there!

Contributor

Techwraith commented Apr 8, 2012

The first pass at a universal mongo adapter is up. Queries aren't done yet, and I still don't have the relational stuff done yet, but we're getting there!

@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith Apr 8, 2012

Contributor

Queries work now. Next up is relations.

Contributor

Techwraith commented Apr 8, 2012

Queries work now. Next up is relations.

@mde

This comment has been minimized.

Show comment Hide comment
@mde

mde Apr 9, 2012

Contributor

I'm going over the changes now. This looks awesome.

Contributor

mde commented Apr 9, 2012

I'm going over the changes now. This looks awesome.

@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith Apr 16, 2012

Contributor

Thought I'd post a quick update.

This is almost ready to merge in. I've just got to implement the model relations. I'll try to get it done this weekend.

Contributor

Techwraith commented Apr 16, 2012

Thought I'd post a quick update.

This is almost ready to merge in. I've just got to implement the model relations. I'll try to get it done this weekend.

@erikbigelow

This comment has been minimized.

Show comment Hide comment
@erikbigelow

erikbigelow Apr 17, 2012

Thanks for this! I love the direction geddy is going!

Thanks for this! I love the direction geddy is going!

@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith Apr 24, 2012

Contributor

I had some flex time today to work on this. The relations work as they should now.

Things left to do (some of this can wait until after this feature lands):

  • create a simple filesystem store adapter (might just use DirtyDB) and make it the default for development
  • Make the scaffolding much more robust to work with the new model stuff
  • Migrations?

@mde what do you think?

Contributor

Techwraith commented Apr 24, 2012

I had some flex time today to work on this. The relations work as they should now.

Things left to do (some of this can wait until after this feature lands):

  • create a simple filesystem store adapter (might just use DirtyDB) and make it the default for development
  • Make the scaffolding much more robust to work with the new model stuff
  • Migrations?

@mde what do you think?

@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith May 1, 2012

Contributor

@mde can we merge this in yet?

Contributor

Techwraith commented May 1, 2012

@mde can we merge this in yet?

@mde

This comment has been minimized.

Show comment Hide comment
@mde

mde May 1, 2012

Contributor

Sure. We can always take a pass through after the fact and clean up here or there if needed.

Contributor

mde commented May 1, 2012

Sure. We can always take a pass through after the fact and clean up here or there if needed.

@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith May 1, 2012

Contributor

Should we bump the version up to 0.4.x? I don't think there are any breaking API changes, but it might be the safest way to go. At the very least it might alleviate some confusion.

Contributor

Techwraith commented May 1, 2012

Should we bump the version up to 0.4.x? I don't think there are any breaking API changes, but it might be the safest way to go. At the very least it might alleviate some confusion.

@mde

This comment has been minimized.

Show comment Hide comment
@mde

mde May 1, 2012

Contributor

Doooooo eeeeet!

Contributor

mde commented May 1, 2012

Doooooo eeeeet!

Techwraith added a commit that referenced this pull request May 1, 2012

Merge pull request #78 from mde/model-adapters
Model Adapters: they work now.

@Techwraith Techwraith merged commit 441ca10 into master May 1, 2012

@Techwraith

This comment has been minimized.

Show comment Hide comment
@Techwraith

Techwraith May 1, 2012

Contributor

@mde Go ahead and push this up to NPM :)

Contributor

Techwraith commented May 1, 2012

@mde Go ahead and push this up to NPM :)

@mde

This comment has been minimized.

Show comment Hide comment
@mde

mde May 2, 2012

Contributor

Done.

Contributor

mde commented May 2, 2012

Done.

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