├── .gitignore
├── .travis.yml
├── @types
├── index.d.ts
└── orm_mirror
│ ├── orm.d.ts
│ └── sql-query.d.ts
├── License
├── Readme.md
├── build
├── bundle.js
└── orm_mirror.js
├── lib
├── index.js
├── modules
│ ├── mysql.js
│ └── sqlite3.js
└── patch.js
├── package.json
├── src
├── index.ts
├── modules
│ ├── index.d.ts
│ ├── mysql.ts
│ └── sqlite3.ts
└── patch.ts
└── test
├── index.js
├── integration
├── association-extend.js
├── association-hasmany-extra.js
├── association-hasmany-hooks.js
├── association-hasmany.js
├── association-hasone-required.js
├── association-hasone-reverse.js
├── association-hasone-zeroid.js
├── association-hasone.js
├── date-type.js
├── event.js
├── hook.js
├── instance.js
├── model-aggregate.js
├── model-clear.js
├── model-count.js
├── model-create.js
├── model-exists.js
├── model-find-chain.js
├── model-find-mapsto.js
├── model-find.js
├── model-get.js
├── model-keys.js
├── model-one.js
├── model-save.js
├── model-sync.js
├── predefined-validators.js
├── property-custom.js
├── property-lazyload.js
├── property-maps-to.js
├── property.js
├── settings.js
├── smart-types.js
└── validation.js
└── support
├── opts.js
└── spec_helper.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules
2 | test.db*
3 | package-lock.json
--------------------------------------------------------------------------------
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: node_js
2 | node_js: '6'
3 | env:
4 | - VERSION=0.21.0
5 | - VERSION=0.22.0
6 | - VERSION=0.23.0
7 | - VERSION=0.24.0
8 | script:
9 | - npm i
10 | - mkdir -p ./node_modules/.bin
11 | - curl -SL "https://github.com/fibjs/fibjs/releases/download/v${VERSION}/fibjs-v${VERSION}-linux-x64.xz" -o ./node_modules/.bin/fibjs.xz
12 | - xz -d ./node_modules/.bin/fibjs.xz
13 | - chmod a+x ./node_modules/.bin/fibjs
14 | - npm run ci
15 |
--------------------------------------------------------------------------------
/@types/index.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import OrmNS = require('orm')
4 | import SqlQueryNS from 'sqlquery'
5 |
6 | declare module "orm" {
7 | /* Connection About Patch :start */
8 |
9 | /**
10 | * it should be defined in 'orm' but there is not, so we should fix it
11 | */
12 | interface ConnInstanceInOrmConnDriverDB {
13 | begin(): void;
14 | close(): void;
15 | commit(): void;
16 | rollback(): void;
17 | trans(func: Function): boolean
18 | execute(sql: string, ...args: any[]): any[];
19 | }
20 |
21 | interface SQLiteConnInstanceInOrmConnDriverDB extends ConnInstanceInOrmConnDriverDB, Class_SQLite {
22 | }
23 | interface MySQLConnInstanceInOrmConnDriverDB extends ConnInstanceInOrmConnDriverDB, Class_MySQL {
24 | }
25 | interface MSSQLConnInstanceInOrmConnDriverDB extends ConnInstanceInOrmConnDriverDB, Class_MSSQL {
26 | }
27 |
28 | interface DbInstanceInOrmConnDriver {
29 | conn: ConnInstanceInOrmConnDriverDB
30 | }
31 | export interface OrigOrmExecQueryOpts {
32 | [key: string]: any;
33 | }
34 | export interface OrigOrmConnDriver {
35 | // dialog type
36 | dialect: string;
37 | propertyToValue: Function;
38 | valueToProperty: Function;
39 | insert: Function;
40 | db: DbInstanceInOrmConnDriver
41 | }
42 | /**
43 | * then we should patch still
44 | */
45 | export interface PatchedOrmConnDriver extends OrigOrmConnDriver {
46 | execQuerySync: (query: SqlQueryNS.Query, opt: OrigOrmExecQueryOpts) => any
47 | }
48 |
49 | export interface OrmConnectionOpts {
50 | }
51 | /* Connection About Patch :end */
52 |
53 | export interface FibOrmFixedModelOptions /* extends OrmNS.ModelOptions */ {
54 | id?: string[];
55 | autoFetch?: boolean;
56 | autoFetchLimit?: number;
57 | cacheFetch?: boolean;
58 | hooks?: OrmNS.Hooks;
59 | methods?: { [name: string]: Function };
60 |
61 | [extensibleProperty: string]: any;
62 | }
63 |
64 | export interface FibORM extends OrmNS.ORM {
65 | /* all fixed: start */
66 | models: { [key: string]: FibOrmFixedModel };
67 |
68 | use(plugin: string, options?: any): FibORM;
69 | use(plugin: Plugin, options?: any): FibORM;
70 |
71 | define(name: string, properties: { [key: string]: OrigModelPropertyDefinition }, opts?: FibOrmFixedModelOptions): FibOrmFixedModel;
72 | ping(callback: (err: Error) => void): FibORM;
73 | close(callback: (err: Error) => void): FibORM;
74 | load(file: string, callback: (err: Error) => void): any;
75 | sync(callback: (err: Error) => void): FibORM;
76 | drop(callback: (err: Error) => void): FibORM;
77 | /* all fixed: end */
78 |
79 | /* memeber patch: start */
80 | driver: PatchedOrmConnDriver
81 | begin: () => any
82 | commit: () => any
83 | rollback: () => any
84 | trans: (func: Function) => any
85 |
86 | syncSync(): void;
87 |
88 | [extraMember: string]: any;
89 | /* memeber patch: end */
90 | }
91 | // bad annotation but 'db' is used as like 'orm' ever, so we use 'FibOrmDB' to substitute FibORM
92 | type FibOrmDB = FibORM
93 |
94 | export function connectSync(opts: OrmNS.FibORMIConnectionOptions | string): FibOrmDB;
95 |
96 | export interface FibORMIConnectionOptions extends OrmNS.IConnectionOptions {
97 | timezone: string;
98 | }
99 |
100 | interface InstanceAssociationItem {
101 | name: string;
102 | field: OrigDetailedModelProperty
103 | // funcname = string;
104 | getAccessor: string;
105 | setAccessor: string;
106 | hasAccessor: string;
107 | delAccessor: string;
108 | addAccessor: string;
109 | }
110 |
111 | interface InstanceOptions extends OrmNS.ModelOptions {
112 | one_associations: InstanceAssociationItem[]
113 | many_associations: InstanceAssociationItem[]
114 | extend_associations: InstanceAssociationItem[]
115 | association_properties: any
116 | fieldToPropertyMap: any
117 | }
118 |
119 | interface PatchedSyncfiedModelOrInstance {
120 | /**
121 | * @important
122 | *
123 | * methods patchSyncfied by 'fib-orm'
124 | */
125 | countSync: Function;
126 | firstSync: Function;
127 | lastSync: Function;
128 | allSync: Function;
129 | whereSync: Function;
130 | findSync: Function;
131 | removeSync: Function;
132 | runSync: Function;
133 | }
134 |
135 | interface PatchedSyncfiedInstanceWithDbWriteOperation extends PatchedSyncfiedModelOrInstance {
136 | saveSync: Function;
137 | removeSync: Function;
138 | validateSync: Function;
139 | modelSync: Function;
140 | }
141 | interface PatchedSyncfiedInstanceWithAssociations {
142 | /**
143 | * generated by association, but you don't know what it is
144 | */
145 | /* getXxx: Function; */
146 | /* setXxx: Function; */
147 | /* removeXxx: Function; */
148 |
149 | /* findByXxx: Function; */
150 | [associationFunc: string]: Function;
151 | }
152 |
153 | // keep compatible to orig one in 'orm'
154 | interface OrigDetailedModelProperty extends OrmNS.Property {
155 | /**
156 | * text | number | integer | boolean | date | enum | object | point | binary | serial
157 | * view details in https://github.com/dresende/node-orm2/wiki/Model-Properties
158 | */
159 | type: string
160 | unique?: boolean
161 | }
162 | type OrigModelPropertyDefinition = OrigDetailedModelProperty |
163 | String | Function | Date | Object | any[]
164 |
165 | type OrigAggreteGenerator = (...args: any[]) => OrmNS.IAggregated
166 | interface OrigHooks extends OrmNS.Hooks {
167 | afterAutoFetch?: (next?) => void
168 | }
169 |
170 | export interface FibOrmFixedModel extends OrmNS.Model, OrigHooks, PatchedSyncfiedModelOrInstance {
171 | (): FibOrmFixedModel;// FibOrmFixedModelInstance;
172 | (...ids: any[]): FibOrmFixedModel;// FibOrmFixedModelInstance;
173 |
174 | new (): FibOrmFixedModelInstance;
175 | new (...ids: any[]): FibOrmFixedModelInstance;
176 |
177 | properties: { [property: string]: OrigDetailedModelProperty }
178 | allProperties: { [key: string]: OrigDetailedModelProperty }
179 |
180 | find(): any /* OrmNS.Model|OrmNS.IChainFind */;
181 |
182 | /**
183 | * methods used to add associations
184 | */
185 | hasOne: (...args: any[]) => any;
186 | hasMany: (...args: any[]) => any;
187 | extendsTo: (...args: any[]) => OrmNS.Model;
188 |
189 | extends: { [extendModel: string]: ExtendModelWrapper };
190 |
191 | [extraProperty: string]: any;
192 | }
193 |
194 | export interface ExtendModelWrapper {
195 | // 'hasOne', 'hasMany'
196 | type: string;
197 | reversed?: boolean;
198 | model: FibOrmFixedExtendModel;
199 | }
200 |
201 | export interface FibOrmFixedExtendModel extends FibOrmFixedModel {
202 | model_name: string;
203 | }
204 |
205 | export interface FibOrmFindLikeQueryObject {
206 | [key: string]: any;
207 | }
208 |
209 | // patch the missing field defined in orm/lib/Instance.js (such as defined by Object.defineProperty)
210 | export interface FibOrmFixedModelInstance extends OrmNS.Instance {
211 | /* all fixed: start */
212 | on(event: string, callback): FibOrmFixedModelInstance;
213 | save(): Instance;
214 | save(data: { [property: string]: any; }, callback: (err: Error) => void): FibOrmFixedModelInstance;
215 | save(data: { [property: string]: any; }, options: any, callback: (err: Error) => void): FibOrmFixedModelInstance;
216 | saved: boolean;
217 | remove(callback: (err: Error) => void): FibOrmFixedModelInstance;
218 | isInstance: boolean;
219 | isPersisted: boolean;
220 | isShell: boolean;
221 | validate(callback: (errors: Error[]) => void);
222 | /* all fixed: end */
223 |
224 | /* missing fix: start */
225 | set: Function;
226 | markAsDirty: Function;
227 | dirtyProperties: object;
228 | __singleton_uid: string | number;
229 | __opts?: InstanceOptions;
230 | model: FibOrmFixedModel;
231 |
232 | /* missing fix: end */
233 |
234 | [extraProperty: string]: any;
235 | }
236 |
237 | export interface FibOrmPatchedSyncfiedInstantce extends PatchedSyncfiedInstanceWithDbWriteOperation, PatchedSyncfiedInstanceWithAssociations {
238 | }
239 |
240 | export interface FibOrmPatchedSyncfiedDueToAggregationInstance {
241 | /* function getXxx() */
242 | }
243 |
244 | // export type FibOrmObjectToPatch =
245 | // FibOrmFixedModel | FibOrmFixedModelInstance
246 | // | FibOrmPatchedSyncfiedInstantce | PatchedSyncfiedInstanceWithDbWriteOperation | PatchedSyncfiedInstanceWithAssociations
247 |
248 | export interface IChainFibORMFind extends PatchedSyncfiedModelOrInstance, SqlQueryNS.SelectQuery {
249 | only(args: string|string[]): IChainFibORMFind;
250 | only(...args: string[]): IChainFibORMFind;
251 | order(...order: string[]): IChainFibORMFind;
252 | }
253 | /* Orm About Patch :end */
254 | }
255 |
--------------------------------------------------------------------------------
/@types/orm_mirror/orm.d.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | declare module "orm" {
4 |
5 | import events = require('events');
6 | import sqlquery = require('sqlquery');
7 |
8 | module orm {
9 |
10 | /**
11 | * Parameter Type Interfaces
12 | **/
13 |
14 | export interface Model {
15 | (): Instance;
16 | (...ids: any[]): Instance;
17 |
18 | properties: { [property: string]: Property };
19 | settings: Settings;
20 |
21 | drop(callback?: (err: Error) => void): Model;
22 | sync(callback?: (err: Error) => void): Model;
23 | get(...args: any[]): Model;
24 | find(conditions: { [property: string]: any }, callback: (err: Error, results: Instance[]) => void): Model;
25 | find(conditions: { [property: string]: any }, options: {
26 | limit?: number;
27 | order?: any;
28 | }, callback: (err: Error, results: Instance[]) => void): Model;
29 | find(conditions: { [property: string]: any }, limit: number, order: string[], callback: (err: Error, results: Instance[]) => void): Model;
30 | find(conditions: { [property: string]: any }): IChainFind;
31 |
32 | all(conditions: { [property: string]: any }, callback: (err: Error, results: Instance[]) => void): Model;
33 | all(conditions: { [property: string]: any }, options: {
34 | limit?: number;
35 | order?: any;
36 | }, callback: (err: Error, results: Instance[]) => void): Model;
37 | all(conditions: { [property: string]: any }, limit: number, order: string[], callback: (err: Error, results: Instance[]) => void): Model;
38 |
39 | one(conditions: { [property: string]: any }, callback: (err: Error, result: Instance) => void): Model;
40 | one(conditions: { [property: string]: any }, options: {
41 | limit?: number;
42 | order?: any;
43 | }, callback: (err: Error, result: Instance) => void): Model;
44 | one(conditions: { [property: string]: any }, limit: number, order: string[], callback: (err: Error, result: Instance) => void): Model;
45 |
46 | count(callback: (err: Error, count: number) => void): Model;
47 | count(conditions: { [property: string]: any }, callback: (err: Error, count: number) => void): Model;
48 |
49 | aggregate(conditions: { [property: string]: any }): IAggregated;
50 | aggregate(properties: string[]): IAggregated;
51 | aggregate(conditions: { [property: string]: any }, properties: string[]): IAggregated;
52 |
53 | exists(id: any, callback: (err: Error, exists: boolean) => void): Model;
54 | exists(...args: any[]): Model;
55 |
56 | create(data: { [property: string]: any; }, callback: (err: Error, instance: Instance) => void): Model;
57 | create(...args: any[]): Model;
58 |
59 | clear(): Model;
60 |
61 | table: string;
62 | id: string[];
63 |
64 | [property: string]: any;
65 | }
66 |
67 | export interface Instance {
68 | on(event: string, callback): Instance;
69 | save(): Instance;
70 | save(data: { [property: string]: any; }, callback: (err: Error) => void): Instance;
71 | save(data: { [property: string]: any; }, options: any, callback: (err: Error) => void): Instance;
72 | saved: boolean;
73 | remove(callback: (err: Error) => void): Instance;
74 | isInstance: boolean;
75 | isPersisted: boolean;
76 | isShell: boolean;
77 | validate(callback: (errors: Error[]) => void);
78 | model: Model;
79 |
80 | [property: string]: any;
81 | }
82 |
83 | export interface ModelOptions {
84 | id?: string[];
85 | autoFetch?: boolean;
86 | autoFetchLimit?: number;
87 | cacheFetch?: boolean;
88 | hooks?: { [property: string]: Hooks };
89 | methods?: { [name: string]: Function };
90 | }
91 |
92 | export interface Hooks {
93 | beforeValidation?: (next?) => void;
94 | beforeCreate?: (next?) => void;
95 | afterCreate?: (next?) => void;
96 | beforeSave?: (next?) => void;
97 | afterSave?: (next?) => void;
98 | afterLoad?: (next?) => void;
99 | afterAutoFetch?: (next?) => void;
100 | beforeRemove?: (next?) => void;
101 | afterRemove?: (next?) => void;
102 | }
103 |
104 | export interface IConnectionOptions {
105 | protocol: string;
106 | host?: string;
107 | port?: number;
108 | auth?: string;
109 | username?: string;
110 | password?: string;
111 | database?: string;
112 | pool?: boolean;
113 | debug?: boolean;
114 | }
115 |
116 | export interface IAggregated {
117 | groupBy(...columns: string[]): IAggregated;
118 | limit(limit: number): IAggregated;
119 | limit(offset: number, limit: number): IAggregated;
120 | order(...order: string[]): IAggregated;
121 | select(columns: string[]): IAggregated;
122 | select(...columns: string[]): IAggregated;
123 | as(alias: string): IAggregated;
124 | call(fun: string, args: any[]): IAggregated;
125 | get(callback: (err: Error, instance: Instance) => void);
126 | }
127 |
128 | export interface IChainFind {
129 | find(conditions: { [property: string]: any }): IChainFind;
130 | only(...args: string[]): IChainFind;
131 | limit(limit: number): IChainFind;
132 | offset(offset: number): IChainFind;
133 | run(callback: (err: Error, results: Instance[]) => void): void;
134 | count(callback: (err: Error, count: number) => void): void;
135 | remove(callback: (err: Error) => void): void;
136 | save(callback: (err: Error) => void): void;
137 | each(callback: (result: Instance) => void): void;
138 | each(): IChainFind;
139 | filter(callback: (result: Instance) => boolean): IChainFind;
140 | sort(callback: (a: Instance, b: Instance) => boolean): IChainFind;
141 | get(callback: (results: Instance[]) => void): IChainFind;
142 | }
143 |
144 | /*
145 | * Classes
146 | */
147 |
148 | export class ORM extends events.EventEmitter {
149 | validators: enforce;
150 | enforce: enforce;
151 | settings: Settings;
152 | driver_name: string;
153 | driver: any;
154 | tools: any;
155 | models: { [key: string]: Model };
156 | plugins: Plugin[];
157 |
158 | use(plugin: string, options?: any): ORM;
159 | use(plugin: Plugin, options?: any): ORM;
160 |
161 | define(name: string, properties: { [key: string]: Property }, opts?: ModelOptions): Model;
162 | ping(callback: (err: Error) => void): ORM;
163 | close(callback: (err: Error) => void): ORM;
164 | load(file: string, callback: (err: Error) => void): any;
165 | sync(callback: (err: Error) => void): ORM;
166 | drop(callback: (err: Error) => void): ORM;
167 | }
168 |
169 | export class enforce {
170 | static required(message?: string);
171 | static notEmptyString(message?: string);
172 | static rangeNumber(min: number, max: number, message?: string);
173 | static rangeLength(min: number, max: number, message?: string);
174 | static insideList(inside: string[], message?: string);
175 | static insideList(inside: number[], message?: string);
176 | static outsideList(outside: string[], message?: string);
177 | static outsideList(outside: number[], message?: string);
178 | static password(conditions?: string, message?: string);
179 | static patterns(expr: RegExp, message?: string);
180 | static patterns(expr: string, flags: string, message?: string);
181 | static equalToProperty(name: string, message?: string);
182 | static unique(message?: string);
183 | static unique(opts: { ignoreCase: boolean }, message?: string);
184 | }
185 |
186 | export function equalToProperty(name: string, message?: string);
187 | export function unique(message?: string);
188 | export function unique(opts: { ignoreCase: boolean }, message?: string);
189 |
190 | export class singleton {
191 | static clear(key?: string): singleton;
192 | static get(key, opts: {
193 | identityCache?: any;
194 | saveCheck?: boolean;
195 | }, createCb: Function, returnCb: Function);
196 | }
197 |
198 | export class Settings {
199 | static Container: any;
200 |
201 | static defaults(): {
202 | properties: {
203 | primary_key: string;
204 | association_key: string;
205 | required: boolean;
206 | };
207 |
208 | instance: {
209 | identityCache: boolean;
210 | identityCacheSaveCheck: boolean;
211 | autoSave: boolean;
212 | autoFetch: boolean;
213 | autoFetchLimit: number;
214 | cascadeRemove: boolean;
215 | returnAllErrors: boolean;
216 | };
217 |
218 | connection: {
219 | reconnect: boolean;
220 | poll: boolean;
221 | debug: boolean;
222 | };
223 | };
224 |
225 | constructor(settings: any);
226 |
227 | //[key: string]: {
228 | // get: (key, def) => any;
229 | // set: (key, value) => Settings;
230 | // unset: (...keys: string[]) => Settings;
231 | //}
232 |
233 | }
234 |
235 | export var settings: Settings;
236 |
237 | export class Property {
238 | static normalize(property: string, settings: Settings): any;
239 | static validate(value: any, property: string): any;
240 | }
241 |
242 | export interface ErrorCodes {
243 | QUERY_ERROR: number;
244 | NOT_FOUND: number;
245 | NOT_DEFINED: number;
246 | NO_SUPPORT: number;
247 | MISSING_CALLBACK: number;
248 | PARAM_MISMATCH: number;
249 | CONNECTION_LOST: number;
250 |
251 | generateError(code: number, message: string, extra: any): Error;
252 | }
253 |
254 | export function Text(type: string): sqlquery.TextQuery;
255 | export function eq(value: any): sqlquery.Comparator;
256 | export function ne(value: any): sqlquery.Comparator;
257 | export function gt(value: any): sqlquery.Comparator;
258 | export function gte(value: any): sqlquery.Comparator;
259 | export function lt(value: any): sqlquery.Comparator;
260 | export function lte(value: any): sqlquery.Comparator;
261 | export function like(value: string): sqlquery.Comparator;
262 | export function between(a: number, b: number): sqlquery.Comparator;
263 | export function not_between(a: number, b: number): sqlquery.Comparator;
264 | export function express(uri: string, handlers: {
265 | define(db: ORM, models: { [key: string]: Model });
266 | }): (req, res, next) => void;
267 | export function use(connection, protocol: string, options, callback: (err: Error, db?: ORM) => void);
268 | export function connect(uri: string): ORM;
269 | export function connect(uri: string, callback: (err: Error, db: ORM) => void);
270 | export function connect(options: IConnectionOptions): ORM;
271 | export function connect(options: IConnectionOptions, callback: (err: Error, db: ORM) => void);
272 | }
273 |
274 | export = orm;
275 | }
276 |
--------------------------------------------------------------------------------
/@types/orm_mirror/sql-query.d.ts:
--------------------------------------------------------------------------------
1 | declare module "sqlquery" {
2 | module sqlquery {
3 | export class Query {
4 | constructor(dialect: string);
5 | constructor(options: {
6 | dialect: string;
7 | });
8 | static Text(type: string): TextQuery;
9 |
10 | static Comparators: string[];
11 | static between(a: number, b: number): Comparator;
12 | static not_between(a: number, b: number): Comparator;
13 | static like(expression: string): Comparator;
14 | static eq(value: any): Comparator;
15 | static ne(value: any): Comparator;
16 | static gt(value: any): Comparator;
17 | static gte(value: any): Comparator;
18 | static lt(value: any): Comparator;
19 | static lte(value: any): Comparator;
20 |
21 | escapeId(id: string): string;
22 | escapeId(id: string, table: string): string;
23 | escapeVal(value: any): string;
24 | select(): SelectQuery;
25 | insert(): InsertQuery;
26 | update(): UpdateQuery;
27 | remove(): RemoveQuery;
28 | }
29 |
30 | export interface Comparator {
31 | sql_comparator(): string;
32 | from?: any;
33 | to?: any;
34 | expr?: string;
35 | value?: any;
36 | }
37 |
38 | export interface TextQuery {
39 | data: any;
40 | type: string;
41 | }
42 |
43 | export interface SelectQuery {
44 | select(fields: string): SelectQuery;
45 | calculateFoundRows: SelectQuery;
46 | as(alias: string): SelectQuery;
47 | fun(fun: string, column: string, alias: string): SelectQuery;
48 | from(table: string, from_id: string, to_id: string): SelectQuery;
49 | from(table: string, from_id: string, to_table: string, to_id: string): SelectQuery;
50 | where(...args: any[]): SelectQuery;
51 | whereExists(table: string, table_link: string, link: string, conditions: { [column: string]: any }): SelectQuery;
52 | groupBy(...columns: string[]): SelectQuery;
53 | offset(offset: number): SelectQuery;
54 | limit(limit: number): SelectQuery;
55 | order(column: string, direction: string): SelectQuery;
56 | build(): string;
57 | }
58 |
59 | export interface InsertQuery {
60 | into(table: string): InsertQuery;
61 | set(values: { [key: string]: any }[]): InsertQuery;
62 | build(): string;
63 | }
64 |
65 | export interface UpdateQuery {
66 | into(table: string): UpdateQuery;
67 | set(values: { [column: string]: any }): UpdateQuery;
68 | where(...conditions: { [column: string]: any }[]): UpdateQuery;
69 | build(): string;
70 | }
71 |
72 | export interface RemoveQuery {
73 | from(table: string): RemoveQuery;
74 | where(...conditions: { [column: string]: any }[]): RemoveQuery;
75 | build(): string;
76 | }
77 | }
78 | export = sqlquery;
79 | }
80 |
--------------------------------------------------------------------------------
/License:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013 Diogo Resende and other contributors
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/build/bundle.js:
--------------------------------------------------------------------------------
1 | const fs = require('fs')
2 | const path = require('path')
3 |
4 | const fibTypify = require('fib-typify')
5 |
6 | const baseDir = path.resolve(__dirname, '../src')
7 | const distDir = path.resolve(__dirname, '../lib')
8 |
9 | fibTypify.compileDirectoryTo(baseDir, distDir, {
10 | compilerOptions: {
11 | target: 'es6',
12 | module: 'commonjs'
13 | }
14 | })
--------------------------------------------------------------------------------
/build/orm_mirror.js:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env fibjs
2 |
3 | const fs = require('fs')
4 | const path = require('path')
5 |
6 | const filepathList = [
7 | 'orm.d.ts',
8 | 'sql-query.d.ts'
9 | ]
10 |
11 | const distDir = path.resolve(__dirname, '../@types/orm_mirror')
12 | if (!fs.exists(distDir)) {
13 | fs.mkdir(distDir)
14 | }
15 |
16 | filepathList.forEach((filepath) => {
17 | fs.copy(
18 | path.resolve(__dirname, '../node_modules/orm/lib/TypeScript', filepath),
19 | path.join(distDir, filepath)
20 | )
21 | })
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | Object.defineProperty(exports, "__esModule", { value: true });
3 | const vm = require("vm");
4 | const patch = require('./patch');
5 | const sbox = new vm.SandBox({
6 | util: require('util'),
7 | events: require('events'),
8 | url: require('url'),
9 | sqlite3: require('./modules/sqlite3'),
10 | mysql: require('./modules/mysql')
11 | });
12 | const orm = sbox.require('orm', __filename);
13 | module.exports = patch(orm);
14 |
--------------------------------------------------------------------------------
/lib/modules/mysql.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | ///
3 | ///
4 | Object.defineProperty(exports, "__esModule", { value: true });
5 | const db = require("db");
6 | class Database {
7 | constructor(connOpts) {
8 | this.opts = connOpts;
9 | }
10 | on(ev) { }
11 | ping(cb) {
12 | setImmediate(cb);
13 | }
14 | connect(cb) {
15 | const that = this;
16 | const openMySQL = db.openMySQL;
17 | openMySQL(this.opts, function (e, conn) {
18 | if (!e)
19 | that.conn = conn;
20 | cb(e);
21 | });
22 | }
23 | query(sql, cb) {
24 | this.conn.execute(sql, cb);
25 | }
26 | execute(sql) {
27 | return this.conn.execute(sql);
28 | }
29 | end(cb) {
30 | this.conn.close(cb);
31 | }
32 | }
33 | exports.createConnection = function (connOpts) {
34 | return new Database(connOpts);
35 | };
36 |
--------------------------------------------------------------------------------
/lib/modules/sqlite3.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | ///
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | const db = require("db");
5 | class Database {
6 | constructor(fname) {
7 | this.conn = db.openSQLite(fname);
8 | }
9 | on(ev) { }
10 | all(sql, cb) {
11 | this.conn.execute(sql, cb);
12 | }
13 | get(sql, cb) {
14 | this.all(sql, function (e, r) {
15 | if (e)
16 | cb(e);
17 | cb(e, r[0]);
18 | });
19 | }
20 | execute(sql) {
21 | return this.conn.execute(sql);
22 | }
23 | close() {
24 | this.conn.close();
25 | }
26 | }
27 | exports.Database = Database;
28 |
--------------------------------------------------------------------------------
/lib/patch.js:
--------------------------------------------------------------------------------
1 | "use strict";
2 | ///
3 | Object.defineProperty(exports, "__esModule", { value: true });
4 | const util = require("util");
5 | const url = require("url");
6 | // patch async function to sync function
7 | function patchSync(o, funcs) {
8 | funcs.forEach(function (func) {
9 | const old_func = o[func];
10 | if (old_func) {
11 | Object.defineProperty(o, func + 'Sync', {
12 | value: util.sync(old_func),
13 | writable: true
14 | });
15 | }
16 | });
17 | }
18 | // hook find, patch result
19 | function patchResult(o) {
20 | var old_func = o.find;
21 | var m = o.model || o;
22 | var comps = ['val', 'from', 'to'];
23 | if (old_func.is_new)
24 | return;
25 | /**
26 | * filter the Date-Type SelectQuery Property corresponding item when call find-like executor ('find', 'get', 'where')
27 | * @param opt
28 | */
29 | function filter_date(opt) {
30 | for (var k in opt) {
31 | if (k === 'or')
32 | opt[k].forEach(filter_date);
33 | else {
34 | var p = m.allProperties[k];
35 | if (p && p.type === 'date') {
36 | var v = opt[k];
37 | if (!util.isDate(v)) {
38 | if (util.isNumber(v) || util.isString(v))
39 | opt[k] = new Date(v);
40 | else if (util.isObject(v)) {
41 | comps.forEach(c => {
42 | var v1 = v[c];
43 | if (util.isArray(v1)) {
44 | v1.forEach((v2, i) => {
45 | if (!util.isDate(v2))
46 | v1[i] = new Date(v2);
47 | });
48 | }
49 | else if (v1 !== undefined && !util.isDate(v1)) {
50 | v[c] = new Date(v1);
51 | }
52 | });
53 | }
54 | }
55 | }
56 | }
57 | }
58 | }
59 | var new_func = function () {
60 | var opt = arguments[0];
61 | if (util.isObject(opt) && !util.isFunction(opt)) {
62 | /** filter opt to make Date-Type SelectQuery Property corresponding item */
63 | filter_date(opt);
64 | }
65 | var rs = old_func.apply(this, Array.prototype.slice.apply(arguments));
66 | if (rs) {
67 | patchResult(rs);
68 | patchSync(rs, [
69 | "count",
70 | "first",
71 | "last",
72 | 'all',
73 | 'where',
74 | 'find',
75 | 'remove',
76 | 'run'
77 | ]);
78 | }
79 | return rs;
80 | };
81 | new_func.is_new = true;
82 | o.where = o.all = o.find = new_func;
83 | }
84 | function patchObject(m) {
85 | var methods = [
86 | "save",
87 | "remove",
88 | "validate",
89 | "model"
90 | ];
91 | function enum_associations(assoc) {
92 | assoc.forEach(function (item) {
93 | if (item.getAccessor)
94 | methods.push(item.getAccessor);
95 | if (item.setAccessor)
96 | methods.push(item.setAccessor);
97 | if (item.hasAccessor)
98 | methods.push(item.hasAccessor);
99 | if (item.delAccessor)
100 | methods.push(item.delAccessor);
101 | if (item.addAccessor)
102 | methods.push(item.addAccessor);
103 | });
104 | }
105 | // patch associations methods
106 | var opts = m.__opts;
107 | if (opts) {
108 | enum_associations(opts.one_associations);
109 | enum_associations(opts.many_associations);
110 | enum_associations(opts.extend_associations);
111 | enum_associations(opts.association_properties);
112 | for (var f in opts.fieldToPropertyMap) {
113 | if (opts.fieldToPropertyMap[f].lazyload) {
114 | var name = f.charAt(0).toUpperCase() + f.slice(1);
115 | methods.push('get' + name);
116 | methods.push('set' + name);
117 | methods.push('remove' + name);
118 | }
119 | }
120 | ;
121 | }
122 | patchSync(m, methods);
123 | }
124 | function patchHas(m, funcs) {
125 | funcs.forEach(function (func) {
126 | var old_func = m[func];
127 | if (old_func)
128 | m[func] = function () {
129 | var r = old_func.apply(this, Array.prototype.slice.apply(arguments));
130 | var name = arguments[0];
131 | name = 'findBy' + name.charAt(0).toUpperCase() + name.slice(1);
132 | patchSync(this, [name]);
133 | return r;
134 | };
135 | });
136 | }
137 | function patchAggregate(m) {
138 | var aggregate = m.aggregate;
139 | m.aggregate = function () {
140 | var r = aggregate.apply(this, Array.prototype.slice.apply(arguments));
141 | patchSync(r, ['get']);
142 | return r;
143 | };
144 | }
145 | function patchModel(m, opts) {
146 | var _afterAutoFetch;
147 | if (opts !== undefined && opts.hooks)
148 | _afterAutoFetch = opts.hooks.afterAutoFetch;
149 | m.afterAutoFetch(function (next) {
150 | patchObject(this);
151 | if (_afterAutoFetch) {
152 | if (_afterAutoFetch.length > 0)
153 | return _afterAutoFetch(next);
154 | _afterAutoFetch();
155 | }
156 | next();
157 | });
158 | patchResult(m);
159 | patchSync(m, [
160 | "clear",
161 | "count",
162 | "exists",
163 | "one",
164 | "where",
165 | 'all',
166 | 'create',
167 | 'drop',
168 | 'find',
169 | 'get',
170 | 'sync'
171 | ]);
172 | patchHas(m, [
173 | 'hasOne',
174 | 'extendsTo'
175 | ]);
176 | patchAggregate(m);
177 | }
178 | function patchInsert(table, data, keyProperties, cb) {
179 | var q = this.query.insert()
180 | .into(table)
181 | .set(data)
182 | .build();
183 | this.db.all(q, function (err, info) {
184 | if (err)
185 | return cb(err);
186 | if (!keyProperties)
187 | return cb(null);
188 | var i, ids = {}, prop;
189 | if (keyProperties.length == 1 && keyProperties[0].type == 'serial') {
190 | ids[keyProperties[0].name] = info.insertId;
191 | return cb(null, ids);
192 | }
193 | else {
194 | for (i = 0; i < keyProperties.length; i++) {
195 | prop = keyProperties[i];
196 | // Zero is a valid value for an ID column
197 | ids[prop.name] = data[prop.mapsTo] !== undefined ? data[prop.mapsTo] : null;
198 | }
199 | return cb(null, ids);
200 | }
201 | }.bind(this));
202 | }
203 | ;
204 | function patchDriver(driver) {
205 | if (driver.dialect === 'sqlite')
206 | driver.insert = patchInsert;
207 | var propertyToValue = driver.propertyToValue;
208 | driver.propertyToValue = function (value, property) {
209 | if (property.type === 'date' &&
210 | (util.isNumber(value) || util.isString(value)))
211 | value = new Date(value);
212 | return propertyToValue.call(this, value, property);
213 | };
214 | var valueToProperty = driver.valueToProperty;
215 | driver.valueToProperty = function (value, property) {
216 | if (property.type === 'date' &&
217 | (util.isNumber(value) || util.isString(value)))
218 | value = new Date(value);
219 | return valueToProperty.call(this, value, property);
220 | };
221 | }
222 | function execQuerySync(query, opt) {
223 | if (arguments.length == 2)
224 | query = this.query.escape(query, opt);
225 | return this.db.execute(query);
226 | }
227 | module.exports = function (orm) {
228 | var conn = util.sync(orm.connect);
229 | orm.connectSync = function (opts) {
230 | if (typeof opts == 'string')
231 | opts = url.parse(opts, true).toJSON();
232 | else if (typeof opts == 'object')
233 | opts = util.clone(opts);
234 | if (opts.protocol === 'sqlite:' && opts.timezone === undefined)
235 | opts.timezone = 'UTC';
236 | var db = conn.call(this, opts);
237 | patchSync(db, [
238 | 'sync',
239 | 'close',
240 | 'drop',
241 | 'ping'
242 | ]);
243 | patchDriver(db.driver);
244 | var def = db.define;
245 | db.define = function (name, properties, opts) {
246 | if (opts !== undefined) {
247 | opts = util.clone(opts);
248 | if (opts.hooks !== undefined)
249 | opts.hooks = util.clone(opts.hooks);
250 | }
251 | var m = def.call(this, name, properties, opts);
252 | patchModel(m, opts);
253 | return m;
254 | };
255 | db.begin = function () {
256 | return this.driver.db.conn.begin();
257 | };
258 | db.commit = function () {
259 | return this.driver.db.conn.commit();
260 | };
261 | db.rollback = function () {
262 | return this.driver.db.conn.rollback();
263 | };
264 | db.trans = function (func) {
265 | return this.driver.db.conn.trans(func);
266 | };
267 | db.driver.execQuerySync = execQuerySync;
268 | return db;
269 | };
270 | return orm;
271 | };
272 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "fib-orm",
3 | "version": "1.4.2",
4 | "description": "Object Relational Mapping for fibjs",
5 | "main": "lib/index.js",
6 | "types": "@types/index.d.ts",
7 | "repository": "git://github.com/fibjs/fib-orm.git",
8 | "author": "Liu Hu ",
9 | "homepage": "https://github.com/fibjs/fib-orm",
10 | "license": "MIT",
11 | "keywords": [
12 | "orm",
13 | "database",
14 | "fibjs"
15 | ],
16 | "files": [
17 | "@types",
18 | "lib"
19 | ],
20 | "scripts": {
21 | "ci": "fibjs test",
22 | "bundle": "fibjs ./build/bundle"
23 | },
24 | "ci": {
25 | "type": "travis",
26 | "version": [
27 | "0.21.0",
28 | "0.22.0",
29 | "0.23.0",
30 | "0.24.0"
31 | ]
32 | },
33 | "dependencies": {
34 | "orm": "^3.2.2"
35 | },
36 | "devDependencies": {
37 | "@fibjs/ci": "^2.0.0",
38 | "@types/fibjs": "github:fibjs/fib-types#v1.0.4",
39 | "fib-typify": "^0.1.0",
40 | "lodash": "^4.17.4"
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------
1 | import vm = require('vm')
2 | const patch = require('./patch');
3 |
4 | const sbox = new vm.SandBox({
5 | util: require('util'),
6 | events: require('events'),
7 | url: require('url'),
8 | sqlite3: require('./modules/sqlite3'),
9 | mysql: require('./modules/mysql')
10 | });
11 |
12 | const orm = sbox.require('orm', __filename);
13 |
14 | module.exports = patch(orm);
--------------------------------------------------------------------------------
/src/modules/index.d.ts:
--------------------------------------------------------------------------------
1 | interface DatabaseBase {
2 | on: (ev) => void;
3 | execute: (sql: string) => void;
4 |
5 | end?: (cb: Function) => void;
6 | close?: () => void;
7 | connect?: (cb: Function) => void
8 | query?: Function;
9 | }
--------------------------------------------------------------------------------
/src/modules/mysql.ts:
--------------------------------------------------------------------------------
1 | ///
2 | ///
3 |
4 | import db = require('db');
5 | import OrmNS from 'orm';
6 |
7 | class Database implements DatabaseBase {
8 | conn: OrmNS.ConnInstanceInOrmConnDriverDB;
9 | opts: OrmNS.OrmConnectionOpts;
10 |
11 | constructor(connOpts) {
12 | this.opts = connOpts;
13 | }
14 |
15 | on(ev) {}
16 |
17 | ping(cb: Function) {
18 | setImmediate(cb);
19 | }
20 |
21 | connect(cb: Function): void {
22 | const that = this;
23 | const openMySQL: Function = db.openMySQL
24 |
25 | openMySQL(this.opts, function (e: Error, conn: OrmNS.ConnInstanceInOrmConnDriverDB) {
26 | if (!e)
27 | that.conn = conn;
28 | cb(e);
29 | });
30 | }
31 |
32 | query(sql: string, cb: Function) {
33 | this.conn.execute(sql, cb);
34 | }
35 |
36 | execute(sql: string) {
37 | return this.conn.execute(sql);
38 | }
39 |
40 | end(cb: Function) {
41 | this.conn.close(cb);
42 | }
43 | }
44 |
45 | export const createConnection = function (connOpts: OrmNS.OrmConnectionOpts) {
46 | return new Database(connOpts);
47 | };
48 |
--------------------------------------------------------------------------------
/src/modules/sqlite3.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import db = require('db');
4 | import OrmNS from 'orm';
5 |
6 | export class Database implements DatabaseBase {
7 | conn: OrmNS.ConnInstanceInOrmConnDriverDB;
8 |
9 | constructor(fname) {
10 | this.conn = db.openSQLite(fname);
11 | }
12 |
13 | on(ev) {}
14 |
15 | all(sql: string, cb: Function) {
16 | this.conn.execute(sql, cb);
17 | }
18 |
19 | get(sql: string, cb: Function) {
20 | this.all(sql, function (e, r) {
21 | if (e)
22 | cb(e);
23 | cb(e, r[0]);
24 | });
25 | }
26 |
27 | execute(sql: string) {
28 | return this.conn.execute(sql);
29 | }
30 |
31 | close() {
32 | this.conn.close();
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/src/patch.ts:
--------------------------------------------------------------------------------
1 | ///
2 |
3 | import OrmNS from 'orm'
4 | import SqlQueryNS from 'sqlquery'
5 |
6 | import * as util from 'util'
7 | import * as url from 'url'
8 |
9 | interface ModelFuncToPatch extends Function {
10 | is_new?: boolean;
11 | }
12 |
13 | type HashOfModelFuncNameToPath = string[];
14 |
15 | // patch async function to sync function
16 | function patchSync(
17 | o: OrmNS.FibOrmFixedModel | OrmNS.FibOrmFixedModelInstance | OrmNS.FibOrmDB,
18 | funcs: HashOfModelFuncNameToPath
19 | ) {
20 | funcs.forEach(function (func) {
21 | const old_func = o[func];
22 | if (old_func) {
23 | Object.defineProperty(o, func + 'Sync', {
24 | value: util.sync(old_func),
25 | writable: true
26 | });
27 | }
28 | })
29 | }
30 |
31 | // hook find, patch result
32 | function patchResult(o: OrmNS.FibOrmFixedModelInstance | OrmNS.FibOrmFixedModel): void {
33 | var old_func: ModelFuncToPatch = o.find;
34 | var m: OrmNS.FibOrmFixedModel = o.model || o;
35 | var comps = ['val', 'from', 'to'];
36 |
37 | if (old_func.is_new)
38 | return;
39 |
40 | /**
41 | * filter the Date-Type SelectQuery Property corresponding item when call find-like executor ('find', 'get', 'where')
42 | * @param opt
43 | */
44 | function filter_date(opt) {
45 | for (var k in opt) {
46 | if (k === 'or')
47 | opt[k].forEach(filter_date);
48 | else {
49 | var p = m.allProperties[k];
50 | if (p && p.type === 'date') {
51 | var v: any = opt[k];
52 |
53 | if (!util.isDate(v)) {
54 | if (util.isNumber(v) || util.isString(v))
55 | opt[k] = new Date(v);
56 | else if (util.isObject(v)) {
57 | comps.forEach(c => {
58 | var v1 = v[c];
59 |
60 | if (util.isArray(v1)) {
61 | v1.forEach((v2, i) => {
62 | if (!util.isDate(v2))
63 | v1[i] = new Date(v2);
64 | });
65 | } else if (v1 !== undefined && !util.isDate(v1)) {
66 | v[c] = new Date(v1);
67 | }
68 | });
69 | }
70 | }
71 | }
72 | }
73 | }
74 | }
75 |
76 | var new_func: ModelFuncToPatch = function () {
77 | var opt = arguments[0];
78 |
79 | if (util.isObject(opt) && !util.isFunction(opt)) {
80 | /** filter opt to make Date-Type SelectQuery Property corresponding item */
81 | filter_date(opt);
82 | }
83 |
84 | var rs: OrmNS.FibOrmFixedModelInstance = old_func.apply(this, Array.prototype.slice.apply(arguments));
85 | if (rs) {
86 | patchResult(rs);
87 | patchSync(rs, [
88 | "count",
89 | "first",
90 | "last",
91 | 'all',
92 | 'where',
93 | 'find',
94 | 'remove',
95 | 'run'
96 | ]);
97 | }
98 | return rs;
99 | }
100 |
101 | new_func.is_new = true;
102 | o.where = o.all = o.find = new_func;
103 | }
104 |
105 | function patchObject(m: OrmNS.FibOrmFixedModelInstance) {
106 | var methods = [
107 | "save",
108 | "remove",
109 | "validate",
110 | "model"
111 | ];
112 |
113 | function enum_associations(assoc: OrmNS.InstanceAssociationItem[]) {
114 | assoc.forEach(function (item) {
115 | if (item.getAccessor)
116 | methods.push(item.getAccessor);
117 | if (item.setAccessor)
118 | methods.push(item.setAccessor);
119 | if (item.hasAccessor)
120 | methods.push(item.hasAccessor);
121 | if (item.delAccessor)
122 | methods.push(item.delAccessor);
123 | if (item.addAccessor)
124 | methods.push(item.addAccessor);
125 | });
126 | }
127 |
128 | // patch associations methods
129 | var opts = m.__opts;
130 | if (opts) {
131 | enum_associations(opts.one_associations);
132 | enum_associations(opts.many_associations);
133 | enum_associations(opts.extend_associations);
134 | enum_associations(opts.association_properties);
135 |
136 |
137 | for (var f in opts.fieldToPropertyMap) {
138 | if (opts.fieldToPropertyMap[f].lazyload) {
139 | var name = f.charAt(0).toUpperCase() + f.slice(1);
140 | methods.push('get' + name);
141 | methods.push('set' + name);
142 | methods.push('remove' + name);
143 | }
144 | };
145 | }
146 |
147 | patchSync(m, methods);
148 | }
149 |
150 | function patchHas(m: OrmNS.FibOrmFixedModel, funcs: HashOfModelFuncNameToPath) {
151 | funcs.forEach(function (func) {
152 | var old_func: ModelFuncToPatch = m[func];
153 | if (old_func)
154 | m[func] = function () {
155 | var r = old_func.apply(this, Array.prototype.slice.apply(arguments));
156 |
157 | var name = arguments[0];
158 | name = 'findBy' + name.charAt(0).toUpperCase() + name.slice(1);
159 | patchSync(this, [name]);
160 |
161 | return r;
162 | }
163 | })
164 | }
165 |
166 | function patchAggregate(m: OrmNS.FibOrmFixedModel) {
167 | var aggregate: OrmNS.OrigAggreteGenerator = m.aggregate;
168 | m.aggregate = function () {
169 | var r = aggregate.apply(this, Array.prototype.slice.apply(arguments));
170 | patchSync(r, ['get']);
171 | return r;
172 | };
173 | }
174 |
175 | function patchModel(m: OrmNS.FibOrmFixedModel, opts: OrmNS.ModelOptions) {
176 | var _afterAutoFetch;
177 | if (opts !== undefined && opts.hooks)
178 | _afterAutoFetch = opts.hooks.afterAutoFetch;
179 |
180 | m.afterAutoFetch(function (next) {
181 | patchObject(this as OrmNS.FibOrmFixedModelInstance);
182 |
183 | if (_afterAutoFetch) {
184 | if (_afterAutoFetch.length > 0)
185 | return _afterAutoFetch(next);
186 | _afterAutoFetch();
187 | }
188 |
189 | next();
190 | });
191 |
192 | patchResult(m);
193 |
194 | patchSync(m, [
195 | "clear",
196 | "count",
197 | "exists",
198 | "one",
199 | "where",
200 | 'all',
201 | 'create',
202 | 'drop',
203 | 'find',
204 | 'get',
205 | 'sync'
206 | ]);
207 |
208 | patchHas(m, [
209 | 'hasOne',
210 | 'extendsTo'
211 | ]);
212 |
213 | patchAggregate(m);
214 | }
215 |
216 | interface keyPropertiesTypeItem {
217 | type: string;
218 | name: string;
219 | }
220 | function patchInsert(table: string, data: any, keyProperties: keyPropertiesTypeItem[], cb: Function) {
221 | var q = this.query.insert()
222 | .into(table)
223 | .set(data)
224 | .build();
225 |
226 | this.db.all(q, function (err, info) {
227 | if (err) return cb(err);
228 | if (!keyProperties) return cb(null);
229 |
230 | var i, ids = {},
231 | prop;
232 |
233 | if (keyProperties.length == 1 && keyProperties[0].type == 'serial') {
234 | ids[keyProperties[0].name] = info.insertId;
235 | return cb(null, ids);
236 | } else {
237 | for (i = 0; i < keyProperties.length; i++) {
238 | prop = keyProperties[i];
239 | // Zero is a valid value for an ID column
240 | ids[prop.name] = data[prop.mapsTo] !== undefined ? data[prop.mapsTo] : null;
241 | }
242 | return cb(null, ids);
243 | }
244 | }.bind(this));
245 | };
246 |
247 | function patchDriver(driver: OrmNS.OrigOrmConnDriver) {
248 | if (driver.dialect === 'sqlite')
249 | driver.insert = patchInsert;
250 |
251 | var propertyToValue = driver.propertyToValue;
252 | driver.propertyToValue = function (value, property) {
253 | if (property.type === 'date' &&
254 | (util.isNumber(value) || util.isString(value)))
255 | value = new Date(value);
256 | return propertyToValue.call(this, value, property);
257 | }
258 |
259 | var valueToProperty = driver.valueToProperty;
260 | driver.valueToProperty = function (value, property) {
261 | if (property.type === 'date' &&
262 | (util.isNumber(value) || util.isString(value)))
263 | value = new Date(value);
264 | return valueToProperty.call(this, value, property);
265 | }
266 | }
267 |
268 | function execQuerySync(query: SqlQueryNS.Query, opt) {
269 | if (arguments.length == 2)
270 | query = this.query.escape(query, opt);
271 |
272 | return this.db.execute(query);
273 | }
274 |
275 | module.exports = function (orm: typeof OrmNS) {
276 | var conn = util.sync(orm.connect);
277 | orm.connectSync = function (opts: OrmNS.FibORMIConnectionOptions) {
278 | if (typeof opts == 'string')
279 | opts = url.parse(opts, true).toJSON();
280 | else if (typeof opts == 'object')
281 | opts = util.clone(opts);
282 |
283 | if (opts.protocol === 'sqlite:' && opts.timezone === undefined)
284 | opts.timezone = 'UTC';
285 |
286 | var db: OrmNS.FibOrmDB = conn.call(this, opts);
287 |
288 | patchSync(db, [
289 | 'sync',
290 | 'close',
291 | 'drop',
292 | 'ping'
293 | ]);
294 |
295 | patchDriver(db.driver);
296 |
297 | var def = db.define;
298 | db.define = function (name: string, properties: OrmNS.Property, opts: OrmNS.ModelOptions) {
299 | if (opts !== undefined) {
300 | opts = util.clone(opts);
301 | if (opts.hooks !== undefined)
302 | opts.hooks = util.clone(opts.hooks);
303 | }
304 |
305 | var m: OrmNS.FibOrmFixedModel = def.call(this, name, properties, opts);
306 | patchModel(m, opts);
307 | return m;
308 | }
309 |
310 | db.begin = function () {
311 | return this.driver.db.conn.begin();
312 | };
313 |
314 | db.commit = function () {
315 | return this.driver.db.conn.commit();
316 | };
317 |
318 | db.rollback = function () {
319 | return this.driver.db.conn.rollback();
320 | };
321 |
322 | db.trans = function (func) {
323 | return this.driver.db.conn.trans(func);
324 | };
325 |
326 | db.driver.execQuerySync = execQuerySync;
327 |
328 | return db;
329 | }
330 |
331 | return orm;
332 | }
333 |
--------------------------------------------------------------------------------
/test/index.js:
--------------------------------------------------------------------------------
1 | #!/usr/local/bin/fibjs
2 |
3 | var test = require("test");
4 | test.setup();
5 |
6 | function t() {
7 | run('./integration/association-extend.js');
8 | run('./integration/association-hasmany-extra.js');
9 | run('./integration/association-hasmany-hooks.js');
10 | run('./integration/association-hasmany.js');
11 | run('./integration/association-hasone.js');
12 | run('./integration/association-hasone-required.js');
13 | run('./integration/association-hasone-reverse.js');
14 | run('./integration/association-hasone-zeroid.js');
15 | run('./integration/event.js');
16 |
17 | run('./integration/hook.js');
18 |
19 | run('./integration/instance.js');
20 | run('./integration/model-aggregate.js');
21 | run('./integration/model-clear.js');
22 | run('./integration/model-count.js');
23 | run('./integration/model-create.js');
24 | run('./integration/model-exists.js');
25 | run('./integration/model-find-chain.js');
26 | run('./integration/model-find-mapsto.js');
27 | run('./integration/model-find.js');
28 | run('./integration/model-get.js');
29 | run('./integration/model-keys.js');
30 | run('./integration/model-one.js');
31 | run('./integration/model-save.js');
32 | run('./integration/model-sync.js');
33 |
34 | run('./integration/predefined-validators.js');
35 |
36 | run('./integration/property-custom.js');
37 | run('./integration/property-lazyload.js');
38 | run('./integration/property-maps-to.js');
39 | run('./integration/property.js');
40 |
41 | run('./integration/settings.js');
42 |
43 | run('./integration/smart-types.js');
44 |
45 | run('./integration/validation.js');
46 |
47 | run('./integration/date-type.js');
48 |
49 | test.run();
50 | }
51 |
52 | t();
--------------------------------------------------------------------------------
/test/integration/association-extend.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.extendsTo()", function () {
5 | var db = null;
6 | var Person = null;
7 | var PersonAddress = null;
8 |
9 | var setup = function () {
10 | return function () {
11 | Person = db.define("person", {
12 | name: String
13 | });
14 | PersonAddress = Person.extendsTo("address", {
15 | street: String,
16 | number: Number
17 | });
18 |
19 | ORM.singleton.clear();
20 |
21 | helper.dropSync([Person, PersonAddress], function () {
22 | var person = Person.createSync({
23 | name: "John Doe"
24 | });
25 | person.setAddressSync(new PersonAddress({
26 | street: "Liberty",
27 | number: 123
28 | }));
29 | });
30 | };
31 | };
32 |
33 | before(function () {
34 | db = helper.connect();
35 | });
36 |
37 | after(function () {
38 | return db.closeSync();
39 | });
40 |
41 | describe("when calling hasAccessor", function () {
42 | before(setup());
43 |
44 | it("should return true if found", function () {
45 | var John = Person.find().firstSync();
46 | var hasAddress = John.hasAddressSync();
47 | assert.equal(hasAddress, true);
48 | });
49 |
50 | it("should return false if not found", function () {
51 | var John = Person.find().firstSync();
52 |
53 | John.removeAddressSync();
54 | assert.throws(function () {
55 | John.hasAddressSync();
56 | })
57 | });
58 |
59 | it("should return error if instance not with an ID", function () {
60 | var Jane = new Person({
61 | name: "Jane"
62 | });
63 |
64 | try {
65 | Jane.hasAddressSync();
66 | } catch (err) {
67 | assert.propertyVal(err, "code", ORM.ErrorCodes.NOT_DEFINED);
68 | }
69 | });
70 | });
71 |
72 | describe("when calling getAccessor", function () {
73 | before(setup());
74 |
75 | it("should return extension if found", function () {
76 | var John = Person.find().firstSync();
77 | var Address = John.getAddressSync();
78 | assert.isObject(Address);
79 | assert.propertyVal(Address, "street", "Liberty");
80 | });
81 |
82 | it("should return error if not found", function () {
83 | var John = Person.find().firstSync();
84 |
85 | John.removeAddressSync();
86 |
87 | try {
88 | John.getAddressSync();
89 | } catch (err) {
90 | assert.propertyVal(err, "code", ORM.ErrorCodes.NOT_FOUND);
91 | }
92 | });
93 |
94 | it("should return error if instance not with an ID", function () {
95 | var Jane = new Person({
96 | name: "Jane"
97 | });
98 |
99 | try {
100 | Jane.getAddressSync();
101 | } catch (err) {
102 | assert.propertyVal(err, "code", ORM.ErrorCodes.NOT_DEFINED);
103 | }
104 | });
105 | });
106 |
107 | describe("when calling setAccessor", function () {
108 | before(setup());
109 |
110 | it("should remove any previous extension", function () {
111 | var John = Person.find().firstSync();
112 |
113 | var c = PersonAddress.find({
114 | number: 123
115 | }).countSync();
116 |
117 | assert.equal(c, 1);
118 |
119 | var addr = new PersonAddress({
120 | street: "4th Ave",
121 | number: 4
122 | });
123 |
124 | John.setAddressSync(addr);
125 |
126 | var Address = John.getAddressSync();
127 |
128 | assert.isObject(Address);
129 | assert.propertyVal(Address, "street", addr.street);
130 |
131 | var c = PersonAddress.find({
132 | number: 123
133 | }).countSync();
134 |
135 | assert.equal(c, 0);
136 | });
137 | });
138 |
139 | describe("when calling delAccessor", function () {
140 | before(setup());
141 |
142 | it("should remove any extension", function () {
143 | var John = Person.find().firstSync();
144 |
145 | var c = PersonAddress.find({
146 | number: 123
147 | }).countSync();
148 | assert.equal(c, 1);
149 |
150 | var addr = new PersonAddress({
151 | street: "4th Ave",
152 | number: 4
153 | });
154 |
155 | John.removeAddressSync();
156 |
157 | var c = PersonAddress.find({
158 | number: 123
159 | }).countSync();
160 | assert.equal(c, 0);
161 | });
162 |
163 | it("should return error if instance not with an ID", function () {
164 | var Jane = new Person({
165 | name: "Jane"
166 | });
167 | try {
168 | Jane.removeAddressSync();
169 | } catch (err) {
170 | assert.propertyVal(err, "code", ORM.ErrorCodes.NOT_DEFINED);
171 | }
172 | });
173 | });
174 |
175 | describe("findBy()", function () {
176 | before(setup());
177 |
178 | it("should throw if no conditions passed", function () {
179 | assert.throws(function () {
180 | Person.findByAddressSync();
181 | });
182 | });
183 |
184 | it("should lookup in Model based on associated model properties", function () {
185 | var people = Person.findByAddressSync({
186 | number: 123
187 | });
188 |
189 | assert.ok(Array.isArray(people));
190 | assert.ok(people.length == 1);
191 | });
192 |
193 | it("should return a ChainFind if no callback passed", function () {
194 | var ChainFind = Person.findByAddress({
195 | number: 123
196 | });
197 | assert.isFunction(ChainFind.run);
198 | });
199 | });
200 | });
--------------------------------------------------------------------------------
/test/integration/association-hasmany-extra.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("hasMany extra properties", function () {
5 | var db = null;
6 | var Person = null;
7 | var Pet = null;
8 |
9 | var setup = function (opts) {
10 | opts = opts || {};
11 | return function () {
12 | db.settings.set('instance.identityCache', false);
13 |
14 | Person = db.define('person', {
15 | name: String,
16 | }, opts);
17 | Pet = db.define('pet', {
18 | name: String
19 | });
20 | Person.hasMany('pets', Pet, {
21 | since: Date,
22 | data: Object
23 | });
24 |
25 | return helper.dropSync([Person, Pet]);
26 | };
27 | };
28 |
29 | before(function () {
30 | db = helper.connect();
31 | });
32 |
33 | after(function () {
34 | db.closeSync();
35 | });
36 |
37 | describe("if passed to addAccessor", function () {
38 | before(setup());
39 |
40 | it("should be added to association", function () {
41 | var people = Person.createSync([{
42 | name: "John"
43 | }]);
44 |
45 | var pets = Pet.createSync([{
46 | name: "Deco"
47 | }, {
48 | name: "Mutt"
49 | }]);
50 |
51 | var data = {
52 | adopted: true
53 | };
54 |
55 | people[0].addPetsSync(pets, {
56 | since: new Date(),
57 | data: data
58 | });
59 |
60 | var John = Person.find({
61 | name: "John"
62 | }, {
63 | autoFetch: true
64 | }).firstSync();
65 |
66 | assert.property(John, "pets");
67 | assert.ok(Array.isArray(pets));
68 |
69 | assert.equal(John.pets.length, 2);
70 |
71 | assert.property(John.pets[0], "name");
72 | assert.property(John.pets[0], "extra");
73 | assert.isObject(John.pets[0].extra);
74 | assert.property(John.pets[0].extra, "since");
75 | assert.ok(John.pets[0].extra.since instanceof Date);
76 |
77 | assert.equal(typeof John.pets[0].extra.data, 'object');
78 | assert.equal(JSON.stringify(data), JSON.stringify(John.pets[0].extra.data));
79 | });
80 | });
81 | });
--------------------------------------------------------------------------------
/test/integration/association-hasmany-hooks.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("hasMany hooks", function () {
5 | var db = null;
6 | var Person = null;
7 | var Pet = null;
8 |
9 | var setup = function (props, opts) {
10 | return function () {
11 | db.settings.set('instance.identityCache', false);
12 |
13 | Person = db.define('person', {
14 | name: String,
15 | });
16 | Pet = db.define('pet', {
17 | name: String
18 | });
19 | Person.hasMany('pets', Pet, props || {}, opts || {});
20 |
21 | helper.dropSync([Person, Pet]);
22 | };
23 | };
24 |
25 | before(function () {
26 | db = helper.connect();
27 | });
28 |
29 | after(function () {
30 | return db.closeSync();
31 | });
32 |
33 | describe("beforeSave", function () {
34 | var had_extra = false;
35 |
36 | before(setup({
37 | born: Date
38 | }, {
39 | hooks: {
40 | beforeSave: function (extra, next) {
41 | had_extra = (typeof extra == "object");
42 | return next();
43 | }
44 | }
45 | }));
46 |
47 | it("should pass extra data to hook if extra defined", function () {
48 | var John = Person.createSync({
49 | name: "John"
50 | });
51 | var Deco = Pet.createSync({
52 | name: "Deco"
53 | });
54 |
55 | John.addPetsSync(Deco);
56 | assert.isTrue(had_extra);
57 | });
58 | });
59 |
60 | describe("beforeSave", function () {
61 | var had_extra = false;
62 |
63 | before(setup({}, {
64 | hooks: {
65 | beforeSave: function (next) {
66 | assert.isFunction(next);
67 | return next();
68 | }
69 | }
70 | }));
71 |
72 | it("should not pass extra data to hook if extra defined", function () {
73 | var John = Person.createSync({
74 | name: "John"
75 | });
76 | var Deco = Pet.createSync({
77 | name: "Deco"
78 | });
79 | John.addPetsSync(Deco);
80 | });
81 | });
82 |
83 | describe("beforeSave", function () {
84 | var had_extra = false;
85 |
86 | before(setup({}, {
87 | hooks: {
88 | beforeSave: function (next) {
89 | setTimeout(function () {
90 | return next(new Error('blocked'));
91 | }, 100);
92 | }
93 | }
94 | }));
95 |
96 | it("should block if error returned", function () {
97 | var John = Person.createSync({
98 | name: "John"
99 | });
100 | var Deco = Pet.createSync({
101 | name: "Deco"
102 | });
103 |
104 | try {
105 | John.addPetsSync(Deco);
106 | } catch (err) {
107 | assert.equal(err.message, 'blocked');
108 | }
109 | });
110 | });
111 | });
--------------------------------------------------------------------------------
/test/integration/association-hasone-required.js:
--------------------------------------------------------------------------------
1 | var ORM = require('../../');
2 | var helper = require('../support/spec_helper');
3 |
4 | describe("hasOne", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var setup = function (required) {
9 | return function () {
10 | db.settings.set('instance.identityCache', false);
11 | db.settings.set('instance.returnAllErrors', true);
12 |
13 | Person = db.define('person', {
14 | name: String
15 | });
16 | Person.hasOne('parent', Person, {
17 | required: required,
18 | field: 'parentId'
19 | });
20 |
21 | helper.dropSync(Person);
22 | };
23 | };
24 |
25 | before(function () {
26 | db = helper.connect();
27 | });
28 |
29 | after(function () {
30 | return db.closeSync();
31 | });
32 |
33 | describe("required", function () {
34 | before(setup(true));
35 |
36 | it("should not accept empty association", function () {
37 | var John = new Person({
38 | name: "John",
39 | parentId: null
40 | });
41 | try {
42 | John.saveSync();
43 | } catch (errors) {
44 | assert.equal(errors.length, 1);
45 | assert.equal(errors[0].type, 'validation');
46 | assert.equal(errors[0].msg, 'required');
47 | assert.equal(errors[0].property, 'parentId');
48 | }
49 | });
50 |
51 | it("should accept association", function () {
52 | var John = new Person({
53 | name: "John",
54 | parentId: 1
55 | });
56 | John.saveSync();
57 | });
58 | });
59 |
60 | describe("not required", function () {
61 | before(setup(false));
62 |
63 | it("should accept empty association", function () {
64 | var John = new Person({
65 | name: "John"
66 | });
67 | John.saveSync();
68 | });
69 |
70 | it("should accept null association", function () {
71 | var John = new Person({
72 | name: "John",
73 | parent_id: null
74 | });
75 | John.saveSync();
76 | });
77 | });
78 | });
--------------------------------------------------------------------------------
/test/integration/association-hasone-reverse.js:
--------------------------------------------------------------------------------
1 | var ORM = require('../../');
2 | var helper = require('../support/spec_helper');
3 |
4 | describe("hasOne", function () {
5 | var db = null;
6 | var Person = null;
7 | var Pet = null;
8 |
9 | var setup = function () {
10 | return function () {
11 | Person = db.define('person', {
12 | name: String
13 | });
14 | Pet = db.define('pet', {
15 | name: String
16 | });
17 | Person.hasOne('pet', Pet, {
18 | reverse: 'owners',
19 | field: 'pet_id'
20 | });
21 |
22 | helper.dropSync([Person, Pet], function () {
23 | // Running in series because in-memory sqlite encounters problems
24 | Person.createSync({
25 | name: "John Doe"
26 | });
27 | Person.createSync({
28 | name: "Jane Doe"
29 | });
30 | Pet.createSync({
31 | name: "Deco"
32 | });
33 | Pet.createSync({
34 | name: "Fido"
35 | });
36 | });
37 | };
38 | };
39 |
40 | before(function () {
41 | db = helper.connect();
42 | });
43 |
44 | after(function () {
45 | return db.closeSync();
46 | });
47 |
48 | describe("reverse", function () {
49 | var removeHookRun = false;
50 |
51 | before(setup({
52 | hooks: {
53 | beforeRemove: function () {
54 | removeHookRun = true;
55 | }
56 | }
57 | }));
58 |
59 | it("should create methods in both models", function () {
60 | var person = Person(1);
61 | var pet = Pet(1);
62 |
63 | assert.isFunction(person.getPet);
64 | assert.isFunction(person.setPet);
65 | assert.isFunction(person.removePet);
66 | assert.isFunction(person.hasPet);
67 |
68 | assert.isFunction(pet.getOwners);
69 | assert.isFunction(pet.setOwners);
70 | assert.isFunction(pet.hasOwners);
71 | });
72 |
73 | describe(".getAccessor()", function () {
74 | it("should work", function () {
75 | var John = Person.find({
76 | name: "John Doe"
77 | }).firstSync();
78 | var Deco = Pet.find({
79 | name: "Deco"
80 | }).firstSync();
81 | var has_owner = Deco.hasOwnersSync();
82 | assert.isFalse(has_owner);
83 |
84 | Deco.setOwnersSync(John);
85 |
86 | var JohnCopy = Deco.getOwnersSync();
87 | assert.ok(Array.isArray(JohnCopy));
88 | assert.deepEqual(John, JohnCopy[0]);
89 | });
90 |
91 | describe("Chain", function () {
92 | before(function () {
93 | var petParams = [{
94 | name: "Hippo"
95 | },
96 | {
97 | name: "Finch",
98 | owners: [{
99 | name: "Harold"
100 | }, {
101 | name: "Hagar"
102 | }]
103 | },
104 | {
105 | name: "Fox",
106 | owners: [{
107 | name: "Nelly"
108 | }, {
109 | name: "Narnia"
110 | }]
111 | }
112 | ];
113 |
114 | var pets = Pet.createSync(petParams);
115 | assert.equal(pets.length, 3);
116 |
117 | var people = Person.findSync({
118 | name: ["Harold", "Hagar", "Nelly", "Narnia"]
119 | });
120 | assert.exist(people);
121 | assert.equal(people.length, 4);
122 | });
123 |
124 | it("should be returned if no callback is passed", function () {
125 | var pet = Pet.oneSync();
126 | assert.exist(pet);
127 |
128 | var chain = pet.getOwners();
129 |
130 | assert.equal(typeof chain, 'object');
131 | assert.equal(typeof chain.run, 'function');
132 | });
133 |
134 | it(".remove() should not call hooks", function () {
135 | var pet = Pet.oneSync({
136 | name: "Finch"
137 | });
138 | assert.exist(pet);
139 |
140 | assert.equal(removeHookRun, false);
141 | pet.getOwners().removeSync();
142 | assert.equal(removeHookRun, false);
143 |
144 | var items = Person.findSync({
145 | name: "Harold"
146 | });
147 | assert.equal(items.length, 0);
148 | });
149 |
150 | });
151 | });
152 |
153 | it("should be able to set an array of people as the owner", function () {
154 | var owners = Person.findSync({
155 | name: ["John Doe", "Jane Doe"]
156 | });
157 |
158 | var Fido = Pet.find({
159 | name: "Fido"
160 | }).firstSync();
161 |
162 | var has_owner = Fido.hasOwnersSync();
163 | assert.isFalse(has_owner);
164 |
165 | Fido.setOwnersSync(owners);
166 |
167 | var ownersCopy = Fido.getOwnersSync();
168 | assert.ok(Array.isArray(owners));
169 | assert.equal(owners.length, 2);
170 |
171 | // Don't know which order they'll be in.
172 | var idProp = 'id'
173 |
174 | if (owners[0][idProp] == ownersCopy[0][idProp]) {
175 | assert.deepEqual(owners[0], ownersCopy[0]);
176 | assert.deepEqual(owners[1], ownersCopy[1]);
177 | } else {
178 | assert.deepEqual(owners[0], ownersCopy[1]);
179 | assert.deepEqual(owners[1], ownersCopy[0]);
180 | }
181 |
182 | });
183 |
184 | // broken in mongo
185 | describe("findBy()", function () {
186 | before(setup());
187 |
188 | before(function () {
189 | var jane = Person.oneSync({
190 | name: "Jane Doe"
191 | });
192 | var deco = Pet.oneSync({
193 | name: "Deco"
194 | });
195 | deco.setOwnersSync(jane);
196 | });
197 |
198 | it("should throw if no conditions passed", function () {
199 | assert.throws(function () {
200 | Pet.findByOwners(function () {});
201 | });
202 | });
203 |
204 | it("should lookup reverse Model based on associated model properties", function () {
205 | var pets = Pet.findByOwnersSync({
206 | name: "Jane Doe"
207 | });
208 | assert.equal(Array.isArray(pets), true);
209 | });
210 |
211 | it("should return a ChainFind if no callback passed", function () {
212 | var ChainFind = Pet.findByOwners({
213 | name: "John Doe"
214 | });
215 | assert.isFunction(ChainFind.run);
216 | });
217 | });
218 | });
219 |
220 | xdescribe("reverse find", function () {
221 | it("should be able to find given an association id", function () {
222 | common.retry(setup(), function () {
223 | Person.find({
224 | name: "John Doe"
225 | }).first(function (err, John) {
226 | assert.notExist(err);
227 | assert.exist(John);
228 | Pet.find({
229 | name: "Deco"
230 | }).first(function (err, Deco) {
231 | assert.notExist(err);
232 | assert.exist(Deco);
233 | Deco.hasOwners(function (err, has_owner) {
234 | assert.notExist(err);
235 | assert.isFalse(has_owner);
236 |
237 | Deco.setOwners(John, function (err) {
238 | assert.notExist(err);
239 |
240 | Person.find({
241 | pet_id: Deco[Pet.id[0]]
242 | }).first(function (err, owner) {
243 | assert.notExist(err);
244 | assert.exist(owner);
245 | assert.equal(owner.name, John.name);
246 | done();
247 | });
248 |
249 | });
250 | });
251 | });
252 | });
253 | }, 3);
254 | });
255 |
256 | xit("should be able to find given an association instance", function () {
257 | common.retry(setup(), function () {
258 | Person.find({
259 | name: "John Doe"
260 | }).first(function (err, John) {
261 | assert.notExist(err);
262 | assert.exist(John);
263 | Pet.find({
264 | name: "Deco"
265 | }).first(function (err, Deco) {
266 | assert.notExist(err);
267 | assert.exist(Deco);
268 | Deco.hasOwners(function (err, has_owner) {
269 | assert.notExist(err);
270 | assert.isFalse(has_owner);
271 |
272 | Deco.setOwners(John, function (err) {
273 | assert.notExist(err);
274 |
275 | Person.find({
276 | pet: Deco
277 | }).first(function (err, owner) {
278 | assert.notExist(err);
279 | assert.exist(owner);
280 | assert.equal(owner.name, John.name);
281 | done();
282 | });
283 |
284 | });
285 | });
286 | });
287 | });
288 | }, 3);
289 | });
290 |
291 | xit("should be able to find given a number of association instances with a single primary key", function () {
292 | common.retry(setup(), function () {
293 | Person.find({
294 | name: "John Doe"
295 | }).first(function (err, John) {
296 | assert.notExist(err);
297 | assert.exist(John);
298 | Pet.all(function (err, pets) {
299 | assert.notExist(err);
300 | assert.exist(pets);
301 | assert.equal(pets.length, 2);
302 |
303 | pets[0].hasOwners(function (err, has_owner) {
304 | assert.notExist(err);
305 | assert.isFalse(has_owner);
306 |
307 | pets[0].setOwners(John, function (err) {
308 | assert.notExist(err);
309 |
310 | Person.find({
311 | pet: pets
312 | }, function (err, owners) {
313 | assert.notExist(err);
314 | assert.exist(owners);
315 | assert.equal(owners.length, 1);
316 |
317 | assert.equal(owners[0].name, John.name);
318 | done();
319 | });
320 | });
321 | });
322 | });
323 | });
324 | }, 3);
325 | });
326 | });
327 | });
--------------------------------------------------------------------------------
/test/integration/association-hasone-zeroid.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("hasOne", function () {
5 | var db = null;
6 | var Person = null;
7 | var Pet = null;
8 |
9 | var setup = function (autoFetch) {
10 | return function () {
11 | db.settings.set('instance.identityCache', false);
12 | db.settings.set('instance.returnAllErrors', true);
13 |
14 | Person = db.define('person', {
15 | id: {
16 | type: "integer",
17 | mapsTo: "personID",
18 | key: true
19 | },
20 | firstName: {
21 | type: "text",
22 | size: "255"
23 | },
24 | lastName: {
25 | type: "text",
26 | size: "255"
27 | }
28 | });
29 |
30 | Pet = db.define('pet', {
31 | id: {
32 | type: "integer",
33 | mapsTo: "petID",
34 | key: true
35 | },
36 | petName: {
37 | type: "text",
38 | size: "255"
39 | },
40 | ownerID: {
41 | type: "integer",
42 | size: "4"
43 | }
44 | });
45 |
46 | Pet.hasOne('owner', Person, {
47 | field: 'ownerID',
48 | autoFetch: autoFetch
49 | });
50 |
51 | helper.dropSync([Person, Pet], function () {
52 | Pet.createSync([{
53 | id: 10,
54 | petName: 'Muttley',
55 | owner: {
56 | id: 12,
57 | firstName: 'Stuey',
58 | lastName: 'McG'
59 | }
60 | }, {
61 | id: 11,
62 | petName: 'Snagglepuss',
63 | owner: {
64 | id: 0,
65 | firstName: 'John',
66 | lastName: 'Doe'
67 | }
68 | }]);
69 | });
70 | };
71 | };
72 |
73 | before(function () {
74 | db = helper.connect();
75 | });
76 |
77 | after(function () {
78 | return db.closeSync();
79 | });
80 |
81 | describe("auto fetch", function () {
82 | before(setup(true));
83 |
84 | it("should work for non-zero ownerID ", function () {
85 | var pets = Pet.findSync({
86 | petName: "Muttley"
87 | });
88 |
89 | assert.equal(pets[0].petName, "Muttley");
90 | assert.property(pets[0], "id");
91 | assert.equal(pets[0].id, 10);
92 | assert.equal(pets[0].ownerID, 12);
93 |
94 | assert.property(pets[0], "owner");
95 | assert.equal(pets[0].owner.firstName, "Stuey");
96 | });
97 |
98 | it("should work for zero ownerID ", function () {
99 | var pets = Pet.findSync({
100 | petName: "Snagglepuss"
101 | });
102 |
103 | assert.equal(pets[0].petName, "Snagglepuss");
104 | assert.property(pets[0], "id");
105 | assert.equal(pets[0].id, 11);
106 |
107 | var people = db.models.person.allSync();
108 | });
109 | });
110 |
111 | describe("no auto fetch", function () {
112 | before(setup(false));
113 |
114 | it("should work for non-zero ownerID ", function () {
115 | var pets = Pet.findSync({
116 | petName: "Muttley"
117 | });
118 |
119 | assert.equal(pets[0].petName, "Muttley");
120 | assert.property(pets[0], "id");
121 | assert.equal(pets[0].id, 10);
122 | assert.equal(pets[0].ownerID, 12);
123 |
124 | assert.notProperty(pets[0], "owner");
125 |
126 | // But we should be able to see if its there
127 | var result = pets[0].hasOwnerSync();
128 | assert.equal(result, true);
129 |
130 | // ...and then get it
131 | var result = pets[0].getOwnerSync();
132 | assert.equal(result.firstName, "Stuey");
133 | });
134 |
135 | it("should work for zero ownerID ", function () {
136 | var pets = Pet.findSync({
137 | petName: "Snagglepuss"
138 | });
139 |
140 | assert.equal(pets[0].petName, "Snagglepuss");
141 | assert.property(pets[0], "id");
142 | assert.equal(pets[0].id, 11);
143 | assert.equal(pets[0].ownerID, 0);
144 |
145 | assert.notProperty(pets[0], "owner");
146 |
147 | // But we should be able to see if its there
148 | var result = pets[0].hasOwnerSync();
149 | assert.equal(result, true);
150 |
151 | // ...and then get it
152 | result = pets[0].getOwnerSync();
153 | assert.equal(result.firstName, "John");
154 | });
155 | });
156 | });
--------------------------------------------------------------------------------
/test/integration/association-hasone.js:
--------------------------------------------------------------------------------
1 | var ORM = require('../../');
2 | var helper = require('../support/spec_helper');
3 | var _ = require('lodash');
4 |
5 | describe("hasOne", function () {
6 | var db = null;
7 | var Tree = null;
8 | var Stalk = null;
9 | var Leaf = null;
10 | var leafId = null;
11 | var treeId = null;
12 | var stalkId = null;
13 | var holeId = null;
14 |
15 | var setup = function (opts) {
16 | opts = opts || {};
17 | return function () {
18 | db.settings.set('instance.identityCache', false);
19 | db.settings.set('instance.returnAllErrors', true);
20 | Tree = db.define("tree", {
21 | type: {
22 | type: 'text'
23 | }
24 | });
25 | Stalk = db.define("stalk", {
26 | length: {
27 | type: 'integer'
28 | }
29 | });
30 | var Hole = db.define("hole", {
31 | width: {
32 | type: 'integer'
33 | }
34 | });
35 | Leaf = db.define("leaf", {
36 | size: {
37 | type: 'integer'
38 | },
39 | holeId: {
40 | type: 'integer',
41 | mapsTo: 'hole_id'
42 | }
43 | }, {
44 | validations: opts.validations
45 | });
46 | Leaf.hasOne('tree', Tree, {
47 | field: 'treeId',
48 | autoFetch: !!opts.autoFetch
49 | });
50 | Leaf.hasOne('stalk', Stalk, {
51 | field: 'stalkId',
52 | mapsTo: 'stalk_id'
53 | });
54 | Leaf.hasOne('hole', Hole, {
55 | field: 'holeId'
56 | });
57 |
58 | helper.dropSync([Tree, Stalk, Hole, Leaf], function () {
59 | var tree = Tree.createSync({
60 | type: 'pine'
61 | });
62 | treeId = tree[Tree.id];
63 |
64 | var leaf = Leaf.createSync({
65 | size: 14
66 | });
67 | leafId = leaf[Leaf.id];
68 | leaf.setTreeSync(tree);
69 |
70 | var stalk = Stalk.createSync({
71 | length: 20
72 | });
73 | assert.exist(stalk);
74 | stalkId = stalk[Stalk.id];
75 |
76 | var hole = Hole.createSync({
77 | width: 3
78 | });
79 | holeId = hole.id;
80 | });
81 | };
82 | };
83 |
84 | before(function () {
85 | db = helper.connect();
86 | });
87 |
88 | after(function () {
89 | db.closeSync();
90 | });
91 |
92 | describe("accessors", function () {
93 | before(setup());
94 |
95 | it("get should get the association", function () {
96 | var leaf = Leaf.oneSync({
97 | size: 14
98 | });
99 | assert.exist(leaf);
100 | var tree = leaf.getTreeSync();
101 | assert.exist(tree);
102 | });
103 |
104 | it("should return proper instance model", function () {
105 | var leaf = Leaf.oneSync({
106 | size: 14
107 | });
108 | var tree = leaf.getTreeSync();
109 | assert.equal(tree.model(), Tree);
110 | });
111 |
112 | it("get should get the association with a shell model", function () {
113 | var tree = Leaf(leafId).getTreeSync();
114 | assert.exist(tree);
115 | assert.equal(tree[Tree.id], treeId);
116 | });
117 |
118 | it("has should indicate if there is an association present", function () {
119 | var leaf = Leaf.oneSync({
120 | size: 14
121 | });
122 | assert.exist(leaf);
123 |
124 | var has = leaf.hasTreeSync();
125 | assert.equal(has, true);
126 |
127 | has = leaf.hasStalkSync();
128 | assert.equal(has, false);
129 | });
130 |
131 | it("set should associate another instance", function () {
132 | var stalk = Stalk.oneSync({
133 | length: 20
134 | });
135 | assert.exist(stalk);
136 |
137 | var leaf = Leaf.oneSync({
138 | size: 14
139 | });
140 | assert.exist(leaf);
141 | assert.notExist(leaf.stalkId);
142 |
143 | leaf.setStalkSync(stalk);
144 |
145 | var leaf = Leaf.oneSync({
146 | size: 14
147 | });
148 | assert.equal(leaf.stalkId, stalk[Stalk.id]);
149 | });
150 |
151 | it("remove should unassociation another instance", function () {
152 | var stalk = Stalk.oneSync({
153 | length: 20
154 | });
155 | assert.exist(stalk);
156 | var leaf = Leaf.oneSync({
157 | size: 14
158 | });
159 | assert.exist(leaf);
160 | assert.exist(leaf.stalkId);
161 | leaf.removeStalkSync();
162 | var leaf = Leaf.oneSync({
163 | size: 14
164 | });
165 | assert.equal(leaf.stalkId, null);
166 | });
167 | });
168 |
169 | [false, true].forEach(function (af) {
170 | describe("with autofetch = " + af, function () {
171 | before(setup({
172 | autoFetch: af
173 | }));
174 |
175 | describe("autofetching", function () {
176 | it((af ? "should" : "shouldn't") + " be done", function () {
177 | var leaf = Leaf.oneSync({});
178 | assert.equal(typeof leaf.tree, af ? 'object' : 'undefined');
179 | });
180 | });
181 |
182 | describe("associating by parent id", function () {
183 | var tree = null;
184 |
185 | before(function () {
186 | tree = Tree.createSync({
187 | type: "cyprus"
188 | });
189 | });
190 |
191 | it("should work when calling Instance.save", function () {
192 | var leaf = new Leaf({
193 | size: 4,
194 | treeId: tree[Tree.id]
195 | });
196 | leaf.saveSync();
197 |
198 | var fetchedLeaf = Leaf.getSync(leaf[Leaf.id]);
199 | assert.exist(fetchedLeaf);
200 | assert.equal(fetchedLeaf.treeId, leaf.treeId);
201 | });
202 |
203 | it("should work when calling Instance.save after initially setting parentId to null", function () {
204 | var leaf = new Leaf({
205 | size: 4,
206 | treeId: null
207 | });
208 | leaf.treeId = tree[Tree.id];
209 | leaf.saveSync();
210 |
211 | var fetchedLeaf = Leaf.getSync(leaf[Leaf.id]);
212 | assert.exist(fetchedLeaf);
213 | assert.equal(fetchedLeaf.treeId, leaf.treeId);
214 | });
215 |
216 | it("should work when specifying parentId in the save call", function () {
217 | var leaf = new Leaf({
218 | size: 4
219 | });
220 | leaf.saveSync({
221 | treeId: tree[Tree.id]
222 | });
223 |
224 | assert.exist(leaf.treeId);
225 |
226 | var fetchedLeaf = Leaf.getSync(leaf[Leaf.id]);
227 | assert.exist(fetchedLeaf);
228 | assert.equal(fetchedLeaf.treeId, leaf.treeId);
229 | });
230 |
231 | it("should work when calling Model.create", function () {
232 | var leaf = Leaf.createSync({
233 | size: 4,
234 | treeId: tree[Tree.id]
235 | });
236 |
237 | var fetchedLeaf = Leaf.getSync(leaf[Leaf.id]);
238 |
239 | assert.exist(fetchedLeaf);
240 | assert.equal(fetchedLeaf.treeId, leaf.treeId);
241 | });
242 |
243 | it("shouldn't cause an infinite loop when getting and saving with no changes", function () {
244 | var leaf = Leaf.getSync(leafId);
245 | leaf.saveSync();
246 | });
247 |
248 | it("shouldn't cause an infinite loop when getting and saving with changes", function () {
249 | var leaf = Leaf.getSync(leafId);
250 | leaf.saveSync({
251 | size: 14
252 | });
253 | });
254 | });
255 | });
256 | });
257 |
258 | describe("validations", function () {
259 | before(setup({
260 | validations: {
261 | stalkId: ORM.validators.rangeNumber(undefined, 50)
262 | }
263 | }));
264 |
265 | it("should allow validating parentId", function () {
266 | var leaf = Leaf.oneSync({
267 | size: 14
268 | });
269 | assert.exist(leaf);
270 |
271 | try {
272 | leaf.saveSync({
273 | stalkId: 51
274 | });
275 | } catch (err) {
276 | assert.ok(Array.isArray(err));
277 | assert.equal(err.length, 1);
278 | assert.equal(err[0].msg, 'out-of-range-number');
279 | }
280 | });
281 | });
282 |
283 | describe("if not passing another Model", function () {
284 | it("should use same model", function () {
285 | db.settings.set('instance.identityCache', false);
286 | db.settings.set('instance.returnAllErrors', true);
287 |
288 | var Person = db.define("person", {
289 | name: String
290 | });
291 | Person.hasOne("parent", {
292 | autoFetch: true
293 | });
294 |
295 | helper.dropSync(Person, function () {
296 | var child = new Person({
297 | name: "Child"
298 | });
299 | child.setParentSync(new Person({
300 | name: "Parent"
301 | }));
302 | });
303 | });
304 | });
305 |
306 | describe("association name letter case", function () {
307 | it("should be kept", function () {
308 | db.settings.set('instance.identityCache', false);
309 | db.settings.set('instance.returnAllErrors', true);
310 |
311 | var Person = db.define("person", {
312 | name: String
313 | });
314 | Person.hasOne("topParent", Person);
315 |
316 | helper.dropSync(Person, function () {
317 | var person = Person.createSync({
318 | name: "Child"
319 | });
320 |
321 | person = Person.getSync(person[Person.id]);
322 |
323 | assert.isFunction(person.setTopParent);
324 | assert.isFunction(person.removeTopParent);
325 | assert.isFunction(person.hasTopParent);
326 | });
327 | });
328 | });
329 |
330 | describe("findBy()", function () {
331 | before(setup());
332 |
333 | it("should throw if no conditions passed", function () {
334 | assert.throws(function () {
335 | Leaf.findByTreeSync();
336 | });
337 | });
338 |
339 | it("should lookup in Model based on associated model properties", function () {
340 | var leafs = Leaf.findByTreeSync({
341 | type: "pine"
342 | });
343 |
344 | assert.ok(Array.isArray(leafs));
345 | assert.ok(leafs.length == 1);
346 | });
347 |
348 | it("should return a ChainFind if no callback passed", function () {
349 | var ChainFind = Leaf.findByTree({
350 | type: "pine"
351 | });
352 | assert.isFunction(ChainFind.run);
353 | assert.isFunction(ChainFind.runSync);
354 | });
355 | });
356 |
357 |
358 | describe("mapsTo", function () {
359 | describe("with `mapsTo` set via `hasOne`", function () {
360 | var leaf = null;
361 |
362 | before(setup());
363 |
364 | before(function () {
365 | var lf = Leaf.createSync({
366 | size: 444,
367 | stalkId: stalkId,
368 | holeId: holeId
369 | });
370 | leaf = lf;
371 | });
372 |
373 | it("should have correct fields in the DB", function () {
374 | var sql = db.driver.query.select()
375 | .from('leaf')
376 | .select('size', 'stalk_id')
377 | .where({
378 | size: 444
379 | })
380 | .build();
381 |
382 | var rows = db.driver.execQuerySync(sql);
383 |
384 | assert.equal(rows[0].size, 444);
385 | assert.equal(rows[0].stalk_id, 1);
386 | });
387 |
388 | it("should get parent", function () {
389 | var stalk = leaf.getStalkSync();
390 |
391 | assert.exist(stalk);
392 | assert.equal(stalk.id, stalkId);
393 | assert.equal(stalk.length, 20);
394 | });
395 | });
396 |
397 | describe("with `mapsTo` set via property definition", function () {
398 | var leaf = null;
399 |
400 | before(setup());
401 |
402 | before(function () {
403 | var lf = Leaf.createSync({
404 | size: 444,
405 | stalkId: stalkId,
406 | holeId: holeId
407 | });
408 | leaf = lf;
409 | });
410 |
411 | it("should have correct fields in the DB", function () {
412 | var sql = db.driver.query.select()
413 | .from('leaf')
414 | .select('size', 'hole_id')
415 | .where({
416 | size: 444
417 | })
418 | .build();
419 |
420 | var rows = db.driver.execQuerySync(sql);
421 |
422 | assert.equal(rows[0].size, 444);
423 | assert.equal(rows[0].hole_id, 1);
424 | });
425 |
426 | it("should get parent", function () {
427 | var hole = leaf.getHoleSync();
428 |
429 | assert.exist(hole);
430 | assert.equal(hole.id, stalkId);
431 | assert.equal(hole.width, 3);
432 | });
433 | });
434 | });
435 | });
--------------------------------------------------------------------------------
/test/integration/date-type.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 | var util = require('util');
4 |
5 | describe("Date Type", function () {
6 | var db = null;
7 | var Person = null;
8 |
9 | var setup = function (hooks) {
10 | return function () {
11 | Person = db.define("person", {
12 | name: {
13 | type: "text",
14 | required: true
15 | },
16 | birthday: Date
17 | });
18 |
19 | return helper.dropSync(Person);
20 | };
21 | };
22 |
23 | before(function () {
24 | db = helper.connect();
25 | });
26 |
27 | after(function () {
28 | return db.closeSync();
29 | });
30 |
31 | describe("opt", function () {
32 | before(setup());
33 |
34 | it("insert", function () {
35 | var John = Person.createSync({
36 | name: "John Doe",
37 | birthday: '1971-08-28T00:00:00Z'
38 | });
39 |
40 | var who = Person.oneSync({
41 | name: "John Doe"
42 | });
43 |
44 | assert.equal(who.birthday.getTime(),
45 | new Date('1971-08-28T00:00:00Z').getTime());
46 | });
47 |
48 | it("update", function () {
49 | var John = Person.oneSync({
50 | name: "John Doe"
51 | });
52 |
53 | John.birthday = '1971-08-29T00:00:00Z';
54 | John.saveSync();
55 |
56 | var who = Person.oneSync({
57 | name: "John Doe"
58 | });
59 |
60 | assert.equal(who.birthday.getTime(),
61 | new Date('1971-08-29T00:00:00Z').getTime());
62 | });
63 |
64 | it("find", function () {
65 | var who = Person.oneSync({
66 | birthday: '1971-08-29T00:00:00Z'
67 | });
68 |
69 | assert.equal(who.name, 'John Doe');
70 | });
71 | });
72 | });
--------------------------------------------------------------------------------
/test/integration/event.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Event", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var triggeredHooks = {};
9 |
10 | var checkHook = function (hook) {
11 | triggeredHooks[hook] = false;
12 |
13 | return function () {
14 | triggeredHooks[hook] = Date.now();
15 | };
16 | };
17 |
18 | var setup = function (hooks) {
19 | return function () {
20 | Person = db.define("person", {
21 | name: {
22 | type: "text",
23 | required: true
24 | }
25 | });
26 |
27 | return helper.dropSync(Person);
28 | };
29 | };
30 |
31 | before(function () {
32 | db = helper.connect();
33 | });
34 |
35 | after(function () {
36 | return db.closeSync();
37 | });
38 |
39 | describe("save", function () {
40 | before(setup());
41 |
42 | it("should trigger when saving an instance", function () {
43 | var triggered = false;
44 | var John = new Person({
45 | name: "John Doe"
46 | });
47 |
48 | John.on("save", function () {
49 | triggered = true;
50 | });
51 |
52 | assert.isFalse(triggered);
53 |
54 | John.saveSync();
55 | assert.isTrue(triggered);
56 | });
57 |
58 | it("should trigger when saving an instance even if it fails", function () {
59 | var triggered = false;
60 | var John = new Person();
61 |
62 | John.on("save", function (err) {
63 | triggered = true;
64 |
65 | assert.isObject(err);
66 | assert.propertyVal(err, "msg", "required");
67 | });
68 |
69 | assert.isFalse(triggered);
70 |
71 | assert.throws(function () {
72 | John.saveSync();
73 | })
74 |
75 | assert.isTrue(triggered);
76 | });
77 |
78 | it("should be writable for mocking", function () {
79 | var triggered = false;
80 | var John = new Person();
81 |
82 | John.on = function (event, cb) {
83 | triggered = true;
84 | };
85 | assert.isFalse(triggered);
86 |
87 | John.on("mocked", function (err) {});
88 | assert.isTrue(triggered);
89 | });
90 | });
91 | });
--------------------------------------------------------------------------------
/test/integration/instance.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model instance", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var setup = function () {
9 | db.settings.set('instance.returnAllErrors', true);
10 |
11 | Person = db.define("person", {
12 | name: String,
13 | age: {
14 | type: 'integer',
15 | required: false
16 | },
17 | height: {
18 | type: 'integer',
19 | required: false
20 | },
21 | weight: {
22 | type: 'number',
23 | required: false,
24 | enumerable: true
25 | },
26 | secret: {
27 | type: 'text',
28 | required: false,
29 | enumerable: false
30 | },
31 | data: {
32 | type: 'object',
33 | required: false
34 | }
35 | }, {
36 | identityCache: false,
37 | validations: {
38 | age: ORM.validators.rangeNumber(0, 150)
39 | }
40 | });
41 |
42 | helper.dropSync(Person, function () {
43 | Person.createSync([{
44 | name: "Jeremy Doe"
45 | }, {
46 | name: "John Doe"
47 | }, {
48 | name: "Jane Doe"
49 | }]);
50 | });
51 | };
52 |
53 | before(function () {
54 | db = helper.connect();
55 | setup();
56 | });
57 |
58 | after(function () {
59 | return db.closeSync();
60 | });
61 |
62 | describe("#save", function () {
63 | var main_item, item;
64 |
65 | before(function () {
66 | main_item = db.define("main_item", {
67 | name: String
68 | }, {
69 | auteFetch: true
70 | });
71 |
72 | item = db.define("item", {
73 | name: String
74 | }, {
75 | identityCache: false
76 | });
77 |
78 | item.hasOne("main_item", main_item, {
79 | reverse: "items",
80 | autoFetch: true
81 | });
82 |
83 | helper.dropSync([main_item, item], function () {
84 | var mainItem = main_item.createSync({
85 | name: "Main Item"
86 | });
87 |
88 | var Item = item.createSync({
89 | name: "Item"
90 | });
91 |
92 | var r = mainItem.setItemsSync(Item);
93 | });
94 | });
95 |
96 | it("should have a saving state to avoid loops", function () {
97 | var mainItem = main_item.find({
98 | name: "Main Item"
99 | }).firstSync();
100 | mainItem.saveSync({
101 | name: "new name"
102 | });
103 | });
104 | });
105 |
106 | describe("#isInstance", function () {
107 | it("should always return true for instances", function () {
108 | assert.equal((new Person).isInstance, true);
109 | assert.equal((Person(4)).isInstance, true);
110 |
111 | var item = Person.find().firstSync();
112 | assert.equal(item.isInstance, true);
113 | });
114 |
115 | it("should be false for all other objects", function () {
116 | assert.notEqual({}.isInstance, true);
117 | assert.notEqual([].isInstance, true);
118 | });
119 | });
120 |
121 | describe("#isPersisted", function () {
122 | it("should return true for persisted instances", function () {
123 | var item = Person.find().firstSync();
124 | assert.equal(item.isPersisted(), true);
125 | });
126 |
127 | it("should return true for shell instances", function () {
128 | assert.equal(Person(4).isPersisted(), true);
129 | });
130 |
131 | it("should return false for new instances", function () {
132 | assert.equal((new Person).isPersisted(), false);
133 | });
134 |
135 | it("should be writable for mocking", function () {
136 | var person = new Person()
137 | var triggered = false;
138 | person.isPersisted = function () {
139 | triggered = true;
140 | };
141 | person.isPersisted()
142 | assert.isTrue(triggered);
143 | });
144 | });
145 |
146 | describe("#set", function () {
147 | var person = null;
148 | var data = null;
149 |
150 | function clone(obj) {
151 | return JSON.parse(JSON.stringify(obj))
152 | };
153 |
154 | beforeEach(function () {
155 | data = {
156 | a: {
157 | b: {
158 | c: 3,
159 | d: 4
160 | }
161 | },
162 | e: 5
163 | };
164 | var p = Person.createSync({
165 | name: 'Dilbert',
166 | data: data
167 | });
168 | person = p;
169 | });
170 |
171 | it("should do nothing with flat paths when setting to same value", function () {
172 | assert.equal(person.saved(), true);
173 | person.set('name', 'Dilbert');
174 | assert.equal(person.name, 'Dilbert');
175 | assert.equal(person.saved(), true);
176 | });
177 |
178 | it("should mark as dirty with flat paths when setting to different value", function () {
179 | assert.equal(person.saved(), true);
180 | person.set('name', 'Dogbert');
181 | assert.equal(person.name, 'Dogbert');
182 | assert.equal(person.saved(), false);
183 | assert.equal(person.__opts.changes.join(','), 'name');
184 | });
185 |
186 | it("should do nothin with deep paths when setting to same value", function () {
187 | assert.equal(person.saved(), true);
188 | person.set('data.e', 5);
189 |
190 | var expected = clone(data);
191 | expected.e = 5;
192 |
193 | assert.equal(JSON.stringify(person.data), JSON.stringify(expected));
194 | assert.equal(person.saved(), true);
195 | });
196 |
197 | it("should mark as dirty with deep paths when setting to different value", function () {
198 | assert.equal(person.saved(), true);
199 | person.set('data.e', 6);
200 |
201 | var expected = clone(data);
202 | expected.e = 6;
203 |
204 | assert.equal(JSON.stringify(person.data), JSON.stringify(expected));
205 | assert.equal(person.saved(), false);
206 | assert.equal(person.__opts.changes.join(','), 'data');
207 | });
208 |
209 | it("should do nothing with deeper paths when setting to same value", function () {
210 | assert.equal(person.saved(), true);
211 | person.set('data.a.b.d', 4);
212 |
213 | var expected = clone(data);
214 | expected.a.b.d = 4;
215 |
216 | assert.equal(JSON.stringify(person.data), JSON.stringify(expected));
217 | assert.equal(person.saved(), true);
218 | });
219 |
220 | it("should mark as dirty with deeper paths when setting to different value", function () {
221 | assert.equal(person.saved(), true);
222 | person.set('data.a.b.d', 6);
223 |
224 | var expected = clone(data);
225 | expected.a.b.d = 6;
226 |
227 | assert.equal(JSON.stringify(person.data), JSON.stringify(expected));
228 | assert.equal(person.saved(), false);
229 | assert.equal(person.__opts.changes.join(','), 'data');
230 | });
231 |
232 | it("should mark as dirty with array path when setting to different value", function () {
233 | assert.equal(person.saved(), true);
234 | person.set(['data', 'a', 'b', 'd'], 6);
235 |
236 | var expected = clone(data);
237 | expected.a.b.d = 6;
238 |
239 | assert.equal(JSON.stringify(person.data), JSON.stringify(expected));
240 | assert.equal(person.saved(), false);
241 | assert.equal(person.__opts.changes.join(','), 'data');
242 | });
243 |
244 | it("should do nothing with invalid paths", function () {
245 | assert.equal(person.saved(), true);
246 | person.set('data.a.b.d.y.z', 1);
247 | person.set('data.y.z', 1);
248 | person.set('z', 1);
249 | person.set(4, 1);
250 | person.set(null, 1);
251 | person.set(undefined, 1);
252 | assert.equal(person.saved(), true);
253 | });
254 | });
255 |
256 | describe("#markAsDirty", function () {
257 | var person = null;
258 |
259 | beforeEach(function () {
260 | var p = Person.createSync({
261 | name: 'John',
262 | age: 44,
263 | data: {
264 | a: 1
265 | }
266 | });
267 | person = p;
268 | });
269 |
270 | it("should mark individual properties as dirty", function () {
271 | assert.equal(person.saved(), true);
272 | person.markAsDirty('name');
273 | assert.equal(person.saved(), false);
274 | assert.equal(person.__opts.changes.join(','), 'name');
275 | person.markAsDirty('data');
276 | assert.equal(person.__opts.changes.join(','), 'name,data');
277 | });
278 | });
279 |
280 | describe("#dirtyProperties", function () {
281 | var person = null;
282 |
283 | beforeEach(function () {
284 | var p = Person.createSync({
285 | name: 'John',
286 | age: 44,
287 | data: {
288 | a: 1
289 | }
290 | });
291 | person = p;
292 | });
293 |
294 | it("should mark individual properties as dirty", function () {
295 | assert.equal(person.saved(), true);
296 | person.markAsDirty('name');
297 | person.markAsDirty('data');
298 | assert.equal(person.saved(), false);
299 | assert.equal(person.dirtyProperties.join(','), 'name,data');
300 | });
301 | });
302 |
303 | describe("#isShell", function () {
304 | it("should return true for shell models", function () {
305 | assert.equal(Person(4).isShell(), true);
306 | });
307 |
308 | it("should return false for new models", function () {
309 | assert.equal((new Person).isShell(), false);
310 | });
311 |
312 | it("should return false for existing models", function () {
313 | var item = Person.find().firstSync();
314 | assert.equal(item.isShell(), false);
315 | });
316 | });
317 |
318 | describe("#validate", function () {
319 | it("should return validation errors if invalid", function () {
320 | var person = new Person({
321 | age: -1
322 | });
323 |
324 | var validationErrors = person.validateSync();
325 | assert.equal(Array.isArray(validationErrors), true);
326 | });
327 |
328 | it("should return false if valid", function () {
329 | var person = new Person({
330 | name: 'Janette'
331 | });
332 |
333 | var validationErrors = person.validateSync();
334 | assert.equal(validationErrors, false);
335 | });
336 | });
337 |
338 | describe("properties", function () {
339 | describe("Number", function () {
340 | it("should be saved for valid numbers, using both save & create", function () {
341 | var person1 = new Person({
342 | height: 190
343 | });
344 |
345 | person1.saveSync();
346 |
347 | var person2 = Person.createSync({
348 | height: 170
349 | });
350 |
351 | var item = Person.getSync(person1[Person.id]);
352 |
353 | assert.equal(item.height, 190);
354 |
355 | item = Person.getSync(person2[Person.id]);
356 | assert.equal(item.height, 170);
357 | });
358 | });
359 |
360 | describe("Enumerable", function () {
361 | it("should not stringify properties marked as not enumerable", function () {
362 | var p = Person.createSync({
363 | name: 'Dilbert',
364 | secret: 'dogbert',
365 | weight: 100,
366 | data: {
367 | data: 3
368 | }
369 | });
370 |
371 | var result = JSON.parse(JSON.stringify(p));
372 | assert.notExist(result.secret);
373 | assert.exist(result.weight);
374 | assert.exist(result.data);
375 | assert.exist(result.name);
376 | });
377 | });
378 | });
379 | });
--------------------------------------------------------------------------------
/test/integration/model-aggregate.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.aggregate()", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var setup = function () {
9 | return function () {
10 | Person = db.define("person", {
11 | name: String
12 | });
13 |
14 | helper.dropSync(Person, function () {
15 | Person.createSync([{
16 | id: 1,
17 | name: "John Doe"
18 | }, {
19 | id: 2,
20 | name: "Jane Doe"
21 | }, {
22 | id: 3,
23 | name: "John Doe"
24 | }]);
25 | });
26 | };
27 | };
28 |
29 | before(function () {
30 | db = helper.connect();
31 | });
32 |
33 | after(function () {
34 | return db.closeSync();
35 | });
36 |
37 | xdescribe("with multiple methods", function () {
38 | before(setup());
39 |
40 | it("should return value for everyone of them", function () {
41 | Person.aggregate().count('id').min('id').max('id').get(function (err, count, min, max) {
42 | assert.equal(err, null);
43 |
44 | assert.equal(count, 3);
45 | assert.equal(min, 1);
46 | assert.equal(max, 3);
47 | });
48 | });
49 | });
50 |
51 | describe("with call()", function () {
52 | before(setup());
53 |
54 | it("should accept a function", function () {
55 | var count = Person.aggregate().call('COUNT').getSync();
56 | assert.equal(count, 3);
57 | });
58 |
59 | it("should accept arguments to the funciton as an Array", function () {
60 | var count = Person.aggregate().call('COUNT', ['id']).getSync();
61 | assert.equal(count, 3);
62 | });
63 |
64 | describe("if function is DISTINCT", function () {
65 | it("should work as calling .distinct() directly", function () {
66 | var rows = Person.aggregate().call('DISTINCT', ['name']).as('name').order('name').getSync();
67 |
68 | assert.ok(Array.isArray(rows));
69 | assert.equal(rows.length, 2);
70 |
71 | assert.equal(rows[0], 'Jane Doe');
72 | assert.equal(rows[1], 'John Doe');
73 | });
74 | });
75 | });
76 |
77 | xdescribe("with as() without previous aggregates", function () {
78 | before(setup());
79 |
80 | it("should throw", function () {
81 | Person.aggregate().as.should.throw();
82 | });
83 | });
84 |
85 | xdescribe("with select() without arguments", function () {
86 | before(setup());
87 |
88 | it("should throw", function () {
89 | Person.aggregate().select.should.throw();
90 |
91 | return done();
92 | });
93 | });
94 |
95 | describe("with select() with arguments", function () {
96 | before(setup());
97 |
98 | it("should use them as properties if 1st argument is Array", function () {
99 | var people = Person.aggregate().select(['id']).count('id').groupBy('id').getSync();
100 |
101 | // assert.ok(Array.isArray(people));
102 | assert.greaterThan(people.length, 0);
103 |
104 | assert.isObject(people[0]);
105 | assert.property(people[0], "id");
106 | assert.notProperty(people[0], "name");
107 | });
108 |
109 | it("should use them as properties", function () {
110 | var people = Person.aggregate().select('id').count().groupBy('id').getSync();
111 | // assert.ok(Array.isArray(people));
112 | assert.greaterThan(people.length, 0);
113 |
114 | assert.isObject(people[0]);
115 | assert.property(people[0], "id");
116 | assert.notProperty(people[0], "name");
117 | });
118 | });
119 |
120 | xdescribe("with get() without callback", function () {
121 | before(setup());
122 |
123 | it("should throw", function () {
124 | Person.aggregate().count('id').get.should.throw();
125 |
126 | return done();
127 | });
128 | });
129 |
130 | describe("with get() without aggregates", function () {
131 | before(setup());
132 |
133 | it("should throw", function () {
134 | assert.throws(function () {
135 | Person.aggregate().get(function () {});
136 | });
137 | });
138 | });
139 |
140 | describe("with distinct()", function () {
141 | before(setup());
142 |
143 | it("should return a list of distinct properties", function () {
144 | var names = Person.aggregate().distinct('name').getSync();
145 | assert.isObject(names);
146 | assert.property(names, "length", 2);
147 | });
148 |
149 | describe("with limit(1)", function () {
150 | it("should return only one value", function () {
151 | var names = Person.aggregate().distinct('name').limit(1).order("name").getSync();
152 | assert.isObject(names);
153 | assert.property(names, "length", 1);
154 | assert.equal(names[0], "Jane Doe");
155 | });
156 | });
157 |
158 | describe("with limit(1, 1)", function () {
159 | it("should return only one value", function () {
160 | var names = Person.aggregate().distinct('name').limit(1, 1).order("name").getSync();
161 | assert.isObject(names);
162 | assert.property(names, "length", 1);
163 | assert.equal(names[0], "John Doe");
164 | });
165 | });
166 | });
167 |
168 | describe("with groupBy()", function () {
169 | before(setup());
170 |
171 | it("should return items grouped by property", function () {
172 | var rows = Person.aggregate().count().groupBy('name').getSync();
173 | assert.isObject(rows);
174 | assert.property(rows, "length", 2);
175 |
176 | assert.equal((rows[0].count + rows[1].count), 3); // 1 + 2
177 | });
178 |
179 | describe("with order()", function () {
180 | before(setup());
181 |
182 | it("should order items", function () {
183 | var rows = Person.aggregate().count().groupBy('name').order('-count').getSync();
184 | assert.isObject(rows);
185 | assert.property(rows, "length", 2);
186 |
187 | assert.equal(rows[0].count, 2);
188 | assert.equal(rows[1].count, 1);
189 | });
190 | });
191 | });
192 |
193 | describe("using as()", function () {
194 | before(setup());
195 |
196 | it("should use as an alias", function () {
197 | var people = Person.aggregate().count().as('total').groupBy('name').getSync();
198 | // assert.ok(Array.isArray(people));
199 | assert.greaterThan(people.length, 0);
200 |
201 | assert.isObject(people[0]);
202 | assert.property(people[0], "total");
203 | });
204 |
205 | it("should throw if no aggregates defined", function () {
206 | assert.throws(function () {
207 | Person.aggregate().as('total');
208 | });
209 | });
210 | });
211 | });
--------------------------------------------------------------------------------
/test/integration/model-clear.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.clear()", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var setup = function () {
9 | Person = db.define("person", {
10 | name: String
11 | });
12 |
13 | ORM.singleton.clear();
14 |
15 | return helper.dropSync(Person, function () {
16 | Person.createSync([{
17 | name: "John Doe"
18 | }, {
19 | name: "Jane Doe"
20 | }]);
21 | });
22 | };
23 |
24 | before(function () {
25 | db = helper.connect();
26 | });
27 |
28 | after(function () {
29 | db.closeSync();
30 | });
31 |
32 | describe("with sync", function () {
33 | before(setup);
34 |
35 | it("should call when done", function () {
36 | Person.clearSync();
37 |
38 | var count = Person.find().countSync();
39 | assert.equal(count, 0);
40 | });
41 | });
42 |
43 | });
--------------------------------------------------------------------------------
/test/integration/model-count.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.count()", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var setup = function () {
9 | Person = db.define("person", {
10 | name: String
11 | });
12 |
13 | return helper.dropSync(Person, function () {
14 | Person.createSync([{
15 | id: 1,
16 | name: "John Doe"
17 | }, {
18 | id: 2,
19 | name: "Jane Doe"
20 | }, {
21 | id: 3,
22 | name: "John Doe"
23 | }]);
24 | });
25 | };
26 |
27 | before(function () {
28 | db = helper.connect();
29 | });
30 |
31 | after(function () {
32 | return db.closeSync();
33 | });
34 |
35 | describe("without conditions", function () {
36 | before(setup);
37 |
38 | it("should return all items in model", function () {
39 | var count = Person.countSync();
40 | assert.equal(count, 3);
41 | });
42 | });
43 |
44 | describe("with conditions", function () {
45 | before(setup);
46 |
47 | it("should return only matching items", function () {
48 | var count = Person.countSync({
49 | name: "John Doe"
50 | });
51 | assert.equal(count, 2);
52 | });
53 | });
54 | });
--------------------------------------------------------------------------------
/test/integration/model-create.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.create()", function () {
5 | var db = null;
6 | var Pet = null;
7 | var Person = null;
8 |
9 | function setup() {
10 | Person = db.define("person", {
11 | name: String
12 | });
13 | Pet = db.define("pet", {
14 | name: {
15 | type: "text",
16 | defaultValue: "Mutt"
17 | }
18 | });
19 | Person.hasMany("pets", Pet);
20 |
21 | Person.dropSync();
22 | Pet.dropSync();
23 |
24 | db.syncSync();
25 | };
26 |
27 | before(function () {
28 | db = helper.connect();
29 | });
30 |
31 | after(function () {
32 | db.closeSync();
33 | });
34 |
35 | describe("if passing an object", function () {
36 | before(setup);
37 |
38 | it("should accept it as the only item to create", function () {
39 | var John = Person.createSync({
40 | name: "John Doe"
41 | });
42 |
43 | assert.propertyVal(John, "name", "John Doe");
44 | });
45 | });
46 |
47 | describe("if passing an array", function () {
48 | before(setup);
49 |
50 | it("should accept it as a list of items to create", function () {
51 | var people = Person.createSync([{
52 | name: "John Doe"
53 | }, {
54 | name: "Jane Doe"
55 | }]);
56 |
57 | assert.ok(Array.isArray(people));
58 |
59 | assert.propertyVal(people, "length", 2);
60 | assert.propertyVal(people[0], "name", "John Doe");
61 | assert.propertyVal(people[1], "name", "Jane Doe");
62 | });
63 | });
64 |
65 | describe("if element has an association", function () {
66 | before(setup);
67 |
68 | it("should also create it or save it", function () {
69 | var John = Person.createSync({
70 | name: "John Doe",
71 | pets: [new Pet({
72 | name: "Deco"
73 | })]
74 | });
75 |
76 | assert.propertyVal(John, "name", "John Doe");
77 |
78 | assert.ok(Array.isArray(John.pets));
79 |
80 | assert.propertyVal(John.pets[0], "name", "Deco");
81 | assert.property(John.pets[0], Pet.id[0]);
82 | assert.ok(John.pets[0].saved());
83 | });
84 |
85 | it("should also create it or save it even if it's an object and not an instance", function () {
86 | var John = Person.createSync({
87 | name: "John Doe",
88 | pets: [{
89 | name: "Deco"
90 | }]
91 | });
92 |
93 | assert.propertyVal(John, "name", "John Doe");
94 |
95 | assert.ok(Array.isArray(John.pets));
96 |
97 | assert.propertyVal(John.pets[0], "name", "Deco");
98 | assert.property(John.pets[0], Pet.id[0]);
99 | assert.ok(John.pets[0].saved());
100 | });
101 | });
102 |
103 | describe("when not passing a property", function () {
104 | before(setup);
105 |
106 | it("should use defaultValue if defined", function () {
107 | var Mutt = Pet.createSync({});
108 | assert.propertyVal(Mutt, "name", "Mutt");
109 | });
110 | });
111 | });
--------------------------------------------------------------------------------
/test/integration/model-exists.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.exists()", function () {
5 | var db = null;
6 | var Person = null;
7 | var good_id, bad_id;
8 |
9 | var setup = function () {
10 | return function () {
11 | Person = db.define("person", {
12 | name: String
13 | });
14 |
15 | return helper.dropSync(Person, function () {
16 | var people = Person.createSync([{
17 | name: "Jeremy Doe"
18 | }, {
19 | name: "John Doe"
20 | }, {
21 | name: "Jane Doe"
22 | }]);
23 | good_id = people[0][Person.id];
24 |
25 | if (typeof good_id == "number") {
26 | // numeric ID
27 | bad_id = good_id * 100;
28 | } else {
29 | // string ID, keep same length..
30 | bad_id = good_id.split('').reverse().join('');
31 | }
32 | });
33 | };
34 | };
35 |
36 | before(function () {
37 | db = helper.connect();
38 | });
39 |
40 | after(function () {
41 | db.closeSync();
42 | });
43 |
44 | describe("with an id", function () {
45 | before(setup());
46 |
47 | it("should return true if found", function () {
48 | var exists = Person.existsSync(good_id);
49 | assert.ok(exists);
50 | });
51 |
52 | it("should return false if not found", function () {
53 | var exists = Person.existsSync(bad_id);
54 | assert.notOk(exists);
55 | });
56 | });
57 |
58 | describe("with a list of ids", function () {
59 | before(setup());
60 |
61 | it("should return true if found", function () {
62 | var exists = Person.existsSync([good_id]);
63 | assert.ok(exists);
64 | });
65 |
66 | it("should return false if not found", function () {
67 | var exists = Person.existsSync([bad_id]);
68 | assert.notOk(exists);
69 | });
70 | });
71 |
72 | describe("with a conditions object", function () {
73 | before(setup());
74 |
75 | it("should return true if found", function () {
76 | var exists = Person.existsSync({
77 | name: "John Doe"
78 | });
79 | assert.ok(exists);
80 | });
81 |
82 | it("should return false if not found", function () {
83 | var exists = Person.existsSync({
84 | name: "Jack Doe"
85 | });
86 | assert.notOk(exists);
87 | });
88 | });
89 | });
--------------------------------------------------------------------------------
/test/integration/model-find-mapsto.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.pkMapTo.find()", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var setup = function () {
9 | return function () {
10 |
11 | // The fact that we've applied mapsTo to the key
12 | // property of the model - will break the cache.
13 |
14 | // Without Stuart's little bugfix, 2nd (and subsequent) calls to find()
15 | // will return the repeats of the first obect retrieved and placed in the cache.
16 | Person = db.define("person", {
17 | personId: {
18 | type: "integer",
19 | key: true,
20 | mapsTo: "id"
21 | },
22 | name: String,
23 | surname: String,
24 | age: Number,
25 | male: Boolean
26 | });
27 |
28 | return helper.dropSync(Person, function () {
29 | Person.createSync([{
30 | personId: 1001,
31 | name: "John",
32 | surname: "Doe",
33 | age: 18,
34 | male: true
35 | }, {
36 | personId: 1002,
37 | name: "Jane",
38 | surname: "Doe",
39 | age: 16,
40 | male: false
41 | }, {
42 | personId: 1003,
43 | name: "Jeremy",
44 | surname: "Dean",
45 | age: 18,
46 | male: true
47 | }, {
48 | personId: 1004,
49 | name: "Jack",
50 | surname: "Dean",
51 | age: 20,
52 | male: true
53 | }, {
54 | personId: 1005,
55 | name: "Jasmine",
56 | surname: "Doe",
57 | age: 20,
58 | male: false
59 | }]);
60 | });
61 | };
62 | };
63 |
64 | before(function () {
65 | db = helper.connect();
66 | });
67 |
68 | after(function () {
69 | return db.closeSync();
70 | });
71 |
72 |
73 | describe("Cache should work with mapped key field", function () {
74 | before(setup());
75 |
76 | it("1st find should work", function () {
77 | var people = Person.findSync({
78 | surname: "Dean"
79 | });
80 | assert.isObject(people);
81 | assert.propertyVal(people, "length", 2);
82 | assert.equal(people[0].surname, "Dean");
83 | });
84 |
85 | it("2nd find should should also work", function () {
86 | var people = Person.findSync({
87 | surname: "Doe"
88 | });
89 | assert.isObject(people);
90 | assert.propertyVal(people, "length", 3);
91 | assert.equal(people[0].surname, "Doe");
92 | });
93 | });
94 | });
--------------------------------------------------------------------------------
/test/integration/model-find.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.findSync()", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var setup = function () {
9 | Person = db.define("person", {
10 | name: String,
11 | surname: String,
12 | age: Number,
13 | male: Boolean
14 | });
15 |
16 | return helper.dropSync(Person, function () {
17 | Person.createSync([{
18 | name: "John",
19 | surname: "Doe",
20 | age: 18,
21 | male: true
22 | }, {
23 | name: "Jane",
24 | surname: "Doe",
25 | age: 16,
26 | male: false
27 | }, {
28 | name: "Jeremy",
29 | surname: "Dean",
30 | age: 18,
31 | male: true
32 | }, {
33 | name: "Jack",
34 | surname: "Dean",
35 | age: 20,
36 | male: true
37 | }, {
38 | name: "Jasmine",
39 | surname: "Doe",
40 | age: 20,
41 | male: false
42 | }]);
43 | });
44 | };
45 |
46 | before(function () {
47 | db = helper.connect();
48 | });
49 |
50 | after(function () {
51 | return db.closeSync();
52 | });
53 |
54 | describe("without arguments", function () {
55 | before(setup);
56 |
57 | it("should return all items", function () {
58 | var people = Person.findSync();
59 |
60 | assert.isObject(people);
61 | assert.propertyVal(people, "length", 5);
62 | });
63 | });
64 |
65 | describe("with a number as argument", function () {
66 | before(setup);
67 |
68 | it("should use it as limit", function () {
69 | var people = Person.findSync(2);
70 |
71 | assert.isObject(people);
72 | assert.propertyVal(people, "length", 2);
73 | });
74 | });
75 |
76 | describe("with a string argument", function () {
77 | before(setup);
78 |
79 | it("should use it as property ascending order", function () {
80 | var people = Person.findSync("age");
81 |
82 | assert.isObject(people);
83 | assert.propertyVal(people, "length", 5);
84 | assert.equal(people[0].age, 16);
85 | assert.equal(people[4].age, 20);
86 | });
87 |
88 | it("should use it as property descending order if starting with '-'", function () {
89 | var people = Person.findSync("-age");
90 |
91 | assert.isObject(people);
92 | assert.propertyVal(people, "length", 5);
93 | assert.equal(people[0].age, 20);
94 | assert.equal(people[4].age, 16);
95 | });
96 | });
97 |
98 | describe("with an Array as argument", function () {
99 | before(setup);
100 |
101 | it("should use it as property ascending order", function () {
102 | var people = Person.findSync(["age"]);
103 |
104 | assert.isObject(people);
105 | assert.propertyVal(people, "length", 5);
106 | assert.equal(people[0].age, 16);
107 | assert.equal(people[4].age, 20);
108 | });
109 |
110 | it("should use it as property descending order if starting with '-'", function () {
111 | var people = Person.findSync(["-age"]);
112 |
113 | assert.isObject(people);
114 | assert.propertyVal(people, "length", 5);
115 | assert.equal(people[0].age, 20);
116 | assert.equal(people[4].age, 16);
117 | });
118 |
119 | it("should use it as property descending order if element is 'Z'", function () {
120 | var people = Person.findSync(["age", "Z"]);
121 |
122 | assert.isObject(people);
123 | assert.propertyVal(people, "length", 5);
124 | assert.equal(people[0].age, 20);
125 | assert.equal(people[4].age, 16);
126 | });
127 |
128 | it("should accept multiple ordering", function () {
129 | var people = Person.findSync(["age", "name", "Z"]);
130 |
131 | assert.isObject(people);
132 | assert.propertyVal(people, "length", 5);
133 | assert.equal(people[0].age, 16);
134 | assert.equal(people[4].age, 20);
135 | });
136 |
137 | it("should accept multiple ordering using '-' instead of 'Z'", function () {
138 | var people = Person.findSync(["age", "-name"]);
139 |
140 | assert.isObject(people);
141 | assert.propertyVal(people, "length", 5);
142 | assert.equal(people[0].age, 16);
143 | assert.equal(people[4].age, 20);
144 | });
145 | });
146 |
147 | describe("with an Object as argument", function () {
148 | before(setup);
149 |
150 | it("should use it as conditions", function () {
151 | var people = Person.findSync({
152 | age: 16
153 | });
154 |
155 | assert.isObject(people);
156 | assert.propertyVal(people, "length", 1);
157 | assert.equal(people[0].age, 16);
158 | });
159 |
160 | it("should accept comparison objects", function () {
161 | var people = Person.findSync({
162 | age: ORM.gt(18)
163 | });
164 |
165 | assert.isObject(people);
166 | assert.propertyVal(people, "length", 2);
167 | assert.equal(people[0].age, 20);
168 | assert.equal(people[1].age, 20);
169 | });
170 |
171 | describe("with another Object as argument", function () {
172 | before(setup);
173 |
174 | it("should use it as options", function () {
175 | var people = Person.findSync({
176 | age: 18
177 | }, 1, {
178 | cache: false
179 | });
180 | assert.isObject(people);
181 | assert.propertyVal(people, "length", 1);
182 | assert.equal(people[0].age, 18);
183 | });
184 |
185 | describe("if a limit is passed", function () {
186 | before(setup);
187 |
188 | it("should use it", function () {
189 | var people = Person.findSync({
190 | age: 18
191 | }, {
192 | limit: 1
193 | });
194 |
195 | assert.isObject(people);
196 | assert.propertyVal(people, "length", 1);
197 | assert.equal(people[0].age, 18);
198 | });
199 | });
200 |
201 | describe("if an offset is passed", function () {
202 | before(setup);
203 |
204 | it("should use it", function () {
205 | var people = Person.findSync({}, {
206 | offset: 1
207 | }, "age");
208 |
209 | assert.isObject(people);
210 | assert.propertyVal(people, "length", 4);
211 | assert.equal(people[0].age, 18);
212 | });
213 | });
214 |
215 | describe("if an order is passed", function () {
216 | before(setup);
217 |
218 | it("should use it", function () {
219 | var people = Person.findSync({
220 | surname: "Doe"
221 | }, {
222 | order: "age"
223 | });
224 |
225 | assert.isObject(people);
226 | assert.propertyVal(people, "length", 3);
227 | assert.equal(people[0].age, 16);
228 | });
229 |
230 | it("should use it and ignore previously defined order", function () {
231 | var people = Person.findSync({
232 | surname: "Doe"
233 | }, "-age", {
234 | order: "age"
235 | });
236 |
237 | assert.isObject(people);
238 | assert.propertyVal(people, "length", 3);
239 | assert.equal(people[0].age, 16);
240 | });
241 | });
242 | });
243 | });
244 |
245 | describe("if defined static methods", function () {
246 | before(setup);
247 |
248 | it("should be rechainable", function () {
249 | Person.over18 = function () {
250 | return this.find({
251 | age: ORM.gt(18)
252 | });
253 | };
254 | Person.family = function (family) {
255 | return this.find({
256 | surname: family
257 | });
258 | };
259 |
260 | var people = Person.over18().family("Doe").runSync();
261 |
262 | assert.isObject(people);
263 | assert.propertyVal(people, "length", 1);
264 | assert.equal(people[0].name, "Jasmine");
265 | assert.equal(people[0].surname, "Doe");
266 | });
267 | });
268 |
269 | describe("with identityCache disabled", function () {
270 | it("should not return singletons", function () {
271 | var people = Person.findSync({
272 | name: "Jasmine"
273 | }, {
274 | identityCache: false
275 | });
276 |
277 | assert.isObject(people);
278 | assert.propertyVal(people, "length", 1);
279 | assert.equal(people[0].name, "Jasmine");
280 | assert.equal(people[0].surname, "Doe");
281 |
282 | people[0].surname = "Dux";
283 |
284 | people = Person.findSync({
285 | name: "Jasmine"
286 | }, {
287 | identityCache: false
288 | });
289 |
290 | assert.isObject(people);
291 | assert.propertyVal(people, "length", 1);
292 | assert.equal(people[0].name, "Jasmine");
293 | assert.equal(people[0].surname, "Doe");
294 | });
295 | });
296 |
297 | describe("when using Model.all()", function () {
298 | it("should work exactly the same", function () {
299 | var people = Person.allSync({
300 | surname: "Doe"
301 | }, "-age", 1);
302 |
303 | assert.isObject(people);
304 | assert.propertyVal(people, "length", 1);
305 | assert.equal(people[0].name, "Jasmine");
306 | assert.equal(people[0].surname, "Doe");
307 | });
308 | });
309 |
310 | describe("when using Model.where()", function () {
311 | it("should work exactly the same", function () {
312 | var people = Person.whereSync({
313 | surname: "Doe"
314 | }, "-age", 1);
315 |
316 | assert.isObject(people);
317 | assert.propertyVal(people, "length", 1);
318 | assert.equal(people[0].name, "Jasmine");
319 | assert.equal(people[0].surname, "Doe");
320 | });
321 | });
322 | });
--------------------------------------------------------------------------------
/test/integration/model-get.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | var helper = require('../support/spec_helper');
3 | var ORM = require('../../');
4 | var coroutine = require('coroutine');
5 |
6 | describe("Model.get()", function () {
7 | var db = null;
8 | var Person = null;
9 | var John;
10 |
11 | var setup = function (identityCache) {
12 | return function () {
13 | Person = db.define("person", {
14 | name: {
15 | type: 'text',
16 | mapsTo: 'fullname'
17 | }
18 | }, {
19 | identityCache: identityCache,
20 | methods: {
21 | UID: function () {
22 | return this[Person.id];
23 | }
24 | }
25 | });
26 |
27 | ORM.singleton.clear(); // clear identityCache cache
28 |
29 | return helper.dropSync(Person, function () {
30 | var people = Person.createSync([{
31 | name: "John Doe"
32 | }, {
33 | name: "Jane Doe"
34 | }]);
35 | John = people[0];
36 | });
37 | };
38 | };
39 |
40 | before(function () {
41 | db = helper.connect();
42 | });
43 |
44 | after(function () {
45 | return db.closeSync();
46 | });
47 |
48 | describe("mapsTo", function () {
49 | before(setup(true));
50 |
51 | it("should create the table with a different column name than property name", function () {
52 | var sql;
53 | var protocol = db.driver.db.conn.type;
54 |
55 | if (protocol == 'SQLite') {
56 | sql = "PRAGMA table_info(?)";
57 | } else {
58 | sql = "SELECT column_name FROM information_schema.columns WHERE table_name = ?";
59 | }
60 |
61 | var data = db.driver.execQuerySync(sql, [Person.table]);
62 | var names = _.map(data, protocol == 'SQLite' ? 'name' : 'column_name')
63 |
64 | assert.equal(typeof Person.properties.name, 'object');
65 | assert.notEqual(names.indexOf('fullname'), -1);
66 | });
67 | });
68 |
69 | describe("with identityCache cache", function () {
70 | before(setup(true));
71 |
72 | it("should return item with id 1", function () {
73 | var John1 = Person.getSync(John[Person.id]);
74 |
75 | assert.isObject(John1);
76 | assert.propertyVal(John1, Person.id[0], John[Person.id]);
77 | assert.propertyVal(John1, "name", "John Doe");
78 | });
79 |
80 | it("should have an UID method", function () {
81 | var John1 = Person.getSync(John[Person.id]);
82 |
83 | assert.isFunction(John1.UID);
84 | assert.equal(John1.UID(), John[Person.id]);
85 | });
86 |
87 | describe("changing name and getting id 1 again", function () {
88 | it("should return the original object with unchanged name", function () {
89 | var John1 = Person.getSync(John[Person.id]);
90 |
91 | John1.name = "James";
92 |
93 | var John2 = Person.getSync(John[Person.id]);
94 |
95 | assert.equal(John1[Person.id], John2[Person.id]);
96 | assert.equal(John2.name, "John Doe");
97 | });
98 | });
99 |
100 | describe("changing instance.identityCacheSaveCheck = false", function () {
101 | before(function () {
102 | Person.settings.set("instance.identityCacheSaveCheck", false);
103 | });
104 |
105 | it("should return the same object with the changed name", function () {
106 | var John1 = Person.getSync(John[Person.id]);
107 |
108 | John1.name = "James";
109 |
110 | var John2 = Person.getSync(John[Person.id]);
111 |
112 | assert.equal(John1[Person.id], John2[Person.id]);
113 | assert.equal(John2.name, "James");
114 | });
115 | });
116 | });
117 |
118 | describe("with no identityCache cache", function () {
119 | before(setup(false));
120 |
121 | describe("fetching several times", function () {
122 | it("should return different objects", function () {
123 | var John1 = Person.getSync(John[Person.id]);
124 | var John2 = Person.getSync(John[Person.id]);
125 |
126 | assert.equal(John1[Person.id], John2[Person.id]);
127 | assert.notEqual(John1, John2);
128 | });
129 | });
130 | });
131 |
132 | describe("with identityCache cache = 0.5 secs", function () {
133 | before(setup(0.5));
134 |
135 | describe("fetching again after 0.2 secs", function () {
136 | it("should return same objects", function () {
137 | var John1 = Person.getSync(John[Person.id]);
138 |
139 | coroutine.sleep(200);
140 |
141 | var John2 = Person.getSync(John[Person.id]);
142 |
143 | assert.equal(John1[Person.id], John2[Person.id]);
144 | assert.equal(John1, John2);
145 | });
146 | });
147 |
148 | describe("fetching again after 0.7 secs", function () {
149 | it("should return different objects", function () {
150 | var John1 = Person.getSync(John[Person.id]);
151 |
152 | coroutine.sleep(700);
153 |
154 | var John2 = Person.getSync(John[Person.id]);
155 | assert.notEqual(John1, John2);
156 | });
157 | });
158 | });
159 |
160 | describe("with empty object as options", function () {
161 | before(setup());
162 |
163 | it("should return item with id 1 like previously", function () {
164 | var John1 = Person.getSync(John[Person.id], {});
165 |
166 | assert.isObject(John1);
167 | assert.propertyVal(John1, Person.id[0], John[Person.id]);
168 | assert.propertyVal(John1, "name", "John Doe");
169 | });
170 | });
171 |
172 | describe("when not found", function () {
173 | before(setup(true));
174 |
175 | it("should return an error", function () {
176 | try {
177 | Person.getSync(999);
178 | } catch (err) {
179 | assert.equal(err.message, "Not found");
180 | }
181 | });
182 | });
183 |
184 | describe("if passed an Array with ids", function () {
185 | before(setup(true));
186 |
187 | it("should accept and try to fetch", function () {
188 | var John1 = Person.getSync([John[Person.id]]);
189 |
190 | assert.isObject(John1);
191 | assert.propertyVal(John1, Person.id[0], John[Person.id]);
192 | assert.propertyVal(John1, "name", "John Doe");
193 | });
194 | });
195 |
196 | describe("if primary key name is changed", function () {
197 | before(function () {
198 | Person = db.define("person", {
199 | name: String
200 | });
201 |
202 | ORM.singleton.clear();
203 |
204 | return helper.dropSync(Person, function () {
205 | Person.createSync([{
206 | name: "John Doe"
207 | }, {
208 | name: "Jane Doe"
209 | }]);
210 | });
211 | });
212 |
213 | it("should search by key name and not 'id'", function () {
214 | db.settings.set('properties.primary_key', 'name');
215 |
216 | var OtherPerson = db.define("person", {
217 | id: Number
218 | });
219 |
220 | var person = OtherPerson.getSync("Jane Doe");
221 | assert.equal(person.name, "Jane Doe");
222 | });
223 | });
224 |
225 | xdescribe("with a point property type", function () {
226 | // if (common.protocol() == 'sqlite' || common.protocol() == 'mongodb') return;
227 |
228 | it("should deserialize the point to an array", function () {
229 | db.settings.set('properties.primary_key', 'id');
230 |
231 | Person = db.define("person", {
232 | name: String,
233 | location: {
234 | type: "point"
235 | }
236 | });
237 |
238 | ORM.singleton.clear();
239 |
240 | return helper.dropSync(Person, function () {
241 | Person.create({
242 | name: "John Doe",
243 | location: {
244 | x: 51.5177,
245 | y: -0.0968
246 | }
247 | }, function (err, person) {
248 | assert.equal(err, null);
249 |
250 | person.location.should.be.an.instanceOf(Object);
251 | assert.propertyVal(person.location, 'x', 51.5177);
252 | assert.propertyVal(person.location, 'y', -0.0968);
253 | return done();
254 | });
255 | });
256 | });
257 | });
258 | });
--------------------------------------------------------------------------------
/test/integration/model-keys.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model keys option", function () {
5 | var db = null;
6 |
7 | before(function () {
8 | db = helper.connect();
9 | });
10 |
11 | after(function () {
12 | return db.closeSync();
13 | });
14 |
15 | describe("if model id is a property", function () {
16 | var Person = null;
17 |
18 | before(function () {
19 | Person = db.define("person", {
20 | uid: String,
21 | name: String,
22 | surname: String
23 | }, {
24 | id: "uid"
25 | });
26 |
27 | return helper.dropSync(Person);
28 | });
29 |
30 | it("should not auto increment IDs", function () {
31 | var JohnDoe = Person.createSync({
32 | uid: "john-doe",
33 | name: "John",
34 | surname: "Doe"
35 | });
36 |
37 | assert.equal(JohnDoe.uid, "john-doe");
38 | assert.notProperty(JohnDoe, "id");
39 | });
40 | });
41 |
42 | describe("if model defines several keys", function () {
43 | var DoorAccessHistory = null;
44 |
45 | before(function () {
46 | DoorAccessHistory = db.define("door_access_history", {
47 | year: {
48 | type: 'integer'
49 | },
50 | month: {
51 | type: 'integer'
52 | },
53 | day: {
54 | type: 'integer'
55 | },
56 | user: String,
57 | action: ["in", "out"]
58 | }, {
59 | id: ["year", "month", "day"]
60 | });
61 |
62 | return helper.dropSync(DoorAccessHistory, function () {
63 | DoorAccessHistory.createSync([{
64 | year: 2013,
65 | month: 7,
66 | day: 11,
67 | user: "dresende",
68 | action: "in"
69 | },
70 | {
71 | year: 2013,
72 | month: 7,
73 | day: 12,
74 | user: "dresende",
75 | action: "out"
76 | }
77 | ]);
78 | });
79 | });
80 |
81 | it("should make possible to get instances based on all keys", function () {
82 | var HistoryItem = DoorAccessHistory.getSync(2013, 7, 11);
83 |
84 | assert.equal(HistoryItem.year, 2013);
85 | assert.equal(HistoryItem.month, 7);
86 | assert.equal(HistoryItem.day, 11);
87 | assert.equal(HistoryItem.user, "dresende");
88 | assert.equal(HistoryItem.action, "in");
89 | });
90 |
91 | it("should make possible to remove instances based on all keys", function () {
92 | var HistoryItem = DoorAccessHistory.getSync(2013, 7, 12);
93 |
94 | HistoryItem.removeSync();
95 |
96 | var exists = DoorAccessHistory.existsSync(2013, 7, 12);
97 | assert.isFalse(exists);
98 | });
99 | });
100 | });
--------------------------------------------------------------------------------
/test/integration/model-one.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.one()", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var setup = function () {
9 | return function () {
10 | Person = db.define("person", {
11 | name: String
12 | });
13 |
14 | return helper.dropSync(Person, function () {
15 | Person.createSync([{
16 | id: 1,
17 | name: "Jeremy Doe"
18 | }, {
19 | id: 2,
20 | name: "John Doe"
21 | }, {
22 | id: 3,
23 | name: "Jane Doe"
24 | }]);
25 | });
26 | };
27 | };
28 |
29 | before(function () {
30 | db = helper.connect();
31 | });
32 |
33 | after(function () {
34 | return db.closeSync();
35 | });
36 |
37 | describe("without arguments", function () {
38 | before(setup());
39 |
40 | it("should return first item in model", function () {
41 | var person = Person.oneSync();
42 | assert.equal(person.name, "Jeremy Doe");
43 | });
44 | });
45 |
46 | describe("with order", function () {
47 | before(setup());
48 |
49 | it("should return first item in model based on order", function () {
50 | var person = Person.oneSync("-name");
51 | assert.equal(person.name, "John Doe");
52 | });
53 | });
54 |
55 | describe("with conditions", function () {
56 | before(setup());
57 |
58 | it("should return first item in model based on conditions", function () {
59 | var person = Person.oneSync({
60 | name: "Jane Doe"
61 | });
62 | assert.equal(person.name, "Jane Doe");
63 | });
64 |
65 | describe("if no match", function () {
66 | before(setup());
67 |
68 | it("should return null", function () {
69 | var person = Person.oneSync({
70 | name: "Jack Doe"
71 | });
72 | assert.equal(person, null);
73 | });
74 | });
75 | });
76 | });
--------------------------------------------------------------------------------
/test/integration/model-save.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.save()", function () {
5 | var db = null;
6 | var Person = null;
7 |
8 | var setup = function (nameDefinition, opts) {
9 | opts = opts || {};
10 |
11 | return function () {
12 | Person = db.define("person", {
13 | name: nameDefinition || String
14 | }, opts || {});
15 |
16 | Person.hasOne("parent", Person, opts.hasOneOpts);
17 | if ('saveAssociationsByDefault' in opts) {
18 | Person.settings.set(
19 | 'instance.saveAssociationsByDefault', opts.saveAssociationsByDefault
20 | );
21 | }
22 |
23 | return helper.dropSync(Person);
24 | };
25 | };
26 |
27 | before(function () {
28 | db = helper.connect();
29 |
30 | });
31 |
32 | after(function () {
33 | return db.closeSync();
34 | });
35 |
36 | describe("if properties have default values", function () {
37 | before(setup({
38 | type: "text",
39 | defaultValue: "John"
40 | }));
41 |
42 | it("should use it if not defined", function () {
43 | var John = new Person();
44 |
45 | John.saveSync();
46 | assert.equal(John.name, "John");
47 | });
48 | });
49 |
50 | describe("with callback", function () {
51 | before(setup());
52 |
53 | it("should save item and return id", function () {
54 | var John = new Person({
55 | name: "John"
56 | });
57 | John.saveSync();
58 |
59 | assert.exist(John[Person.id]);
60 |
61 | var JohnCopy = Person.getSync(John[Person.id]);
62 |
63 | assert.equal(JohnCopy[Person.id], John[Person.id]);
64 | assert.equal(JohnCopy.name, John.name);
65 | });
66 | });
67 |
68 | xdescribe("without callback", function () {
69 | before(setup());
70 |
71 | it("should still save item and return id", function () {
72 | var John = new Person({
73 | name: "John"
74 | });
75 | John.save();
76 | John.on("save", function (err) {
77 | assert.equal(err, null);
78 | assert.exist(John[Person.id]);
79 |
80 | Person.get(John[Person.id], function (err, JohnCopy) {
81 | assert.equal(err, null);
82 |
83 | assert.equal(JohnCopy[Person.id], John[Person.id]);
84 | assert.equal(JohnCopy.name, John.name);
85 |
86 | return done();
87 | });
88 | });
89 | });
90 | });
91 |
92 | describe("with properties object", function () {
93 | before(setup());
94 |
95 | it("should update properties, save item and return id", function () {
96 | var John = new Person({
97 | name: "Jane"
98 | });
99 | John.saveSync({
100 | name: "John"
101 | });
102 |
103 | assert.exist(John[Person.id]);
104 | assert.equal(John.name, "John");
105 |
106 | var JohnCopy = Person.getSync(John[Person.id]);
107 |
108 | assert.equal(JohnCopy[Person.id], John[Person.id]);
109 | assert.equal(JohnCopy.name, John.name);
110 | });
111 | });
112 |
113 | describe("with unknown argument type", function () {
114 | before(setup());
115 |
116 | it("should should throw", function () {
117 | var John = new Person({
118 | name: "Jane"
119 | });
120 |
121 | assert.throws(function () {
122 | John.saveSync("will-fail");
123 | });
124 | });
125 | });
126 |
127 | describe("if passed an association instance", function () {
128 | before(setup());
129 |
130 | it("should save association first and then save item and return id", function () {
131 | var Jane = new Person({
132 | name: "Jane"
133 | });
134 | var John = new Person({
135 | name: "John",
136 | parent: Jane
137 | });
138 | John.saveSync();
139 |
140 | assert.isTrue(John.saved());
141 | assert.isTrue(Jane.saved());
142 |
143 | assert.exist(John[Person.id]);
144 | assert.exist(Jane[Person.id]);
145 | });
146 | });
147 |
148 | describe("if passed an association object", function () {
149 | before(setup());
150 |
151 | it("should save association first and then save item and return id", function () {
152 | var John = new Person({
153 | name: "John",
154 | parent: {
155 | name: "Jane"
156 | }
157 | });
158 | John.saveSync();
159 |
160 | assert.isTrue(John.saved());
161 | assert.isTrue(John.parent.saved());
162 |
163 | assert.exist(John[Person.id]);
164 | assert.exist(John.parent[Person.id]);
165 | assert.equal(John.parent.name, "Jane");
166 | });
167 | });
168 |
169 | xdescribe("if autoSave is on", function () {
170 | before(setup(null, {
171 | autoSave: true
172 | }));
173 |
174 | it("should save the instance as soon as a property is changed", function () {
175 | var John = new Person({
176 | name: "Jhon"
177 | });
178 | John.save(function (err) {
179 | assert.equal(err, null);
180 |
181 | John.on("save", function () {
182 | return done();
183 | });
184 |
185 | John.name = "John";
186 | });
187 | });
188 | });
189 |
190 | describe("with saveAssociations", function () {
191 | var afterSaveCalled = false;
192 |
193 | describe("default on in settings", function () {
194 | beforeEach(function () {
195 | function afterSave() {
196 | afterSaveCalled = true;
197 | }
198 | var hooks = {
199 | afterSave: afterSave
200 | };
201 |
202 | setup(null, {
203 | hooks: hooks,
204 | cache: false,
205 | hasOneOpts: {
206 | autoFetch: true
207 | }
208 | })();
209 | var olga = Person.createSync({
210 | name: 'Olga'
211 | });
212 |
213 | assert.exist(olga);
214 | var hagar = Person.createSync({
215 | name: 'Hagar',
216 | parent_id: olga.id
217 | });
218 | assert.exist(hagar);
219 | afterSaveCalled = false;
220 | });
221 |
222 | it("should be on", function () {
223 | assert.equal(Person.settings.get('instance.saveAssociationsByDefault'), true);
224 | });
225 |
226 | it("off should not save associations but save itself", function () {
227 | var hagar = Person.oneSync({
228 | name: 'Hagar'
229 | });
230 |
231 | assert.exist(hagar.parent);
232 |
233 | hagar.parent.name = 'Olga2';
234 | hagar.saveSync({
235 | name: 'Hagar2'
236 | }, {
237 | saveAssociations: false
238 | });
239 |
240 | assert.equal(afterSaveCalled, true);
241 |
242 | var olga = Person.getSync(hagar.parent.id)
243 | assert.equal(olga.name, 'Olga');
244 | });
245 |
246 | it("off should not save associations or itself if there are no changes", function () {
247 | var hagar = Person.oneSync({
248 | name: 'Hagar'
249 | });
250 |
251 | hagar.saveSync({}, {
252 | saveAssociations: false
253 | });
254 |
255 | assert.equal(afterSaveCalled, false);
256 |
257 | var olga = Person.getSync(hagar.parent.id);
258 | assert.equal(olga.name, 'Olga');
259 | });
260 |
261 | it("unspecified should save associations and itself", function () {
262 | var hagar = Person.oneSync({
263 | name: 'Hagar'
264 | });
265 | assert.exist(hagar.parent);
266 |
267 | hagar.parent.name = 'Olga2';
268 | hagar.saveSync({
269 | name: 'Hagar2'
270 | });
271 |
272 | var olga = Person.getSync(hagar.parent.id);
273 | assert.equal(olga.name, 'Olga2');
274 |
275 | var person = Person.getSync(hagar.id);
276 |
277 | assert.equal(person.name, 'Hagar2');
278 | });
279 |
280 | it("on should save associations and itself", function () {
281 | var hagar = Person.oneSync({
282 | name: 'Hagar'
283 | });
284 | assert.exist(hagar.parent);
285 |
286 | hagar.parent.name = 'Olga2';
287 | hagar.saveSync({
288 | name: 'Hagar2'
289 | }, {
290 | saveAssociations: true
291 | });
292 |
293 | var olga = Person.getSync(hagar.parent.id);
294 | assert.equal(olga.name, 'Olga2');
295 |
296 | var person = Person.getSync(hagar.id);
297 | assert.equal(person.name, 'Hagar2');
298 | });
299 | });
300 |
301 | describe("turned off in settings", function () {
302 | beforeEach(function () {
303 | function afterSave() {
304 | afterSaveCalled = true;
305 | }
306 | var hooks = {
307 | afterSave: afterSave
308 | };
309 |
310 | setup(null, {
311 | hooks: hooks,
312 | cache: false,
313 | hasOneOpts: {
314 | autoFetch: true
315 | },
316 | saveAssociationsByDefault: false
317 | })();
318 |
319 | var olga = Person.createSync({
320 | name: 'Olga'
321 | });
322 |
323 | assert.exist(olga);
324 | var hagar = Person.createSync({
325 | name: 'Hagar',
326 | parent_id: olga.id
327 | });
328 | assert.exist(hagar);
329 | afterSaveCalled = false;
330 | });
331 |
332 | it("should be off", function () {
333 | assert.equal(Person.settings.get('instance.saveAssociationsByDefault'), false);
334 | });
335 |
336 | it("unspecified should not save associations but save itself", function () {
337 | var hagar = Person.oneSync({
338 | name: 'Hagar'
339 | });
340 |
341 | assert.exist(hagar.parent);
342 |
343 | hagar.parent.name = 'Olga2';
344 | hagar.saveSync({
345 | name: 'Hagar2'
346 | });
347 |
348 | var olga = Person.getSync(hagar.parent.id);
349 |
350 | assert.equal(olga.name, 'Olga');
351 |
352 | var person = Person.getSync(hagar.id);
353 | assert.equal(person.name, 'Hagar2');
354 | });
355 |
356 | it("off should not save associations but save itself", function () {
357 | var hagar = Person.oneSync({
358 | name: 'Hagar'
359 | });
360 |
361 | assert.exist(hagar.parent);
362 |
363 | hagar.parent.name = 'Olga2';
364 | hagar.saveSync({
365 | name: 'Hagar2'
366 | }, {
367 | saveAssociations: false
368 | });
369 | assert.equal(afterSaveCalled, true);
370 |
371 | var olga = Person.getSync(hagar.parent.id);
372 | assert.equal(olga.name, 'Olga');
373 | });
374 |
375 | it("on should save associations and itself", function () {
376 | var hagar = Person.oneSync({
377 | name: 'Hagar'
378 | });
379 |
380 | assert.exist(hagar.parent);
381 |
382 | hagar.parent.name = 'Olga2';
383 | hagar.saveSync({
384 | name: 'Hagar2'
385 | }, {
386 | saveAssociations: true
387 | });
388 |
389 | var olga = Person.getSync(hagar.parent.id);
390 | assert.equal(olga.name, 'Olga2');
391 |
392 | var person = Person.getSync(hagar.id);
393 | assert.equal(person.name, 'Hagar2');
394 | });
395 | });
396 | });
397 |
398 | xdescribe("with a point property", function () {
399 | // if (common.protocol() == 'sqlite' || common.protocol() == 'mongodb') return;
400 |
401 | it("should save the instance as a geospatial point", function () {
402 | setup({
403 | type: "point"
404 | }, null)(function () {
405 | var John = new Person({
406 | name: {
407 | x: 51.5177,
408 | y: -0.0968
409 | }
410 | });
411 | John.save(function (err) {
412 | assert.equal(err, null);
413 |
414 | John.name.should.be.an.instanceOf(Object);
415 | assert.property(John.name, 'x', 51.5177);
416 | assert.property(John.name, 'y', -0.0968);
417 | return done();
418 | });
419 | });
420 | });
421 | });
422 |
423 | xdescribe("mockable", function () {
424 | before(setup());
425 |
426 | it("save should be writable", function () {
427 | var John = new Person({
428 | name: "John"
429 | });
430 | var saveCalled = false;
431 | John.save = function (cb) {
432 | saveCalled = true;
433 | cb(null);
434 | };
435 | John.save(function (err) {
436 | assert.equal(saveCalled, true);
437 | return done();
438 | });
439 | });
440 |
441 | it("saved should be writable", function () {
442 | var John = new Person({
443 | name: "John"
444 | });
445 | var savedCalled = false;
446 | John.saved = function () {
447 | savedCalled = true;
448 | return true;
449 | };
450 |
451 | John.saved()
452 | assert.isTrue(savedCalled);
453 | done();
454 | })
455 | });
456 | });
--------------------------------------------------------------------------------
/test/integration/model-sync.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Model.sync", function () {
5 | var db = null;
6 |
7 | before(function () {
8 | db = helper.connect();
9 | });
10 |
11 | after(function () {
12 | db.closeSync();
13 | });
14 |
15 | // SQLite scopes index names to a database and NOT a table, so
16 | // index name collisions were possible. This tests the workaround.
17 | it("should work with multiple same-named indexes", function () {
18 | var A, B, C;
19 |
20 | A = db.define('a', {
21 | name: String
22 | });
23 | B = db.define('b', {
24 | name: String
25 | });
26 | C = db.define('c', {
27 | name: String
28 | });
29 |
30 | A.hasMany('bees', B, {}, {
31 | reverse: 'eighs'
32 | });
33 | A.hasMany('cees', C, {}, {
34 | reverse: 'eighs'
35 | });
36 |
37 | helper.dropSync([A, B, C]);
38 | });
39 | });
--------------------------------------------------------------------------------
/test/integration/predefined-validators.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var validators = require('../../').validators;
3 | var undef = undefined;
4 |
5 | function checkValidation(expected) {
6 | return function (returned) {
7 | assert.equal(returned, expected);
8 | };
9 | }
10 |
11 | describe("Predefined Validators", function () {
12 |
13 | describe("equalToProperty('name')", function () {
14 | it("should pass if equal", function () {
15 | validators.equalToProperty('name').call({
16 | name: "John Doe"
17 | }, 'John Doe', checkValidation());
18 | });
19 | it("should not pass if not equal", function () {
20 | validators.equalToProperty('name').call({
21 | name: "John"
22 | }, 'John Doe', checkValidation('not-equal-to-property'));
23 | });
24 | it("should not pass even if equal to other property", function () {
25 | validators.equalToProperty('name').call({
26 | surname: "John Doe"
27 | }, 'John Doe', checkValidation('not-equal-to-property'));
28 | });
29 | });
30 |
31 | describe("unique()", function () {
32 | var db = null;
33 | var Person = null;
34 |
35 | var setup = function () {
36 | return function () {
37 | Person = db.define("person", {
38 | name: String,
39 | surname: String
40 | }, {
41 | validations: {
42 | surname: validators.unique()
43 | }
44 | });
45 |
46 | Person.settings.set("instance.returnAllErrors", false);
47 |
48 | return helper.dropSync(Person, function () {
49 | Person.createSync([{
50 | name: "John",
51 | surname: "Doe"
52 | }]);
53 | });
54 | };
55 | };
56 |
57 | before(function () {
58 | db = helper.connect();
59 | setup()();
60 | });
61 |
62 | after(function () {
63 | return db.closeSync();
64 | });
65 |
66 | it("should not pass if more elements with that property exist", function () {
67 | var janeDoe = new Person({
68 | name: "Jane",
69 | surname: "Doe" // <-- in table already!
70 | });
71 | try {
72 | janeDoe.saveSync();
73 | } catch (err) {
74 | assert.propertyVal(err, "property", "surname");
75 | assert.propertyVal(err, "value", "Doe");
76 | assert.propertyVal(err, "msg", "not-unique");
77 | }
78 | });
79 |
80 | it("should pass if no more elements with that property exist", function () {
81 | var janeDean = new Person({
82 | name: "Jane",
83 | surname: "Dean" // <-- not in table
84 | });
85 | janeDean.saveSync();
86 | });
87 |
88 | it("should pass if resaving the same instance", function () {
89 | var Johns = Person.findSync({
90 | name: "John",
91 | surname: "Doe"
92 | });
93 | assert.propertyVal(Johns, "length", 1);
94 | Johns[0].surname = "Doe"; // forcing resave
95 | Johns[0].saveSync();
96 | });
97 | });
98 |
99 | });
--------------------------------------------------------------------------------
/test/integration/property-custom.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("custom types", function () {
5 | var db = null;
6 |
7 | before(function () {
8 | db = helper.connect();
9 | });
10 |
11 | after(function () {
12 | db.closeSync();
13 | });
14 |
15 | describe("simple", function () {
16 | var LottoTicket = null;
17 |
18 | before(function () {
19 | db.defineType('numberArray', {
20 | datastoreType: function (prop) {
21 | return 'TEXT'
22 | },
23 | valueToProperty: function (value, prop) {
24 | if (Array.isArray(value)) {
25 | return value;
26 | } else {
27 | if (Buffer.isBuffer(value))
28 | value = value.toString();
29 | return value.split(',').map(function (v) {
30 | return Number(v);
31 | });
32 | }
33 | },
34 | propertyToValue: function (value, prop) {
35 | return value.join(',')
36 | }
37 | });
38 |
39 | LottoTicket = db.define('lotto_ticket', {
40 | numbers: {
41 | type: 'numberArray'
42 | }
43 | });
44 |
45 | return helper.dropSync(LottoTicket);
46 | });
47 |
48 | it("should create the table", function () {
49 | assert.ok(true);
50 | });
51 |
52 | it("should store data in the table", function () {
53 | var ticket = new LottoTicket({
54 | numbers: [4, 23, 6, 45, 9, 12, 3, 29]
55 | });
56 |
57 | ticket.saveSync();
58 |
59 | var items = LottoTicket.find().allSync();
60 | assert.equal(items.length, 1);
61 | assert.ok(Array.isArray(items[0].numbers));
62 |
63 | assert.deepEqual([4, 23, 6, 45, 9, 12, 3, 29], items[0].numbers);
64 | });
65 |
66 | describe("hasMany extra properties", function () {
67 | it("should work", function () {
68 | db.defineType('customDate', {
69 | datastoreType: function (prop) {
70 | return 'TEXT';
71 | }
72 | });
73 | var Person = db.define('person', {
74 | name: String,
75 | surname: String,
76 | age: Number
77 | });
78 | var Pet = db.define('pet', {
79 | name: String
80 | });
81 | Person.hasMany('pets', Pet, {
82 | date: {
83 | type: 'customDate'
84 | }
85 | }, {
86 | autoFetch: true
87 | });
88 |
89 | return helper.dropSync([Person, Pet], function () {
90 | var person = Person.createSync({
91 | name: "John",
92 | surname: "Doe",
93 | age: 20
94 | });
95 |
96 | var pet = Pet.createSync({
97 | name: 'Fido'
98 | });
99 |
100 | person.addPetsSync(pet, {
101 | date: '2014-05-20'
102 | });
103 |
104 | var freshPerson = Person.getSync(person.id);
105 | assert.equal(freshPerson.pets.length, 1);
106 | assert.equal(freshPerson.pets[0].extra.date, '2014-05-20');
107 | });
108 | });
109 | });
110 | });
111 |
112 | describe("complex", function () {
113 | var WonkyTotal = null;
114 |
115 | before(function () {
116 | db.defineType('wonkyNumber', {
117 | datastoreType: function (prop) {
118 | return 'INTEGER';
119 | },
120 | datastoreGet: function (prop, helper) {
121 | return helper.escape('?? - 1', [prop.mapsTo]);
122 | },
123 | valueToProperty: function (value, prop) {
124 | return value + 7;
125 | },
126 | propertyToValue: function (value, prop) {
127 | if (value == null) {
128 | return value;
129 | } else {
130 | return function (helper) {
131 | return helper.escape('(? - 2)', [value]);
132 | };
133 | }
134 | }
135 | });
136 |
137 | WonkyTotal = db.define('wonky', {
138 | name: String,
139 | total: {
140 | type: 'wonkyNumber',
141 | mapsTo: 'blah_total'
142 | }
143 | });
144 |
145 | return helper.dropSync(WonkyTotal);
146 | });
147 |
148 | it("should store wonky total in a differently named field", function () {
149 | var item = new WonkyTotal();
150 |
151 | item.name = "cabbages";
152 | item.total = 8;
153 |
154 | item.saveSync();
155 | assert.equal(item.total, 15);
156 |
157 | var item = WonkyTotal.getSync(item.id);
158 |
159 | assert.equal(item.total, 19); // (15 - 2) - 1 + 7
160 | });
161 | });
162 |
163 | });
--------------------------------------------------------------------------------
/test/integration/property-lazyload.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("LazyLoad properties", function () {
5 | var db = null;
6 | var Person = null;
7 | var PersonPhoto = Buffer.alloc(1024); // fake photo
8 | var OtherPersonPhoto = Buffer.alloc(1024); // other fake photo
9 |
10 | var setup = function () {
11 | return function () {
12 | Person = db.define("person", {
13 | name: String,
14 | photo: {
15 | type: "binary",
16 | lazyload: true
17 | }
18 | });
19 |
20 | ORM.singleton.clear();
21 |
22 | helper.dropSync(Person, function () {
23 | Person.createSync({
24 | name: "John Doe",
25 | photo: PersonPhoto
26 | });
27 | });
28 | };
29 | };
30 |
31 | before(function () {
32 | db = helper.connect();
33 | });
34 |
35 | after(function () {
36 | return db.closeSync();
37 | });
38 |
39 | describe("when defined", function () {
40 | before(setup());
41 |
42 | it("should not be available when fetching an instance", function () {
43 | var John = Person.find().firstSync();
44 |
45 | assert.isObject(John);
46 |
47 | assert.propertyVal(John, "name", "John Doe");
48 | assert.propertyVal(John, "photo", null);
49 | });
50 |
51 | it("should have apropriate accessors", function () {
52 | var John = Person.find().firstSync();
53 |
54 | assert.isObject(John);
55 | assert.isFunction(John.getPhoto);
56 | assert.isFunction(John.setPhoto);
57 | assert.isFunction(John.removePhoto);
58 | });
59 |
60 | it("getAccessor should return property", function () {
61 | var John = Person.find().firstSync();
62 |
63 | assert.isObject(John);
64 |
65 | var photo = John.getPhotoSync();
66 |
67 | assert.equal(photo.toString(), PersonPhoto.toString());
68 | });
69 |
70 | it("setAccessor should change property", function () {
71 | var John = Person.find().firstSync();
72 |
73 | assert.isObject(John);
74 |
75 | John.setPhotoSync(OtherPersonPhoto);
76 |
77 |
78 | var John = Person.find().firstSync();
79 |
80 | assert.isObject(John);
81 |
82 | var photo = John.getPhotoSync();
83 | assert.equal(photo.toString(), OtherPersonPhoto.toString());
84 | });
85 |
86 | it("removeAccessor should change property", function () {
87 | var John = Person.find().firstSync();
88 |
89 | assert.isObject(John);
90 |
91 | John.removePhotoSync();
92 |
93 | var John = Person.getSync(John[Person.id]);
94 |
95 | assert.isObject(John);
96 |
97 | var photo = John.getPhotoSync();
98 | assert.equal(photo, null);
99 | });
100 | });
101 | });
--------------------------------------------------------------------------------
/test/integration/property-maps-to.js:
--------------------------------------------------------------------------------
1 | var _ = require('lodash');
2 | var helper = require('../support/spec_helper');
3 | var ORM = require('../../');
4 |
5 | describe("Property.mapsTo", function () {
6 | var db = null;
7 |
8 | before(function () {
9 | db = helper.connect();
10 | });
11 |
12 | after(function () {
13 | return db.closeSync();
14 | });
15 |
16 | describe("normal", function () {
17 | var Book = null;
18 | var id1 = null,
19 | id2 = null;
20 |
21 | before(function () {
22 | Book = db.define("book", {
23 | title: {
24 | type: 'text',
25 | mapsTo: 'book_title',
26 | required: true
27 | },
28 | pages: {
29 | type: 'integer',
30 | required: false
31 | }
32 | });
33 | return helper.dropSync(Book);
34 | });
35 |
36 | it("should create", function () {
37 | var book = Book.createSync({
38 | title: "History of the wheel",
39 | pages: 297
40 | });
41 | assert.exist(book);
42 | assert.equal(book.title, "History of the wheel");
43 | id1 = book.id;
44 | });
45 |
46 | it("should save new", function () {
47 | var book = new Book({
48 | title: "Stuff",
49 | pages: 33
50 | })
51 | book = book.saveSync();
52 |
53 | assert.exist(book);
54 | assert.equal(book.title, "Stuff");
55 | id2 = book.id;
56 | });
57 |
58 | it("should get book1", function () {
59 | var book = Book.getSync(id1);
60 | assert.exist(book);
61 | assert.equal(book.title, "History of the wheel");
62 | });
63 |
64 | it("should get book2", function () {
65 | var book = Book.getSync(id2);
66 | assert.exist(book);
67 | assert.equal(book.title, "Stuff");
68 | });
69 |
70 | it("should find", function () {
71 | var book = Book.oneSync({
72 | title: "History of the wheel"
73 | });
74 | assert.exist(book);
75 | assert.equal(book.title, "History of the wheel");
76 | });
77 |
78 | it("should update", function () {
79 | var book = Book.oneSync();
80 | assert.exist(book);
81 | assert.equal(book.title, "History of the wheel");
82 |
83 | book.title = "Quantum theory";
84 | book.pages = 5;
85 |
86 | book.saveSync();
87 | assert.equal(book.title, "Quantum theory");
88 |
89 | var freshBook = Book.getSync(book.id);
90 | assert.exist(freshBook);
91 | assert.equal(book.title, "Quantum theory");
92 | });
93 |
94 | it("should order", function () {
95 | Book.createSync({
96 | title: "Zzz",
97 | pages: 2
98 | });
99 | Book.createSync({
100 | title: "Aaa",
101 | pages: 3
102 | });
103 |
104 | var items = Book.find().order("-title").allSync();
105 | assert.equal(
106 | _.map(items, 'title').join(','),
107 | "Zzz,Stuff,Quantum theory,Aaa"
108 | )
109 | items = Book.find().order("title").allSync();
110 | assert.equal(
111 | _.map(items, 'title').join(','),
112 | "Aaa,Quantum theory,Stuff,Zzz"
113 | )
114 | });
115 | });
116 |
117 | describe("keys", function () {
118 | var Person = null;
119 | var id1 = null,
120 | id2 = null;
121 |
122 | before(function () {
123 | Person = db.define("person", {
124 | firstName: {
125 | type: 'text',
126 | mapsTo: 'first_name',
127 | key: true
128 | },
129 | lastName: {
130 | type: 'text',
131 | mapsTo: 'last_name',
132 | key: true
133 | },
134 | age: {
135 | type: 'integer'
136 | }
137 | });
138 |
139 | return helper.dropSync(Person);
140 | });
141 |
142 | it("should throw an error if invalid keys are specified", function () {
143 | assert.throws(function () {
144 | db.define("blah", {
145 | name: {
146 | type: 'text'
147 | }
148 | }, {
149 | id: ['banana']
150 | });
151 | }, "Model defined without any keys");
152 | });
153 |
154 | it("should create", function () {
155 | var person = Person.createSync({
156 | firstName: 'John',
157 | lastName: 'Smith',
158 | age: 48
159 | });
160 | assert.exist(person);
161 | assert.equal(person.firstName, 'John');
162 | assert.equal(person.lastName, 'Smith');
163 | id1 = [person.firstName, person.lastName];
164 | });
165 |
166 | it("should save new", function () {
167 | var person = new Person({
168 | firstName: 'Jane',
169 | lastName: 'Doe',
170 | age: 50
171 | });
172 |
173 | person.saveSync();
174 | assert.exist(person);
175 | assert.equal(person.firstName, 'Jane');
176 | assert.equal(person.lastName, 'Doe');
177 | id2 = [person.firstName, person.lastName];
178 | });
179 |
180 | it("should get person1", function () {
181 | var person = Person.getSync(id1[0], id1[1]);
182 | assert.exist(person);
183 | assert.equal(person.firstName, 'John');
184 | assert.equal(person.lastName, 'Smith');
185 | });
186 |
187 | it("should get person2", function () {
188 | var person = Person.getSync(id2[0], id2[1]);
189 | assert.exist(person);
190 | assert.equal(person.firstName, 'Jane');
191 | assert.equal(person.lastName, 'Doe');
192 | });
193 |
194 | it("should find", function () {
195 | var person = Person.oneSync({
196 | firstName: 'Jane'
197 | });
198 | assert.exist(person);
199 | assert.equal(person.firstName, 'Jane');
200 | assert.equal(person.lastName, 'Doe');
201 | });
202 |
203 | it("should update", function () {
204 | var person = Person.oneSync({
205 | firstName: 'Jane'
206 | });
207 | assert.exist(person);
208 |
209 | person.firstName = 'Jeniffer';
210 | person.saveSync();
211 |
212 | assert.equal(person.firstName, 'Jeniffer');
213 | assert.equal(person.lastName, 'Doe');
214 |
215 | var freshPerson = Person.getSync(person.firstName, person.lastName);
216 | assert.exist(freshPerson);
217 |
218 | assert.equal(freshPerson.firstName, 'Jeniffer');
219 | assert.equal(freshPerson.lastName, 'Doe');
220 |
221 | freshPerson.lastName = 'Dee';
222 | freshPerson.saveSync();
223 |
224 | assert.equal(freshPerson.firstName, 'Jeniffer');
225 | assert.equal(freshPerson.lastName, 'Dee');
226 |
227 | var jennifer = Person.getSync(freshPerson.firstName, freshPerson.lastName);
228 |
229 | assert.equal(jennifer.firstName, 'Jeniffer');
230 | assert.equal(jennifer.lastName, 'Dee');
231 | });
232 |
233 | it("should count", function () {
234 | var person = Person.createSync({
235 | firstName: 'Greg',
236 | lastName: 'McDoofus',
237 | age: 30
238 | });
239 |
240 | var count = Person.find({
241 | firstName: 'Greg',
242 | lastName: 'McDoofus'
243 | }).countSync();
244 | assert.equal(count, 1);
245 | });
246 |
247 | it("should chain delete", function () {
248 | var person = Person.createSync({
249 | firstName: 'Alfred',
250 | lastName: 'McDoogle',
251 | age: 50
252 | });
253 |
254 | var count = Person.find({
255 | firstName: 'Alfred',
256 | lastName: 'McDoogle'
257 | }).countSync();
258 | assert.equal(count, 1);
259 |
260 | Person.find({
261 | firstName: 'Alfred',
262 | lastName: 'McDoogle'
263 | }).removeSync();
264 |
265 | var count = Person.find({
266 | firstName: 'Alfred',
267 | lastName: 'McDoogle'
268 | }).countSync();
269 | assert.equal(count, 0);
270 | });
271 | });
272 | });
--------------------------------------------------------------------------------
/test/integration/property.js:
--------------------------------------------------------------------------------
1 | var ORM = require("../..");
2 | var Property = ORM.Property;
3 |
4 | describe("Property", function () {
5 | it("passing String should return type: 'text'", function () {
6 | assert.equal(Property.normalize({
7 | prop: String,
8 | customTypes: {},
9 | settings: ORM.settings,
10 | name: 'abc'
11 | }).type, "text");
12 | });
13 | it("passing Number should return type: 'number'", function () {
14 | assert.equal(Property.normalize({
15 | prop: Number,
16 | customTypes: {},
17 | settings: ORM.settings,
18 | name: 'abc'
19 | }).type, "number");
20 | });
21 | it("passing deprecated rational: false number should return type: 'integer'", function () {
22 | assert.equal(Property.normalize({
23 | prop: {
24 | type: 'number',
25 | rational: false
26 | },
27 | customTypes: {},
28 | settings: ORM.settings,
29 | name: 'abc'
30 | }).type, "integer");
31 | });
32 |
33 | it("passing Boolean should return type: 'boolean'", function () {
34 | assert.equal(Property.normalize({
35 | prop: Boolean,
36 | customTypes: {},
37 | settings: ORM.settings,
38 | name: 'abc'
39 | }).type, "boolean");
40 | });
41 | it("passing Date should return type: 'date'", function () {
42 | assert.equal(Property.normalize({
43 | prop: Date,
44 | customTypes: {},
45 | settings: ORM.settings,
46 | name: 'abc'
47 | }).type, "date");
48 | });
49 | it("passing Object should return type: 'object'", function () {
50 | assert.equal(Property.normalize({
51 | prop: Object,
52 | customTypes: {},
53 | settings: ORM.settings,
54 | name: 'abc'
55 | }).type, "object");
56 | });
57 | it("passing Buffer should return type: 'binary'", function () {
58 | assert.equal(Property.normalize({
59 | prop: Buffer,
60 | customTypes: {},
61 | settings: ORM.settings,
62 | name: 'abc'
63 | }).type, "binary");
64 | });
65 | it("passing an Array of items should return type: 'enum' with list of items", function () {
66 | var prop = Property.normalize({
67 | prop: [1, 2, 3],
68 | customTypes: {},
69 | settings: ORM.settings,
70 | name: 'abc'
71 | })
72 |
73 | assert.equal(prop.type, "enum");
74 | assert.propertyVal(prop.values, "length", 3);
75 | });
76 | describe("passing a string type", function () {
77 | it("should return type: ", function () {
78 | assert.equal(Property.normalize({
79 | prop: "text",
80 | customTypes: {},
81 | settings: ORM.settings,
82 | name: 'abc'
83 | }).type, "text");
84 | });
85 | it("should accept: 'point'", function () {
86 | assert.equal(Property.normalize({
87 | prop: "point",
88 | customTypes: {},
89 | settings: ORM.settings,
90 | name: 'abc'
91 | }).type, "point");
92 | });
93 |
94 | describe("if not valid", function () {
95 | it("should throw", function () {
96 | assert.throws(function () {
97 | Property.normalize({
98 | prop: "string",
99 | customTypes: {},
100 | settings: ORM.settings,
101 | name: 'abc'
102 | })
103 | });
104 | });
105 | });
106 | });
107 | it("should not modify the original property object", function () {
108 | var original = {
109 | type: 'text',
110 | required: true
111 | };
112 |
113 | var normalized = Property.normalize({
114 | prop: original,
115 | customTypes: {},
116 | settings: ORM.settings,
117 | name: 'abc'
118 | });
119 |
120 | original.test = 3;
121 | assert.strictEqual(normalized.test, undefined);
122 | });
123 | });
--------------------------------------------------------------------------------
/test/integration/settings.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 | var Settings = ORM.Settings;
4 |
5 | describe("Settings", function () {
6 | describe("changed on connection instance", function () {
7 | it("should not change global defaults", function () {
8 | var setting = 'instance.returnAllErrors';
9 | var defaultValue = ORM.settings.get(setting);
10 |
11 | var db = helper.connect();
12 | db.settings.set(setting, !defaultValue);
13 | db.closeSync();
14 |
15 | db = helper.connect();
16 | assert.equal(db.settings.get(setting), defaultValue);
17 | db.closeSync();
18 | });
19 | });
20 |
21 | describe("#get", function () {
22 | var settings, returned;
23 |
24 | beforeEach(function () {
25 | settings = new Settings.Container({
26 | a: [1, 2]
27 | });
28 | returned = null;
29 | });
30 |
31 | it("should clone everything it returns", function () {
32 | returned = settings.get('*');
33 | returned.a = 123;
34 |
35 | assert.deepEqual(settings.get('a'), [1, 2]);
36 | });
37 |
38 | it("should deep clone everything it returns", function () {
39 | returned = settings.get('*');
40 | returned.a.push(3);
41 |
42 | assert.deepEqual(settings.get('a'), [1, 2]);
43 | });
44 | });
45 |
46 | describe("manipulating:", function () {
47 | var testFunction = function testFunction() {
48 | return "test";
49 | };
50 | var settings = new Settings.Container({});
51 |
52 | describe("some.sub.object = 123.45", function () {
53 | before(function () {
54 | settings.set("some.sub.object", 123.45);
55 | });
56 |
57 | it("should be 123.45", function () {
58 | assert.equal(settings.get("some.sub.object"), 123.45);
59 | });
60 | });
61 |
62 | describe("some....object = testFunction", function () {
63 | before(function () {
64 | settings.set("some....object", testFunction);
65 | });
66 |
67 | it("should be testFunction", function () {
68 | assert.equal(settings.get("some....object"), testFunction);
69 | });
70 | });
71 |
72 | describe("not setting some.unknown.object", function () {
73 | it("should be undefined", function () {
74 | assert.equal(settings.get("some.unknown.object"), undefined);
75 | });
76 | });
77 |
78 | describe("unsetting some.sub.object", function () {
79 | before(function () {
80 | settings.unset("some.sub.object");
81 | });
82 |
83 | it("should be undefined", function () {
84 | assert.equal(settings.get("some.sub.object"), undefined);
85 | });
86 | });
87 |
88 | describe("unsetting some....object", function () {
89 | before(function () {
90 | settings.unset("some....object");
91 | });
92 |
93 | it("should be undefined", function () {
94 | assert.equal(settings.get("some....object"), undefined);
95 | });
96 | });
97 |
98 | describe("unsetting some.*", function () {
99 | before(function () {
100 | settings.unset("some.*");
101 | });
102 |
103 | it("should return undefined for any 'some' sub-element", function () {
104 | assert.equal(settings.get("some.other.stuff"), undefined);
105 | });
106 | it("should return an empty object for some.*", function () {
107 | assert.isObject(settings.get("some.*"));
108 | assert.propertyVal(Object.keys(settings.get("some.*")), 'length', 0);
109 | });
110 | it("should return an empty object for some", function () {
111 | assert.isObject(settings.get("some"));
112 | assert.propertyVal(Object.keys(settings.get("some")), 'length', 0);
113 | });
114 | });
115 | });
116 | });
--------------------------------------------------------------------------------
/test/integration/smart-types.js:
--------------------------------------------------------------------------------
1 | var helper = require('../support/spec_helper');
2 | var ORM = require('../../');
3 |
4 | describe("Smart types", function () {
5 | var db = null;
6 | var User = null;
7 | var Profile = null;
8 | var Post = null;
9 | var Group = null;
10 |
11 | var setup = function () {
12 | return function () {
13 | User = db.define("user", {
14 | username: {
15 | type: 'text',
16 | size: 64
17 | },
18 | password: {
19 | type: 'text',
20 | size: 128
21 | }
22 | }, {
23 | id: 'username'
24 | });
25 |
26 | Profile = User.extendsTo("profile", {
27 | firstname: String,
28 | lastname: String
29 | }, {
30 | reverse: 'user',
31 | required: true
32 | });
33 |
34 | Group = db.define("group", {
35 | name: {
36 | type: 'text',
37 | size: 64
38 | }
39 | }, {
40 | id: 'name'
41 | });
42 | Group.hasMany('users', User, {}, {
43 | reverse: 'groups'
44 | });
45 |
46 | Post = db.define("post", {
47 | content: String
48 | }, {
49 |
50 | });
51 | Post.hasOne('user', User, {
52 | reverse: 'posts'
53 | });
54 |
55 | ORM.singleton.clear();
56 | return helper.dropSync([User, Profile, Group, Post], function () {
57 | var billy = User.createSync({
58 | username: 'billy',
59 | password: 'hashed password'
60 | });
61 |
62 | var profile = billy.setProfileSync(new Profile({
63 | firstname: 'William',
64 | lastname: 'Franklin'
65 | }));
66 | var groups = billy.addGroupsSync([new Group({
67 | name: 'admins'
68 | }), new Group({
69 | name: 'developers'
70 | })]);
71 | var posts = billy.setPostsSync(new Post({
72 | content: 'Hello world!'
73 | }));
74 | });
75 | };
76 | };
77 |
78 | before(function () {
79 | db = helper.connect();
80 | });
81 |
82 | after(function () {
83 | return db.closeSync();
84 | });
85 |
86 | describe("extends", function () {
87 | before(setup());
88 |
89 | it("should be able to get extendsTo with custom id", function () {
90 | var billy = User.getSync('billy');
91 | assert.exist(billy);
92 |
93 | var profile = billy.getProfileSync();
94 | assert.exist(profile);
95 | assert.equal(profile.firstname, 'William');
96 | assert.equal(profile.lastname, 'Franklin');
97 | });
98 |
99 | it("should be able to get hasOne with custom id", function () {
100 | var billy = User.getSync('billy');
101 | assert.exist(billy);
102 |
103 | var posts = billy.getPostsSync();
104 | assert.exist(posts);
105 | assert.equal(posts.length, 1);
106 | assert.equal(posts[0].content, 'Hello world!');
107 | });
108 |
109 | it("should be able to get hasMany with custom id", function () {
110 | var billy = User.getSync('billy');
111 |
112 | assert.exist(billy);
113 |
114 | var groups = billy.getGroupsSync();
115 |
116 | assert.exist(groups);
117 | assert.equal(groups.length, 2);
118 | });
119 |
120 | });
121 | });
--------------------------------------------------------------------------------
/test/support/opts.js:
--------------------------------------------------------------------------------
1 | module.exports = 'sqlite:test.db';
--------------------------------------------------------------------------------
/test/support/spec_helper.js:
--------------------------------------------------------------------------------
1 | var ORM = require('../../');
2 |
3 | module.exports.connect = function () {
4 | return ORM.connectSync("sqlite:test.db");
5 | // return ORM.connectSync("mysql://root@localhost/test");
6 | };
7 |
8 | module.exports.dropSync = function (models, done) {
9 | if (!Array.isArray(models)) {
10 | models = [models];
11 | }
12 |
13 | models.forEach(function (item) {
14 | item.dropSync();
15 | item.syncSync();
16 | });
17 |
18 | if (done)
19 | done();
20 | };
--------------------------------------------------------------------------------