Comparing version 2.0.3 to 2.0.4
@@ -0,1 +1,16 @@ | ||
### v2.0.4 - 7 Mar 2013 | ||
- Changes db.load() to behave like builtin require() | ||
- Moves hook beforeSave to before checking validations (#66) | ||
- Changes postgres driver to support ssl flag and pass it to pg driver | ||
- Adds possibility to add order to hasMany getAccessor (#58) | ||
- Fixes hasOne reversed associations not having setAccessor | ||
- Adds db.ping() (#57) | ||
- Changes db.load to avoid throwing and just create the error | ||
- Added "afterRemove" hook | ||
- Added "afterCreate" hook | ||
- Support Model.find({ prop: null }) (closes #59) | ||
- Adds LIKE operator | ||
- Many bug fixes | ||
### v2.0.3 - 26 Feb 2013 | ||
@@ -2,0 +17,0 @@ |
var orm = require("../lib/ORM"); | ||
// orm.connect("redshift://dresende@localhost:/orm_test", function (err, db) { | ||
orm.connect("pg://dresende@localhost:/orm_test?ssl=yes", function (err, db) { | ||
// orm.connect("mysql://root:tedua@localhost/orm?pool=true", function (err, db) { | ||
orm.connect("mongodb:///test", function (err, db) { | ||
// orm.connect("mongodb:///test", function (err, db) { | ||
// orm.connect("sqlite://", function (err, db) { | ||
@@ -12,3 +12,6 @@ if (err) { | ||
db.load(__dirname + "/test-model-person", function (err) { | ||
console.log(db.driver.db);process.exit(1); | ||
db.load("./models/test-model-person", function (err) { | ||
if (err) { | ||
@@ -22,4 +25,11 @@ console.log("LOAD ERROR", err); | ||
Person.find(function (err, people) { | ||
console.log(err, people[0].PESSOA); | ||
Pet.get(3, function (err, Deco) { | ||
Person.find({ name: orm.like("_ohn") }, function (err, people) { | ||
console.log(err, people[0].PESSOA); | ||
console.log(people[0]); | ||
people[0].setChildren(Deco, function (err) { | ||
console.log("done!"); | ||
console.log(err); | ||
}); | ||
}); | ||
}); | ||
@@ -26,0 +36,0 @@ |
@@ -139,2 +139,3 @@ var InstanceConstructor = require("../Instance").Instance; | ||
var limit; | ||
var order = null; | ||
var cb = null; | ||
@@ -151,3 +152,8 @@ | ||
} else { | ||
options = arguments[i]; | ||
if (Array.isArray(arguments[i])) { | ||
order = arguments[i]; | ||
order[0] = association.model.table + "." + order[0]; | ||
} else { | ||
options = arguments[i]; | ||
} | ||
} | ||
@@ -172,2 +178,5 @@ break; | ||
}; | ||
if (order !== null) { | ||
options.order = order; | ||
} | ||
@@ -272,3 +281,3 @@ if (conditions === null) { | ||
Driver.insert(association.mergeTable, data, function (err) { | ||
Driver.insert(association.mergeTable, data, null, function (err) { | ||
if (err) { | ||
@@ -275,0 +284,0 @@ return cb(err); |
@@ -105,5 +105,15 @@ var Settings = require("../Settings"); | ||
}); | ||
if (!association.reversed) { | ||
Object.defineProperty(Instance, association.setAccessor, { | ||
value: function (OtherInstance, cb) { | ||
Object.defineProperty(Instance, association.setAccessor, { | ||
value: function (OtherInstance, cb) { | ||
if (association.reversed) { | ||
Instance.save(function (err) { | ||
if (err) { | ||
return cb(err); | ||
} | ||
OtherInstance[association.field] = Instance[Model.id]; | ||
return OtherInstance.save(cb); | ||
}); | ||
} else { | ||
OtherInstance.save(function (err) { | ||
@@ -114,11 +124,13 @@ if (err) { | ||
Instance[association.field] = OtherInstance.id; | ||
Instance[association.field] = OtherInstance[association.model.id]; | ||
return Instance.save(cb); | ||
}); | ||
} | ||
return this; | ||
}, | ||
enumerable: false | ||
}); | ||
return this; | ||
}, | ||
enumerable: false | ||
}); | ||
if (!association.reversed) { | ||
Object.defineProperty(Instance, association.delAccessor, { | ||
@@ -125,0 +137,0 @@ value: function (cb) { |
exports.drop = function (driver, opts, cb) { | ||
var queries = [], pending; | ||
var i, queries = [], pending; | ||
@@ -4,0 +4,0 @@ queries.push("DROP TABLE IF EXISTS " + driver.escapeId(opts.table)); |
exports.drop = function (driver, opts, cb) { | ||
var queries = [], pending; | ||
var i, queries = [], pending; | ||
@@ -4,0 +4,0 @@ queries.push("DROP TABLE IF EXISTS " + driver.escapeId(opts.table)); |
exports.drop = function (driver, opts, cb) { | ||
var queries = [], pending; | ||
var i, queries = [], pending; | ||
@@ -4,0 +4,0 @@ queries.push("DROP TABLE IF EXISTS " + driver.escapeId(opts.table)); |
exports.drop = function (driver, opts, cb) { | ||
var queries = [], pending; | ||
var i, queries = [], pending; | ||
@@ -4,0 +4,0 @@ queries.push("DROP TABLE IF EXISTS " + driver.escapeId(opts.table)); |
@@ -38,2 +38,7 @@ var mysql = require("mysql"); | ||
Driver.prototype.ping = function (cb) { | ||
this.db.ping(cb); | ||
return this; | ||
}; | ||
Driver.prototype.on = function (ev, cb) { | ||
@@ -121,3 +126,3 @@ if (ev == "error") { | ||
Driver.prototype.insert = function (table, data, cb) { | ||
Driver.prototype.insert = function (table, data, id_prop, cb) { | ||
this.QueryInsert | ||
@@ -124,0 +129,0 @@ .clear() |
@@ -8,3 +8,10 @@ var postgres = require("pg"); | ||
this.opts = opts || {}; | ||
this.db = (connection ? connection : new postgres.Client(config.href || config)); | ||
if (connection) { | ||
this.db = connection; | ||
} else if (config.query && config.query.ssl) { | ||
config.ssl = true; | ||
this.db = new postgres.Client(config); | ||
} else { | ||
this.db = new postgres.Client(config.href || config); | ||
} | ||
@@ -30,2 +37,9 @@ var escapes = { | ||
Driver.prototype.ping = function (cb) { | ||
this.db.query("SELECT * FROM pg_stat_activity LIMIT 1", function () { | ||
return cb(); | ||
}); | ||
return this; | ||
}; | ||
Driver.prototype.on = function (ev, cb) { | ||
@@ -91,3 +105,3 @@ if (ev == "error") { | ||
Driver.prototype.insert = function (table, data, cb) { | ||
Driver.prototype.insert = function (table, data, id_prop, cb) { | ||
this.QueryInsert | ||
@@ -107,3 +121,3 @@ .clear() | ||
return cb(null, { | ||
id: result.rows[0].id || null | ||
id: result.rows[0][id_prop] || null | ||
}); | ||
@@ -110,0 +124,0 @@ }); |
@@ -28,2 +28,10 @@ var postgres = require("pg"), | ||
}; | ||
Driver.prototype.ping = function (cb) { | ||
this.db.query("SELECT * FROM pg_stat_activity LIMIT 1", function () { | ||
return cb(); | ||
}); | ||
return this; | ||
}; | ||
Driver.prototype.connect = function (cb) { | ||
@@ -93,3 +101,3 @@ //console.log("connect called"); | ||
}; | ||
Driver.prototype.insert = function (table, data, cb) { | ||
Driver.prototype.insert = function (table, data, id_prop, cb) { | ||
this.QueryInsert | ||
@@ -109,3 +117,3 @@ .clear() | ||
return cb(null, { | ||
id: result.rows[0].id || null | ||
id: result.rows[0][id_prop] || null | ||
}); | ||
@@ -112,0 +120,0 @@ }); |
@@ -12,3 +12,3 @@ var util = require("util"); | ||
Driver.prototype.insert = function (table, data, cb) { | ||
Driver.prototype.insert = function (table, data, id_prop, cb) { | ||
this.QueryInsert | ||
@@ -15,0 +15,0 @@ .clear() |
@@ -36,2 +36,7 @@ var sqlite3 = require("sqlite3"); | ||
Driver.prototype.ping = function (cb) { | ||
process.nextTick(cb); | ||
return this; | ||
}; | ||
Driver.prototype.on = function (ev, cb) { | ||
@@ -101,3 +106,3 @@ if (ev == "error") { | ||
Driver.prototype.insert = function (table, data, cb) { | ||
Driver.prototype.insert = function (table, data, id_prop, cb) { | ||
this.QueryInsert | ||
@@ -104,0 +109,0 @@ .clear() |
@@ -64,2 +64,4 @@ var Property = require("./Property"); | ||
Hook.trigger(instance, opts.hooks.beforeSave); | ||
handleValidations(function (err) { | ||
@@ -75,4 +77,2 @@ if (err) { | ||
Hook.trigger(instance, opts.hooks.beforeSave); | ||
var data = {}; | ||
@@ -94,3 +94,3 @@ for (var k in opts.data) { | ||
Hook.trigger(instance, opts.hooks.beforeCreate); | ||
opts.driver.insert(opts.table, data, function (err, info) { | ||
opts.driver.insert(opts.table, data, opts.id, function (err, info) { | ||
if (!err) { | ||
@@ -102,2 +102,3 @@ opts.changes.length = 0; | ||
emitEvent("save", err, instance); | ||
Hook.trigger(instance, opts.hooks.afterCreate, !err); | ||
Hook.trigger(instance, opts.hooks.afterSave, !err); | ||
@@ -164,2 +165,3 @@ if (typeof cb == "function") { | ||
emitEvent("remove", err, instance); | ||
Hook.trigger(instance, opts.hooks.afterRemove, !err); | ||
@@ -166,0 +168,0 @@ if (typeof cb == "function") { |
@@ -194,2 +194,6 @@ var Instance = require("./Instance").Instance; | ||
delete options.__merge; | ||
} | ||
if (options.hasOwnProperty("order")) { | ||
order = options.order; | ||
delete options.order; | ||
} | ||
@@ -196,0 +200,0 @@ } |
@@ -121,2 +121,7 @@ var util = require("util"); | ||
}; | ||
ORM.prototype.ping = function (cb) { | ||
this.driver.ping(cb); | ||
return this; | ||
}; | ||
ORM.prototype.close = function (cb) { | ||
@@ -128,4 +133,21 @@ this.driver.close(cb); | ||
ORM.prototype.load = function (file, cb) { | ||
var cwd = process.cwd(); | ||
var err = new Error(); | ||
var tmp = err.stack.split(/\r?\n/)[2], m; | ||
if (m = tmp.match(/^\s*at\s+(.+):\d+:\d+$/)) { | ||
cwd = path.dirname(m[1]); | ||
} else if (m = tmp.match(/^\s*at\s+module\.exports\s+\((.+?)\)/)) { | ||
cwd = path.dirname(m[1]); | ||
} | ||
if (file[0] != path.sep) { | ||
file = cwd + "/" + file; | ||
} | ||
if (file.substr(-1) == path.sep) { | ||
file += "index"; | ||
} | ||
try { | ||
require((file[0] != path.sep ? process.cwd() + "/" : "") + file)(this, cb); | ||
require(file)(this, cb); | ||
} catch (ex) { | ||
@@ -132,0 +154,0 @@ return cb(ex); |
@@ -130,2 +130,9 @@ module.exports = Builder; | ||
for (i = 0; i < this.opts.where.length; i++) { | ||
if (this.opts.where[i].value === null) { | ||
lst.push([ | ||
this.escapeId(this.opts.where[i].field), | ||
"IS NULL" | ||
].join(" ")); | ||
continue; | ||
} | ||
if (typeof this.opts.where[i].value.orm_special_object == "function") { | ||
@@ -143,2 +150,9 @@ var op = this.opts.where[i].value.orm_special_object(); | ||
break; | ||
case "like": | ||
lst.push([ | ||
this.escapeId(this.opts.where[i].field), | ||
"LIKE", | ||
this.escape(this.opts.where[i].value.expr) | ||
].join(" ")); | ||
break; | ||
case "eq": | ||
@@ -145,0 +159,0 @@ case "ne": |
exports.between = function (a, b) { | ||
return createSpecialObject({ from: a, to: b }, 'between'); | ||
}; | ||
exports.like = function (expr) { | ||
return createSpecialObject({ expr: expr }, 'like'); | ||
}; | ||
@@ -5,0 +8,0 @@ exports.eq = function (v) { |
@@ -14,3 +14,3 @@ { | ||
], | ||
"version": "2.0.3", | ||
"version": "2.0.4", | ||
"license": "MIT", | ||
@@ -17,0 +17,0 @@ "repository": { |
@@ -11,3 +11,3 @@ ## Object Relational Mapping | ||
Current stable version: **2.0.3** | ||
Current stable version: **2.0.4** | ||
@@ -26,3 +26,3 @@ ## DBMS Support | ||
- Define custom validations (several builtin validations, check instance properties before saving) | ||
- Instance singleton (table rows fetched twice are the same object, changes to one change all) | ||
- Model instance caching and integrity (table rows fetched twice are the same object, changes to one change all) | ||
@@ -33,3 +33,3 @@ ## Introduction | ||
Here is an example on how to use it: | ||
An example: | ||
@@ -77,3 +77,3 @@ ```js | ||
You have a global settings object and one for each connection. | ||
Settings are used to store key value pairs. A settings object is stored on the global orm object and on each database connection. | ||
@@ -96,11 +96,9 @@ ```js | ||
A Model is a structure binded to one or more tables, depending on the associations. The model name is assumed to be the table name. After defining a model you can use it to manipulate the table. | ||
A Model is an abstraction over one or more database tables. Models support associations (more below). The name of the model is assumed to match the table name. | ||
After defining a Model you can get a specific element or find one or more based on some conditions. | ||
Models support behaviours for accessing and manipulating table data. | ||
## Defining Models | ||
To define a model, you use the reference to the database connection and call `define`. The function will define a Model | ||
and will return it to you. You can get it later by it's id directly from the database connection so you don't actually | ||
need to store a reference to it. | ||
Call `define` on the database connection to setup a model. The name of the table and model is used as an identifier for the model on the database connection, so you can easily access the model later using the connection. | ||
@@ -119,5 +117,6 @@ ```js | ||
If you prefer to have your models defined in separated files, you can define them in a function inside a module and | ||
export the function has the entire module. You can have cascading loads. | ||
Models can be in separate modules. Simply ensure that the module holding the models uses module.exports to publish a function that accepts the database connection, then load your models however you like. | ||
Note - using this technique you can have cascading loads. | ||
```js | ||
@@ -156,7 +155,5 @@ // your main file (after connecting) | ||
## Synching Models | ||
## Synchronizing Models | ||
If you don't have the tables on the database you have to call the `.sync()` on every Model. This will just create the | ||
tables necessary for your Model. If you have more than one Model you can call `.sync()` directly on the database | ||
connection to syncronize all Models. | ||
Models can create their underlying tables in the database. You may call Model.sync() on each Model to create the underlying table or you can call db.sync() at a connection level to create all tables for all models. | ||
@@ -182,3 +179,4 @@ ```js | ||
Using [Settings](#settings) or directly on Model definition you can tweak some options. | ||
ORM2 allows you some advanced tweaks on your Model definitions. You can configure these via settings or in the call to `define` when you setup the Model. | ||
For example, each Model instance has a unique ID in the database. This table column is | ||
@@ -216,4 +214,6 @@ by default "id" but you can change it. | ||
If you want to listen for a type of event than occurs in instances of a Model, you can attach a function that | ||
will be called when that event happens. There are some events possible: | ||
will be called when that event happens. | ||
Currently the following events are supported: | ||
- `afterLoad` : (no parameters) Right after loading and preparing an instance to be used; | ||
@@ -223,3 +223,5 @@ - `beforeSave` : (no parameters) Right before trying to save; | ||
- `beforeCreate` : (no parameters) Right before trying to save a new instance; | ||
- `afterCreate` : (bool success) Right after saving a new instance; | ||
- `beforeRemove` : (no parameters) Right before trying to remove an instance. | ||
- `afterRemove` : (bool success) Right after removing an instance; | ||
@@ -273,3 +275,3 @@ All hook function are called with `this` as the instance so you can access anything you want related to it. | ||
If you just want to count the number of items that match a condition you can just use `.count()` instead of finding all | ||
of them and counting. This will actually tell the database server to do a count, the count is not done in javascript. | ||
of them and counting. This will actually tell the database server to do a count (it won't be done in the node process itself). | ||
@@ -300,3 +302,3 @@ ```js | ||
If you prefer another less complicated syntax you can chain `.find()` by not giving a callback parameter. | ||
If you prefer less complicated syntax you can chain `.find()` by not giving a callback parameter. | ||
@@ -380,8 +382,8 @@ ```js | ||
### Singleton | ||
### Caching & Integrity | ||
Each model instances is cached, so if you fetch the same record using 2 or more different queries, you will | ||
Model instances are cached. If multiple different queries will result in the same result, you will | ||
get the same object. If you have other systems that can change your database (or you're developing and need | ||
to make some manual changes) you should remove this feature by disabling cache. You do this when you're | ||
defining each Model. | ||
to make some manual changes) you should remove this feature by disabling cache. This can be done when you're | ||
defining the Model. | ||
@@ -396,5 +398,31 @@ ```js | ||
If you want singletons but want cache to expire after a period of time, you can pass a number instead of a | ||
The cache can be configured to expire after a period of time by passing in a number instead of a | ||
boolean. The number will be considered the cache timeout in seconds (you can use floating point). | ||
## Creating Items | ||
### Model.create(items, cb) | ||
To insert new elements to the database use `Model.create`. | ||
```js | ||
Person.create([ | ||
{ | ||
name: "John", | ||
surname: "Doe", | ||
age: 25, | ||
male: true | ||
}, | ||
{ | ||
name: "Liza", | ||
surname: "Kollan", | ||
age: 19, | ||
male: false | ||
} | ||
], function (err, items) { | ||
// err - description of the error or null | ||
// items - array of inserted items | ||
}); | ||
``` | ||
## Associations | ||
@@ -445,3 +473,3 @@ | ||
For relations of 1 to many you have to use `hasMany` associations. This assumes another table that has 2 columns, one for each table in the association. | ||
For relations of 1 to many you have to use `hasMany` associations. This assumes the existence of a separate join table that has 2 columns, each referencing the table in the association. Ideally, these would be foreign key relationships in your database. | ||
@@ -544,3 +572,3 @@ ```js | ||
}); | ||
Person.hasMany("pets", Person, { | ||
Person.hasMany("pets", Pet, { | ||
bought : Date | ||
@@ -547,0 +575,0 @@ }, { |
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
196721
119
5822
566