cormo
Advanced tools
Comparing version 0.6.3 to 0.6.4
The list of terms used in this project. | ||
Table | ||
----- | ||
Other candidates: | ||
* collection | ||
Record | ||
@@ -11,2 +18,3 @@ ------ | ||
* tuple | ||
* document | ||
@@ -13,0 +21,0 @@ Column |
@@ -29,2 +29,6 @@ // Generated by CoffeeScript 1.6.2 | ||
ModelQuery.query = function() { | ||
return new Query(this); | ||
}; | ||
ModelQuery.find = function(id, callback) { | ||
@@ -31,0 +35,0 @@ return this._createQueryAndRun('find', id, 'exec', callback); |
{ | ||
"name": "cormo", | ||
"description": "ORM framework for Node.js", | ||
"version": "0.6.3", | ||
"version": "0.6.4", | ||
"keywords": [ | ||
@@ -17,2 +17,3 @@ "orm", | ||
"author": "Sangmin Yoon <sangmin.yoon@croquis.com>", | ||
"license": "MIT", | ||
"bin": { | ||
@@ -19,0 +20,0 @@ "cormo": "./bin/cormo" |
404
README.md
@@ -7,3 +7,3 @@ # About | ||
* multi-DB: MySQL, MongoDB, SQLite3, PostgreSQL, Redis | ||
* multi-DB: MySQL, MongoDB, SQLite3, PostgreSQL | ||
* constraints | ||
@@ -13,272 +13,117 @@ * validations | ||
* geospatial query | ||
* callbacks | ||
* aggregation | ||
* nested column | ||
Will support: | ||
See https://github.com/croquiscom/cormo/wiki/Future-Plans for future plans. | ||
* auto table migration | ||
* more DB, validations, associations, ... | ||
# Overview | ||
This project is not yet stabilized. | ||
API can be changed. | ||
You can see detail guides on http://croquiscom.github.io/cormo/. | ||
# Usage | ||
## Connect to DB | ||
## Define models | ||
```coffeescript | ||
Connection = require('cormo').Connection | ||
cormo = require 'cormo' | ||
connection = new cormo.Connection 'mysql', database: 'test' | ||
connection = new Connection 'mysql', database: 'test' | ||
# this will create two tables - users, posts - in the database. | ||
another = new Connection 'mongodb', database: 'test' | ||
``` | ||
class User extends cormo.Model | ||
@column 'name', type: String | ||
@column 'age', type: cormo.types.Integer | ||
See documents for each adapter([[#AdapterBase]]) for detail options. | ||
## Define models | ||
```coffeescript | ||
# this will create two tables - users, posts. | ||
# using model method | ||
User = connection.model 'User', | ||
name: { type: String } | ||
age: { type: Number } | ||
# using CoffeeScript extends keyword | ||
class Post extends cormo.Model | ||
@connection connection # if omitted, Connection.defaultConnection will be used instead | ||
@column 'title', String # 'String' is the same as '{ type: String }' | ||
@column 'body', 'string' # you can also use 'string' to specify a string type | ||
# apply defined models to Database. This can be omitted | ||
connection.applySchemas (error) -> | ||
console.log error | ||
@column 'title', String # `String` is the same as `type: String` | ||
@column 'body', 'string' # you can also use `string` to specify a string type | ||
@column 'date', Date | ||
``` | ||
You can use any of cormo.types.String, 'string', or String | ||
(native JavaScript Function, if exists) to specify a type. | ||
## Create records | ||
Currently supported types: | ||
* String | ||
* Number | ||
* Integer | ||
* Date | ||
* GeoPoint (MySQL, MonogoDB only) | ||
See [[#Connection::model]], [[#Connection::applySchemas]] for more details. | ||
## Create a record | ||
```coffeescript | ||
# new with no data | ||
user1 = new User() | ||
user1.name = 'John Doe' | ||
user1.age = 27 | ||
user1 = new User name: 'John Doe', age: 27 | ||
user1.save (error) -> | ||
console.log error | ||
# new with data | ||
user2 = new User name: 'John Doe', age: 27 | ||
user2.save (error) -> | ||
User.create name: 'John Doe', age: 27, (error, user2) -> | ||
console.log error | ||
# build is the same as new | ||
user3 = User.build name: 'John Doe', age: 27 | ||
user3.save (error) -> | ||
console.log error | ||
# create is the same as build and save | ||
User.create { name: 'John Doe', age: 27 }, (error, user) -> | ||
console.log error | ||
``` | ||
See [[#Model::constructor]], [[#ModelPersistence::save]], [[#Model.build]], [[#ModelPersistence.create]] for more details. | ||
## Retreive records | ||
## Query | ||
```coffeescript | ||
# simple query | ||
# the same as "SELECT * FROM users WHERE age=27" | ||
User.where age: 27, (error, users) -> | ||
console.log users | ||
User.find 1, (error, user) -> | ||
# else you can use query chain | ||
User.where(age: 27).exec (error, users) -> | ||
console.log users | ||
User.where name: 'John Doe', (error, users) -> | ||
# select partial columns | ||
# the same as "SELECT name,age FROM users" | ||
User.select 'name age', (error, users) -> | ||
console.log users | ||
User.where(age: 27) | ||
.select('name') | ||
.order('name') | ||
.limit(5) | ||
.skip(100) | ||
.exec (error, users) -> | ||
# sort result | ||
# the same as "SELECT * FROM users ORDER BY age ASC, name DESC" | ||
User.order 'age -name', (error, users) -> | ||
console.log users | ||
User.where($or: [ { age: { $lt: 20 } }, { age: { $gt: 60 } } ]) | ||
.where(name: { $contains: 'smi' }) | ||
.exec (error, users) -> | ||
``` | ||
# two condition | ||
# the same as "SELECT * FROM users WHERE name='John Doe' AND age=27" | ||
User.where name: 'John Doe', age: 27, (error, users) -> | ||
console.log users | ||
## Count records | ||
# using query chain | ||
User.where(name: 'John Doe').where(age: 27).exec (error, users) -> | ||
console.log users | ||
# find by id, return a single record | ||
User.find 1, (error, user) -> | ||
console.log user | ||
# find multiple ids | ||
# will return error unless records are found for all ids | ||
User.find [1,2,3], (error, users) -> | ||
console.log users | ||
# find multiple ids with same order | ||
User.findPreserve [2,1,2,3], (error, users) -> | ||
# users[0].id is 2 and users[1].id is 1 and users[2].id is 2 and users[3].id is 3 | ||
console.log users | ||
# get count of all records | ||
# the same as "SELECT COUNT(*) FROM users" | ||
```coffeescript | ||
User.count (error, count) -> | ||
console.log count | ||
# get count of matched records | ||
# the same as "SELECT COUNT(*) FROM users WHERE age=27" | ||
User.count age: 27, (error, count) -> | ||
console.log count | ||
``` | ||
# update records that match conditions | ||
# the same as "UPDATE users SET age=10 WHERE age=27" | ||
User.update { age: 10 }, age: 27, (error, count) -> | ||
console.log count | ||
## Update records | ||
# using query chain | ||
User.where(age: 27).update age:10, (error, count) -> | ||
console.log count | ||
```coffeescript | ||
User.find 1, (error, user) -> | ||
user.age = 30 | ||
user.save (error) -> | ||
# delete records that match conditions | ||
# the same as "DELETE FROM users WHERE age=27" | ||
User.delete age: 27, (error, count) -> | ||
console.log count | ||
User.find(1).update age: 10, (error, count) -> | ||
# or condition, similar syntax with MongoDB | ||
# the same as "SELECT * FROM users WHERE name='John Doe' OR age=27" | ||
User.where $or: [ { name: 'John Doe' }, { age: 27 } ], (error, users) -> | ||
console.log users | ||
User.where(age: 27).update age:10, (error, count) -> | ||
``` | ||
# comparison | ||
# the same as "SELECT * FROM users WHERE age > 30 AND age <= 45" | ||
User.where [ { age: { $gt: 30 } }, { age: { $lte: 45 } } ], (error, users) -> | ||
console.log users | ||
## Delete records | ||
# containing some text in case insensitive | ||
# the same as "SELECT * FROM users WHERE name LIKE '%smi%'" | ||
User.where { name: { $contains: 'smi' } }, (error, users) -> | ||
console.log users | ||
# subset | ||
# the same as "SELECT * FROM users WHERE age IN (10,20,30)" | ||
User.where { age: { $in: [ 10, 20, 30 ] } }, (error, users) -> | ||
console.log users | ||
# you can omit the $in keyword (implicit $in) | ||
User.where { age: [ 10, 20, 30 ] }, (error, users) -> | ||
console.log users | ||
# limit records | ||
# the same as "SELECT * FROM users WHERE age<40 LIMIT 3" | ||
User.where(age: { $lt: 40 }).limit(3).exec (error, users) -> | ||
console.log users | ||
```coffeescript | ||
User.delete age: 27, (error, count) -> | ||
``` | ||
See [[#ModelQuery]], [[#Query]] for more details. | ||
## Constraint | ||
## Constraints | ||
Currently supports 'unique' and 'required'(not null). | ||
'unique' is supported on the database layer. | ||
'required' is supported on the CORMO layer(while validating). | ||
The column unique but not required can have multiple null values. | ||
```coffeescript | ||
User = connection.model 'User', | ||
name: { type: String, required: true } | ||
age: { type: Number, required: true } | ||
email: { type: String, unique: true, required: true } | ||
User.create { name: 'Bill Smith', age: 45, email: 'bill@foo.org' }, (error, user1) -> | ||
User.create { name: 'Bill Simpson', age: 38, email: 'bill@foo.org' }, (error, user2) -> | ||
# error.message will be 'duplicated email' or 'duplicated' | ||
class User extends cormo.Model | ||
@column 'name', type: String, required: true | ||
@column 'age', type: Number, required: true | ||
@column 'email', type: String, unique: true, required: true | ||
``` | ||
See [[#Connection::model]] for more details. | ||
## Validation | ||
## Validations | ||
If you want validations, adds a validator using [[#ModelValidate.addValidator]]. | ||
If a record is invalid, throws an error, or returns false or an error string in the validator. | ||
```coffeescript | ||
User = models.User = connection.model 'User', | ||
name: String | ||
age: Number | ||
email: String | ||
class User extends cormo.Model | ||
@column 'name', String | ||
@column 'age', Number | ||
@column 'email', String | ||
# checkes age validity | ||
User.addValidator (record) -> | ||
if record.age < 18 | ||
return 'too young' | ||
# checkes email validity | ||
User.addValidator (record) -> | ||
if record.email and not /^\w+@.+$/.test record.email | ||
throw new Error 'invalid email' | ||
return true | ||
User.create { name: 'John Doe', age: 10, email: 'invalid' }, (error, user) -> | ||
# error.message will be 'invalid email,too young' or 'too young,invalid email' | ||
@addValidator (record) -> | ||
if record.age < 18 | ||
return 'too young' | ||
``` | ||
See [[#ModelValidate.addValidator]] for more details. | ||
## Callbacks | ||
CORMO supports following callbacks: | ||
* afterInitialize | ||
* afterFind | ||
* beforeValidate | ||
* afterValidate | ||
* beforeSave | ||
* afterSave | ||
* beforeCreate | ||
* afterCreate | ||
* beforeUpdate | ||
* afterUpdate | ||
* beforeDestroy | ||
* afterDestroy | ||
```coffeescript | ||
# using method name | ||
User.afterInitialize 'onAfterInitialie' | ||
User::onAfterInitialie = -> | ||
console.log 'initialized' | ||
class User extends cormo.Model | ||
@column 'name', String | ||
@column 'age', Number | ||
# using anonymous function | ||
User.afterCreate -> | ||
console.log 'created' | ||
# while defining a model | ||
class Post extends cormo.Model | ||
@column 'title', String # 'String' is the same as '{ type: String }' | ||
@column 'body', 'string' # you can also use 'string' to specify a string type | ||
@afterSave 'onAfterSave' | ||
onAfterSave: -> | ||
console.log 'saved' | ||
@beforeSave -> | ||
@name = @name.trim() | ||
``` | ||
@@ -289,75 +134,19 @@ | ||
```coffeescript | ||
User = connection.model 'User', | ||
name: String | ||
age: Number | ||
class User extends cormo.Model | ||
@column 'name', String | ||
@column 'age', Number | ||
@hasMany 'posts' | ||
Post = connection.model 'Post', | ||
title: String | ||
body: String | ||
# one-to-many association | ||
# this will add 'user_id' to the Post model | ||
User.hasMany Post | ||
Post.belongsTo User | ||
# one-to-many association with 'as' | ||
# this will add 'parent_post_id' to the Post model | ||
Post.hasMany Post, as: 'comments', foreign_key: 'parent_post_id' | ||
Post.belongsTo Post, as: 'parent_post' | ||
# get associated objects | ||
user.posts (error, records) -> | ||
console.log records | ||
post.user (error, record) -> | ||
console.log record | ||
post.comments (error, records) -> | ||
console.log records | ||
post.parent_post (error, record) -> | ||
console.log record | ||
# returned objects are cached, give true to reload | ||
user.posts true, (error, records) -> | ||
console.log records | ||
# two ways to create an associated object | ||
Post.create { title: 'first post', body: 'This is the 1st post.', user_id: user.id }, (error, post) -> | ||
console.log post | ||
post = user.posts.build title: 'first post', body: 'This is the 1st post.' | ||
post.save (error) -> | ||
console.log error | ||
class Post extends cormo.Model | ||
@column 'title', String | ||
@column 'body', String | ||
@belongsTo 'user' | ||
``` | ||
See [[#Model.hasMany]], [[#Model.belongsTo]] for more details. | ||
### keep data consistent | ||
CORMO supports foreign key constraints by DBMS for SQL-based DBMS or by framework for MongoDB. | ||
(CORMO does not guarantee integrity for MongoDB even if using this feature) | ||
To use constraints, give an integrity options on [[#Model.hasMany]]. | ||
```coffeescript | ||
# the same as "CREATE TABLE posts ( user_id INT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE SET NULL" | ||
User.hasMany Post, integrity: 'nullify' | ||
# the same as "CREATE TABLE posts ( user_id INT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE RESTRICT" | ||
User.hasMany Post, integrity: 'restrict' | ||
# the same as "CREATE TABLE posts ( user_id INT, FOREIGN KEY (user_id) REFERENCES users(id) ON DELETE CASCADE" | ||
User.hasMany Post, integrity: 'delete' | ||
# no option means no foreign key constraint | ||
# so there may be a post with invalid user_id | ||
User.hasMany Post | ||
``` | ||
## Geospatial query | ||
Currently, we supports only near query of 2D location in MongoDB and MySQL. | ||
```coffeescript | ||
Place = connection.model 'Place', | ||
name: String | ||
location: cormo.types.GeoPoint | ||
class Place extends cormo.Model | ||
@column 'name', String | ||
@column 'location', cormo.types.GeoPoint | ||
@@ -368,31 +157,28 @@ # create | ||
# query | ||
Place.where().near(location: [-5, 45]).limit(4).exec (error, places) -> | ||
Place.query().near(location: [-5, 45]).limit(4).exec (error, places) -> | ||
console.log places | ||
``` | ||
See [[#Query::near]] for more details. | ||
# License | ||
## Other features | ||
The MIT License (MIT) | ||
Use [[#ModelTimestamp.timestamps]] to add created_at and updated_at to the table. | ||
Copyright (c) 2012-2013 Sangmin Yoon <sangmin.yoon@croquis.com> | ||
```coffeescript | ||
User.timestamps() | ||
``` | ||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
Use [[#ModelPersistence.createBulk]] to create many records at once. | ||
The above copyright notice and this permission notice shall be included in | ||
all copies or substantial portions of the Software. | ||
```coffeescript | ||
User.createBulk [ { name: 'John Doe', age: 27 }, { name: 'Bill Smith', age: 45 } ], (error, users) -> | ||
console.log users | ||
``` | ||
If [[#Model.archive]] is true, deleted records are archived in the _Archive table. | ||
```coffeescript | ||
User.archive = true | ||
User.delete age: 27, (error, count) -> | ||
# _Archive will have delete records as | ||
# { model: 'User', data: <deleted user 1> }, | ||
# { model: 'User', data: <deleted user 2> }, ... | ||
``` | ||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | ||
THE SOFTWARE. |
spdx disjunction for an artifact's license information
Licensespdx disjunction for an artifact's license information
Found 1 instance in 1 package
spdx disjunction for an artifact's license information
Licensespdx disjunction for an artifact's license information
Found 1 instance in 1 package
No License Found
License(Experimental) License information could not be found
Found 1 instance in 1 package
231714
47
1
5506
181