1 | _ _ _ _ _
|
2 | | \ | | ___ __| |_ _| | __ _| |_ ___ _ __
|
3 | | \| |/ _ \ / _` | | | | |/ _` | __/ _ \| '__|
|
4 | | |\ | (_) | (_| | |_| | | (_| | || (_) | |
|
5 | |_| \_|\___/ \__,_|\__,_|_|\__,_|\__\___/|_| V0.0.19
|
6 |
|
7 | [![Build Status](https://travis-ci.org/Champii/N.svg?branch=master)](https://travis-ci.org/Champii/Nodulator) (Master)
|
8 |
|
9 | [![Build Status](https://travis-ci.org/Champii/N.svg?branch=develop)](https://travis-ci.org/Champii/Nodulator) (Develop)
|
10 |
|
11 | [![NPM](https://nodei.co/npm/nodulator.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/nodulator/)
|
12 |
|
13 | [![NPM](https://nodei.co/npm-dl/nodulator.png?months=1)](https://nodei.co/npm/nodulator/)
|
14 |
|
15 | ##### Under heavy development
|
16 |
|
17 | ___
|
18 | ## Concept
|
19 |
|
20 | `Nodulator` is designed to make it more easy to create highly modulable
|
21 | applications, built with REST APIs and with integrated ORM in CoffeeScript.
|
22 |
|
23 | You must understand [express](https://github.com/strongloop/express) basics for routing
|
24 |
|
25 | Open [exemples](https://github.com/Champii/Nodulator/blob/master/exemples)
|
26 | folder to see a full working exemple in JavaScript, CoffeeScript and LiveScript.
|
27 |
|
28 | Released under [GPLv2](https://github.com/Champii/Nodulator/blob/master/LICENSE.txt)
|
29 |
|
30 | ### Following documentation is almost deprecated or incomplete and will be removed soon. Please prefer the most updated documentation at [http://nodulator.champii.io](http://nodulator.champii.io)
|
31 |
|
32 | ___
|
33 | ## Jump To
|
34 |
|
35 | - [Philosophy](#philosophy)
|
36 | - [Features](#features)
|
37 | - [Compatible modules](#compatible-modules)
|
38 | - [Installation](#installation)
|
39 | - [Quick Start (TL;DR)](#quick-start-tl;dr)
|
40 | - [Configuration](#configuration)
|
41 | - [Resource](#resource)
|
42 | - [Basics](#basics)
|
43 | - [Init](#init)
|
44 | - [Class methods](#class-methods)
|
45 | - [Instance methods](#instance-methods)
|
46 | - [Promises or Callbacks](#promises-or-callbacks)
|
47 | - [Schema](#schema)
|
48 | - [Schema and Validation](#schema-and-validation)
|
49 | - [Model association](#model-association)
|
50 | - [LocalKey or DistantKey](#localkey-vs-distant-key)
|
51 | - [Depth](#depth)
|
52 | - [Overriding and Inheritance](#overriding-and-inheritance)
|
53 | - [Override default behaviour](#override-default-behaviour)
|
54 | - [Abstract Class](#abstract-class)
|
55 | - [Complex inheritance system](#complex-inheritance-system)
|
56 | - [Route](#route)
|
57 | - [Route Object](#route-object)
|
58 | - [Request Object](#request-object)
|
59 | - [Existing Routes](#existing-routes)
|
60 | - [SingleRoute](#single-route-object)
|
61 | - [MultiRoute](#multi-route-object)
|
62 | - [Route Inheritance](#route-inheritance)
|
63 | - [Standalone Routes](#standalone-routes)
|
64 | - [Wrappers](#route)
|
65 | - [Flip Done](#flip-done)
|
66 | - [Promise](#promise)
|
67 | - [Watch Args](#watch-args)
|
68 | - [Reactive Values](#reactive-values)
|
69 | - [Intro](#intro)
|
70 | - [Resources](#resources)
|
71 | - [Errors](#errors)
|
72 | - [DB Systems](#db-systems)
|
73 | - [Abstraction](#abstraction)
|
74 | - [Mysql](#mysql)
|
75 | - [MongoDB](#mongodb)
|
76 | - [SqlMem](#sqlmem)
|
77 | - [Other Stuff](#other-stuff)
|
78 | - [Bus](#bus)
|
79 | - [Modules](#modules)
|
80 | - [Usage](#usage)
|
81 | - [Module Hacking](#module-hacking)
|
82 | - [Project Generation](#project-generation)
|
83 | - [Developers](#developers)
|
84 | - [Contributors](#contributors)
|
85 | - [TODO](#todo)
|
86 | - [Changelog](#changelog)
|
87 |
|
88 | ___
|
89 | ## Philosophy
|
90 |
|
91 | `Nodulator` is a project that is trying to make a big overlay to every
|
92 | traditional packages used to make REST client/server applications in CoffeeScript.
|
93 |
|
94 | Its main goal is to give developers a complex REST routing system, an ORM and
|
95 | high-level modules, encapsulating every classic behaviour needed to create
|
96 | complex projects.
|
97 |
|
98 | Its core provides everything needed to build powerfull and highly modulable
|
99 | REST APIs, and allow the developer to reuse his code through every projects.
|
100 |
|
101 | With this framework, you will never loose 10 or 20 hours anymore boostraping a
|
102 | project from scratch or looking for the right technology to implement.
|
103 |
|
104 | You will never have headache anymore trying to combine `socket.io` and `passport`
|
105 | to keep track of your session with your sockets (for exemple),
|
106 | or you will never have to consider assets management,
|
107 | and with the integrated [Project Generation](#project-generation) you will never
|
108 | need to manage your `Nodulator` modules dependencies.
|
109 |
|
110 | You need to add authentication logic to your open/public API ? Look for
|
111 | [Nodulator-Account](https://github.com/Champii/Nodulator-Account) !
|
112 |
|
113 | You need to add socket.io support ? Look for
|
114 | [Nodulator-Socket](https://github.com/Champii/Nodulator-Socket) !
|
115 |
|
116 | If you don't find your desired module, just [build it](#modules) !
|
117 |
|
118 | `Nodulator` is like a lego game, instead of learning how to use a given
|
119 | technology and how to combine it with thoses you often use,
|
120 | it allows you to manipulate simple concepts like adding a `Account` concept to
|
121 | your application(for exemple), and so adding authentication and permission logic to your app.
|
122 |
|
123 | Also, each brick or layer of a `Nodulator` application is highly linked to every others.
|
124 | For exemple, when you add `Nodulator-Account` module to your app, if you have
|
125 | already included `Nodulator-Angular` it will automaticaly add everything needed
|
126 | to handle angular authentication (it will add a separate view, some directives
|
127 | and a user service). Have you added `Nodulator-Socket` ?
|
128 | So `Nodulator-Angular` will also be highly linked to your server's models,
|
129 | by providing a socket interface to your server `Resource`.
|
130 |
|
131 | Check the [Jump To](#jump-to) section !
|
132 |
|
133 | ___
|
134 | ## Features
|
135 |
|
136 | - [LiveScript](http://livescript.net/)
|
137 | - Integrated ORM
|
138 | - Integrated Routing system (with express, and highly linked with ORM)
|
139 | - Multiple DB Systems
|
140 | - Complex inheritance system
|
141 | - Chainable async calls
|
142 | - Modulable
|
143 | - Project generation
|
144 | - Cache
|
145 | - Schema-less/Schema-full models
|
146 | - Model validation
|
147 | - Model association (rails style) and automatic retrieval
|
148 | - Models and associations over different DB systems
|
149 | - Reactive values [Hacktiv](https://github.com/Champii/Hacktiv)
|
150 | - Promises or Callbacks
|
151 | - Fliped callback parameters
|
152 | - Log and Debug system
|
153 | - Console mode
|
154 |
|
155 | ___
|
156 | ### Compatible modules
|
157 |
|
158 | - [Nodulator-Assets](https://github.com/Champii/Nodulator-Assets):
|
159 | - Automatic assets management
|
160 | - [Nodulator-Socket](https://github.com/Champii/Nodulator-Socket):
|
161 | - Socket.io implementation for Nodulator
|
162 | - [Nodulator-Angular](https://github.com/Champii/Nodulator-Angular):
|
163 | - Angular implementation for Nodulator
|
164 | - Inheritance system
|
165 | - Integrated and linked SocketIO
|
166 | - Assets management
|
167 | - [Nodulator-Account](https://github.com/Champii/Nodulator-Account):
|
168 | - Authentication with passport
|
169 | - Permissions management
|
170 | - Sessions
|
171 | - Nodulator-Angular integration
|
172 |
|
173 | ___
|
174 | ## Installation
|
175 |
|
176 | Just run :
|
177 | ```
|
178 | npm install nodulator
|
179 | ```
|
180 | Or check the [Project Generation](#project-generation) section
|
181 |
|
182 | After you can require `Nodulator` as a module :
|
183 |
|
184 | ```coffeescript
|
185 | N = require 'nodulator'
|
186 | ```
|
187 |
|
188 | ___
|
189 | ## Quick Start TL;DR
|
190 |
|
191 | Here is the quickiest way to play around `Nodulator`
|
192 |
|
193 | ```coffeescript
|
194 | _ = require 'underscore'
|
195 | N = require 'nodulator'
|
196 |
|
197 | class PlayerRoute extends N.Route.MultiRoute
|
198 | Config: ->
|
199 |
|
200 | # We create: GET => /api/1/{resource_name}/usernames
|
201 | # Get a list of every players' usernames
|
202 | # There is a @resource property, containing attached Resource class
|
203 | @Get '/usernames' ~> @resource.ListUsernames!
|
204 |
|
205 | # We call super() to apply N.Route.MultiRoute behaviour
|
206 | # We called '/usernames' route before, so it won't be override by
|
207 | # default route GET => /api/1/{resource_name}/:id
|
208 | super!
|
209 |
|
210 | # We create: PUT => /api/1/{resource_name}/:id/levelUp
|
211 | @Put '/:id/levelUp' ~> it.instance.LevelUp!
|
212 |
|
213 | # We create a resource, and we attach the PlayerRoute
|
214 | class Players extends N 'player', PlayerRoute
|
215 |
|
216 | # We create a LevelUp method
|
217 | # If you dont provide done callback,
|
218 | # the wrap promise will give one to fill and return as promise
|
219 | LevelUp: @_WrapPromise (done) ->
|
220 | @level++
|
221 | @Save done
|
222 |
|
223 | # And a class method to get a list of usernames
|
224 | @ListUsernames: @_WrapPromise (done) ->
|
225 | @List (err, players) ->
|
226 | return done err if err?
|
227 |
|
228 | done null, _(players).pluck 'username'
|
229 |
|
230 |
|
231 | ```
|
232 |
|
233 | Go inside your project folder, copy this POC in a `test.coffee` file and type in:
|
234 |
|
235 | `$> coffee test.coffee`
|
236 |
|
237 | It will run your project on port `3000` by default
|
238 |
|
239 | Then open your favorite REST API Client ([Postman for Chrome](https://www.google.fr/url?sa=t&rct=j&q=&esrc=s&source=web&cd=1&cad=rja&uact=8&ved=0CCMQFjAA&url=https%3A%2F%2Fchrome.google.com%2Fwebstore%2Fdetail%2Fpostman-rest-client%2Ffdmmgilgnpjigdojojpjoooidkmcomcm%3Fhl%3Den&ei=Tu6iVMqpJZDaatmGgOAL&usg=AFQjCNHaecLwAKk91gpdCY_y1x_ViIrHwQ&sig2=3FcPD7i2Id8La26xJt4PJA&bvm=bv.82001339,d.d2s) is my favorite)
|
240 |
|
241 | and try the following routes :
|
242 |
|
243 | ```
|
244 | (Assuming full url is always of the following form : "http://localhost:3000/api/1/[...]")
|
245 | Each route is of the following form :
|
246 |
|
247 | {VERB} {URL} ({PARAMS}) => {ANSWER}
|
248 |
|
249 | POST '/api/1/players' {username: 'test1', level: 1} => {id: 1, username: 'test1', level: 1}
|
250 | POST '/api/1/players' {username: 'test2', level: 1} => {id: 2, username: 'test2', level: 1}
|
251 |
|
252 | GET '/api/1/players' => [{id: 1, username: 'test1', level: 1},
|
253 | {id: 2, username: 'test2', level: 1}]
|
254 |
|
255 | GET '/api/1/players/1' => {id: 1, username: 'test1', level: 1}
|
256 | GET '/api/1/players/2' => {id: 2, username: 'test2', level: 1}
|
257 |
|
258 | PUT '/api/1/players/2/levelUp' {} => {id: 2, username: 'test2', level: 2}
|
259 | PUT '/api/1/players/2/levelUp' {} => {id: 2, username: 'test2', level: 3}
|
260 |
|
261 | GET '/api/1/players/usernames' => ['test1', 'test2']
|
262 |
|
263 | PUT '/api/1/players/2' {username: 'notAUsername'} => {id: 2, username: 'notAUsername', level: 3}
|
264 |
|
265 | GET '/api/1/players/usernames' => ['test1', 'notAUsername']
|
266 |
|
267 | DELETE '/api/1/players/1' {} => {id: 1, username: 'test1', level: 1}
|
268 |
|
269 | GET '/api/1/players/usernames' => ['notAUsername']
|
270 | ```
|
271 |
|
272 | ___
|
273 | ## Configuration
|
274 |
|
275 | First of all, the configuration process is absolutely optional.
|
276 |
|
277 | If you don't give Nodulator a config, it will assume you want to use
|
278 | [SqlMem](#sqlmem) DB system, with no persistance at all. Usefull for heavy tests periods.
|
279 |
|
280 | If you prefere to use a persistant system, here is the procedure :
|
281 |
|
282 | ```coffeescript
|
283 | N = require 'nodulator'
|
284 |
|
285 | N.Config
|
286 | dbType: 'Mongo' # You can select 'SqlMem' or 'Mongo' or 'Mysql'
|
287 | dbAuth: # Fields needed if Mongo or Mysql
|
288 | host: 'localhost'
|
289 | database: 'test'
|
290 | port: 27017 # From there, can be ignored. Default values taken
|
291 | user: 'test' # |
|
292 | pass: 'test' # |_
|
293 | ```
|
294 |
|
295 | You can also provide a 'store' property in order to use `Redis` to manage sessions:
|
296 |
|
297 | ```coffeescript
|
298 | N = require 'nodulator'
|
299 |
|
300 | N.Config
|
301 | store:
|
302 | type: 'redis'
|
303 | host: 'localhost' # <- default value, can be ignored
|
304 | ```
|
305 |
|
306 | If ommited, sessions will be memory based (not recommended)
|
307 |
|
308 | You can flip the arguments of the default `done` callback by specifying a `flipDone: true` parameter
|
309 |
|
310 | ```coffeescript
|
311 | N.Config
|
312 | flipDone: true
|
313 | ```
|
314 |
|
315 | `Nodulator` provides 2 main Objects :
|
316 |
|
317 | ```coffeescript
|
318 | N
|
319 | N.Route
|
320 | ```
|
321 |
|
322 | ___
|
323 | ## Resource
|
324 |
|
325 | #### Basics
|
326 |
|
327 | A `Resource` is a class permitting to retrive and save a model from a DB.
|
328 |
|
329 | Here is an exemple of creating a `Resource`
|
330 |
|
331 | ```coffeescript
|
332 | Players = N 'player'
|
333 | ```
|
334 |
|
335 | Here, it creates a `PlayerResource`, linked with a `players` table in DB (if any)
|
336 |
|
337 | You can pass several params to `N` :
|
338 |
|
339 | ```coffeescript
|
340 | N name [, Route] [, config]
|
341 | ```
|
342 |
|
343 | You can attach a [Route](#route) and/or a config object to a `Resource`.
|
344 |
|
345 |
|
346 | #### Init
|
347 |
|
348 | This call prepare the `Resource`. All the `Nodulator`'s magic is
|
349 | inside this call.
|
350 |
|
351 | It is now optional to call Init(), as it will be called by the first Resource query.
|
352 |
|
353 | However, note that the Init() call can take the config parameter, in case of crossing
|
354 | Resource schema definition. You must call it befor the first query.
|
355 |
|
356 | Also, it returns the whole Resource for chaining purpose.
|
357 |
|
358 | #### Class methods
|
359 |
|
360 | Each `Resource` provides some 'Class methods' to manage the specific model in db :
|
361 |
|
362 | ```coffeescript
|
363 | Players.Create(blob, done)
|
364 | Players.Fetch(constraints, done)
|
365 | Players.List(constraints, done)
|
366 | Players.Delete(constraints, done)
|
367 | ```
|
368 | The `Create` method, like its name suggests, can add a row to a given Resource.
|
369 |
|
370 | ```coffeescript
|
371 | Players.Create {login: 'player1', age: 24}, (err, player) ->
|
372 | return console.error err if err?
|
373 |
|
374 | [...] # Do something with player instance
|
375 |
|
376 | Players.Create [
|
377 | {login: 'player1', age: 24}
|
378 | {login: 'player2', age: 68}
|
379 | {login: 'player3', age: 34}
|
380 | ], (err, players) -> [...]
|
381 | ```
|
382 |
|
383 | It automaticaly adds an `id` row to every new instance. Don't try to override it !
|
384 |
|
385 | The `Fetch` method can take an id and return a `Players` instance to `done` callback :
|
386 | It can take an id, an object or an array
|
387 |
|
388 | ```coffeescript
|
389 | Players.Fetch 1, (err, player) ->
|
390 | return console.error err if err?
|
391 |
|
392 | [...] # Do something with player instance
|
393 |
|
394 | Players.Fetch [1, 5], (err, players) -> [...]
|
395 | Players.Fetch {login: 'value'}, (err, player) -> [...]
|
396 | Players.Fetch [7, {age: 25}, 9, {login: 'test'}], (err, players) -> [...]
|
397 | ```
|
398 |
|
399 | The `Fetch` will return the first found row that pass the given object equality
|
400 |
|
401 | You can list every models from this `Resource` thanks to `List` call :
|
402 |
|
403 | ```coffeescript
|
404 | Players.List (err, players) ->
|
405 | return console.error err if err?
|
406 |
|
407 | [...] # players is an array of every Players instance
|
408 |
|
409 | Players.List {age: 26}, (err, players) -> [...]
|
410 | Players.List [{age: 26}, {age: 52}], (err, players) -> [...]
|
411 | # Be carefull, as List(obj) returns an array, List(array) returns an Array of Array
|
412 | ```
|
413 |
|
414 | You can delete the same way, except that Delete callback only have one error parameter.
|
415 |
|
416 | Be carefull, The `Delete` will erase the first found row that pass the given object equality
|
417 |
|
418 | ```coffeescript
|
419 | Players.Delete 1, (err) ->
|
420 | return console.error err if err?
|
421 |
|
422 |
|
423 | Players.Delete {login: 'toto'}, (err) -> [...]
|
424 | Players.Delete [9, {login: 'toto'}, 4, {age: 22}], (err) -> [...]
|
425 | ```
|
426 |
|
427 | Never use `new` operator directly on a `Resource`, else you might bypass the
|
428 | relationning system.
|
429 |
|
430 | #### Instance methods
|
431 |
|
432 | A player instance has some methods :
|
433 |
|
434 | ```
|
435 | player.Save(done)
|
436 | Save the model in DB. The callback take 2 arguments : (err, instance) ->
|
437 |
|
438 | player.Delete(done)
|
439 | Delete the model from the DB. The callback take 1 argument : (err) ->
|
440 |
|
441 | player.Serialize()
|
442 | Get every object properties, and return it in a new object.
|
443 | Generaly used to get what to be saved in DB.
|
444 |
|
445 | player.ToJSON()
|
446 | By default, it calls Serialize().
|
447 | Generaly used to get what to send to client.
|
448 |
|
449 | player.ExtendSafe(blob)
|
450 | This method extend the Resource with the given blob,
|
451 | But it keeps safe every Associations and vital stuff.
|
452 | ```
|
453 |
|
454 | Every Instance and Class methods can be chained, they all returns their 'this'. (Except when using Promises,
|
455 | read the corresponding chapter)
|
456 |
|
457 | ___
|
458 | ## Promises or Callbacks
|
459 |
|
460 | Every call that take a `done` callback can be expressed by Promises:
|
461 |
|
462 | ```coffeescript
|
463 | Players.Fetch 1, (err, player) ->
|
464 | return console.error err if err?
|
465 |
|
466 | [...] # Do something with player instance
|
467 | ```
|
468 |
|
469 | Is equivalent to :
|
470 |
|
471 | ```coffeescript
|
472 | Players.Fetch 1
|
473 | .then (player) -> [...] # Do something with player instance
|
474 | .fail (err) -> console.error err
|
475 |
|
476 | ```
|
477 |
|
478 | If you omit the callback, it will return a Promise. If you pass a callback, it will return `this` for chaining purpose.
|
479 |
|
480 | ___
|
481 | ## Schema
|
482 |
|
483 | #### Schema and Validation
|
484 |
|
485 | By default, every `Resource` is schema less. It means that you can put almost
|
486 | anything into your `Resource`.
|
487 |
|
488 | It can obviously be schema less only for DB systems that allows it. When using
|
489 | MySQL for exemple, you'll have to define a schema and validation rules
|
490 | if you don't want your server to answer raw SQL errors for non existant fields
|
491 |
|
492 | To make a `Resource` to respect a given schema, you just have to define a
|
493 | `schema` field into `Resource` configuration
|
494 |
|
495 | ```coffeescript
|
496 | config =
|
497 | schema:
|
498 | toto: 'array' #This can be an array of everything
|
499 | test: ['int'] #This MUST be an array of integer
|
500 | foo: 'int'
|
501 | bar:
|
502 | type: 'string'
|
503 | optional: true
|
504 | foobar:
|
505 | type: 'string'
|
506 | default: 'foobar'
|
507 | ```
|
508 |
|
509 | Differents types are
|
510 | - bool
|
511 | - int
|
512 | - string
|
513 | - date
|
514 | - email
|
515 | - array
|
516 |
|
517 | By default, each fields is required, but you can make one field optional with
|
518 | the `optional` field to `true` or presence of `default` field. It will never
|
519 | complain if this field is not present, but if it is, it will check for its validity.
|
520 |
|
521 |
|
522 | You can specify a type directly with a string, assuming that the given property
|
523 | will be required:
|
524 |
|
525 | ```coffeescript
|
526 | config =
|
527 | schema:
|
528 | foo: 'int'
|
529 | bar: 'string'
|
530 | ```
|
531 |
|
532 | #### Default and Virtual fields
|
533 |
|
534 | If you specify a `default` field, the `Resource` will auto-set its property
|
535 | if the value is not given.
|
536 |
|
537 | For exemple, with this Resource config :
|
538 |
|
539 | ```coffeescript
|
540 | config =
|
541 | schema:
|
542 | foo:
|
543 | type: 'int'
|
544 | default: 5
|
545 | ```
|
546 |
|
547 | If I dont provide a 'foo' value at Resource instanciation, it will take the
|
548 | one given by the 'default' field
|
549 |
|
550 | You can put a function in place of a default value. In this case, this function
|
551 | will be executed to get the default value at instanciation time.
|
552 |
|
553 | ```coffeescript
|
554 | config =
|
555 | schema:
|
556 | foo:
|
557 | type: 'int'
|
558 | default: (obj) ->
|
559 | ```
|
560 |
|
561 |
|
562 | #### Model association
|
563 |
|
564 | You can make associations between `Resource`. For making a `Resource` to be
|
565 | automaticaly fetched when querying another, you can add it to its schema :
|
566 |
|
567 | ```coffeescript
|
568 |
|
569 | Bars = N 'bar', {schema: barProperty: 'string'}
|
570 |
|
571 | config =
|
572 | schema:
|
573 | foo: 'int'
|
574 | barId: 'int'
|
575 | bar:
|
576 | type: Bars
|
577 | localKey: 'barId'
|
578 |
|
579 | Tests = N 'test', config
|
580 |
|
581 | # Fetch Tests with id == 1
|
582 | Tests.Fetch 1, (err, test) ->
|
583 | # Will be for exemple :
|
584 | # {
|
585 | # id: 1,
|
586 | # foo: 12,
|
587 | # barId: 1,
|
588 | # bar: {id: 1, barProperty: 'test'}
|
589 | # }
|
590 |
|
591 | ```
|
592 |
|
593 | If you want to retrive a collection of resource, you can wrap types in arrays instead:
|
594 |
|
595 | ```coffeescript
|
596 | config =
|
597 | schema:
|
598 | foo: 'int'
|
599 | barIds: ['int']
|
600 | bar:
|
601 | type: [Bars]
|
602 | localKey: 'barIds'
|
603 |
|
604 | Tests = N 'test', config
|
605 |
|
606 | # Fetch Tests with id == 1
|
607 | Tests.Fetch 1, (err, test) ->
|
608 | # Will be for exemple :
|
609 | # {
|
610 | # id: 1,
|
611 | # foo: 12,
|
612 | # barIds: [1, 2],
|
613 | # bar: [{id: 1, barProperty: 'test'},
|
614 | # {id: 2, barProperty: 'test2'}]
|
615 | # }
|
616 |
|
617 | ```
|
618 |
|
619 | #### localKey vs distantKey
|
620 |
|
621 | When you want to retrieve a Resource based on an id property inside the object,
|
622 | you must use the 'localKey' property. It will fetch for the given Resource Type
|
623 | with the corresponding id from 'localKey' field
|
624 |
|
625 | If can also retrieve a Resource that have a property containing the actual instance id:
|
626 |
|
627 | ```coffeescript
|
628 | Bars = N 'bar', {schema: testId: 'int'}
|
629 |
|
630 | config =
|
631 | schema:
|
632 | foo: 'int'
|
633 | bar:
|
634 | type: Bars
|
635 | distantKey: 'testId'
|
636 |
|
637 | Tests = N 'test', config
|
638 |
|
639 | Tests.Create {foo: 12}
|
640 | .then -> Bars.Create {testId: 1} #We assume its the first created resource
|
641 | .then -> Tests.Fetch 1 #We know its the first resource created
|
642 | .then (test) -> #Here test have a 'bar' property with testId == 1
|
643 |
|
644 | ```
|
645 |
|
646 | Given the last exemple, when you get a Test instance with id == 1, it will have a
|
647 | field 'bar' with the Bars instance that have testId == 1
|
648 |
|
649 | #### Depth
|
650 |
|
651 | When you have associated Resources, you can choose the depth of your queries.
|
652 |
|
653 | By default, the depth is at 1 for every Resource. It means that it will fetch only
|
654 | one level of Resource maximum.
|
655 |
|
656 | If you want a deeper level of retrieving, you can set the 'maxDepth' property of
|
657 | a Resource configuration:
|
658 |
|
659 | ```coffeescript
|
660 | config =
|
661 | maxDepth: 3
|
662 | ```
|
663 |
|
664 | Or if you prefer change it at runtime and make it inheritable, you can act on
|
665 | the Resource.DEFAULT_DEPTH field.
|
666 |
|
667 | Note that the config one has priority.
|
668 |
|
669 | ____
|
670 | ## Overriding and Inheritance
|
671 |
|
672 | You can inherit from a `Resource` to override or enhance its default behaviour,
|
673 | or to make a complex class inheritance system built on `Resource`
|
674 |
|
675 | #### Override default behaviour
|
676 | In CoffeeScript its pretty easy:
|
677 |
|
678 | ```coffeescript
|
679 | class Units extends N 'unit'
|
680 |
|
681 | # We create a new instance method
|
682 | LevelUp: (done) ->
|
683 | @level++
|
684 | @Save done
|
685 |
|
686 | # We override default 'List' method
|
687 | @List: (done) ->
|
688 | @ListBy {life: 10}, (err, units) ->
|
689 | return done err if err?
|
690 |
|
691 | done null, units
|
692 |
|
693 | ```
|
694 |
|
695 | #### Abstract class
|
696 |
|
697 | You can define an abstract class, that won't be attached to any model in DB or
|
698 | any `Route`
|
699 |
|
700 | ```coffeescript
|
701 | class Units extends N 'unit', {abstract: true}
|
702 | [...]
|
703 |
|
704 | ```
|
705 |
|
706 | Of course, abstract classes are only designed to be inherited. (Please note that
|
707 | they can't have a `Route` attached)
|
708 |
|
709 | #### Complex inheritance system
|
710 |
|
711 | Given the last exemple, here is a class that inherits from `Units`
|
712 |
|
713 | ```coffeescript
|
714 | # Note the call to 'Extend()' method
|
715 | class Players extends Units.Extend 'player'
|
716 |
|
717 | # Give Players a new beheviour
|
718 | NewBehaviour: (args, done) ->
|
719 | [...]
|
720 |
|
721 | # Overriding existing Units LevelUp()
|
722 | LevelUp: (done) ->
|
723 | [...]
|
724 |
|
725 | ```
|
726 |
|
727 | You can call the Extend() method either from a full `Resource` or from an
|
728 | `abstract` one.
|
729 |
|
730 | Please note that if both parent and child are full `Resource`, both will have
|
731 | corresponding model available from ORM (here `units` and `players`)
|
732 |
|
733 | So be carefull when creating extended `Resource`, and think about `abstract` !
|
734 |
|
735 | ___
|
736 | ## Route
|
737 |
|
738 | #### Route Object
|
739 |
|
740 | `Nodulator` provides a `Route` object, which can to be attached to a `Resource` object (or not)
|
741 | in order to describe routing process.
|
742 |
|
743 | There is 2 different ways to attach a `Resource` to a `Route`:
|
744 |
|
745 | You can bind a `Route` to a `Resource`
|
746 |
|
747 | ```coffeescript
|
748 | class Units extends N 'unit', N.Route
|
749 | ```
|
750 |
|
751 | Or you can bind a `Resource` to a `Route`
|
752 |
|
753 | ```coffeescript
|
754 | class UnitRoute extends N.Route
|
755 | resource: Units
|
756 |
|
757 | #In this case, you have to instanciate the Route yourself once
|
758 | route = new UnitRoute
|
759 | ```
|
760 |
|
761 | Every `Route` can be initiated and configured when its attached `Resource` is,
|
762 | else you must instantiate one yourself.
|
763 |
|
764 | Default `N.Route` do nothing. You have to inherit from it to describe routes :
|
765 |
|
766 | ```coffeescript
|
767 | class UnitRoute extends N.Route
|
768 |
|
769 | # Override the Config() method
|
770 | Config: ->
|
771 |
|
772 | # And never forget to call the super()
|
773 | super()
|
774 |
|
775 | # Here we define: GET => /api/1/{resource_name}/:id
|
776 | @Get '/:id', (req, res) =>
|
777 |
|
778 | # The @resource field points to attached Resource
|
779 | @resource.Fetch req.params.id, (err, unit) ->
|
780 | return res.status(500).send err if err?
|
781 |
|
782 | res.status(200).send unit.ToJSON()
|
783 |
|
784 | # Here we define: POST => /api/1/{resource_name}
|
785 | @Post (req, res) ->
|
786 | res.status(200).end()
|
787 | ```
|
788 |
|
789 | This `Route`, attached to a `Resource` (here `Units`) add 2 endpoints :
|
790 |
|
791 | ```
|
792 | GET => /api/1/units/:id
|
793 | POST => /api/1/units
|
794 | ```
|
795 |
|
796 | Each `Route` have to implement a `Config()` method, calling `super()` and
|
797 | defining routes thanks to 'verbs' route calls (@Get(), @Post(), @Put(), @Delete(), @All()).
|
798 |
|
799 | Here are all 'verb' route calls definition :
|
800 |
|
801 | ```coffeescript
|
802 | N.Route.All [endPoint = '/'], [middleware, [middleware, ...]], callback
|
803 | N.Route.Get [endPoint = '/'], [middleware, [middleware, ...]], callback
|
804 | N.Route.Post [endPoint = '/'], [middleware, [middleware, ...]], callback
|
805 | N.Route.Put [endPoint = '/'], [middleware, [middleware, ...]], callback
|
806 | N.Route.Delete [endPoint = '/'], [middleware, [middleware, ...]], callback
|
807 | ```
|
808 |
|
809 | #### Single Route Object
|
810 |
|
811 | Nodulator provides a predefined route system for lazy, adapted for Singleton
|
812 | `Resource`: `N.Route.SingleRoute`.
|
813 |
|
814 | It setups 2 routes (exemple when attached to a `Players`) :
|
815 |
|
816 | ```
|
817 | GET => /api/1/player => Fetch
|
818 | PUT => /api/1/player => Update
|
819 | ```
|
820 |
|
821 | This route system needs to have a resource with `id == 1` in your actual DB
|
822 | before startup time to work.
|
823 |
|
824 | If you don't have a `config.schema` property set in your `Resource`, it will
|
825 | create one for you at startup time.
|
826 |
|
827 | Else, `Nodulator` will throw an error and shutdown.
|
828 |
|
829 | If you use `SqlMem` DB system, you must add a 'default' value to each resource
|
830 | fields in order to add it at startup.
|
831 |
|
832 |
|
833 | #### Multi Route Object
|
834 |
|
835 | Nodulator provides also a standard route system for lazy : `N.Route.MultiRoute`.
|
836 | It allows you to handle your resources like its a big collection.
|
837 | It setups 5 routes (exemple when attached to a `Players`) :
|
838 |
|
839 | ```
|
840 | GET => /api/1/players => List
|
841 | POST => /api/1/players => Create
|
842 | GET => /api/1/players/:id => Get One
|
843 | PUT => /api/1/players/:id => Update
|
844 | DELETE => /api/1/players/:id => Delete
|
845 | ```
|
846 |
|
847 | Note that the first GET call accept query parameters for selection.
|
848 |
|
849 | #### Route Inheritance
|
850 |
|
851 | You can inherit from any route object :
|
852 |
|
853 | ```coffeescript
|
854 | class Test1Route extends N.Route
|
855 | class Test2Route extends N.Route.MultiRoute
|
856 | class Test3Route extends Test2Route
|
857 | class Test4Route extends Test3Route
|
858 | ```
|
859 | And you can override existing route by providing same association verb + url.
|
860 | Exemple :
|
861 |
|
862 | ```coffeescript
|
863 | class TestRoute extends N.Route.MultiRoute
|
864 | Config: ->
|
865 | super()
|
866 |
|
867 | # Here we override the default GET => /api/1/{resource_name}/:id
|
868 | @Get '/:id', (req, res) =>
|
869 | [...]
|
870 | ```
|
871 |
|
872 | #### Standalone Routes
|
873 |
|
874 | You can declare Route that dont belongs to any Resource.
|
875 | Instead, you define yourself every endpoints :
|
876 |
|
877 | ```coffeescript
|
878 | class TestRoute extends N.Route
|
879 |
|
880 | Config: ->
|
881 | super()
|
882 |
|
883 | @Get (req, res) =>
|
884 | res.status(200).send {elem: 'value'}
|
885 |
|
886 | # You have to give them a name, to replace the one a Resource would have given
|
887 | route = new TestRoute 'test'
|
888 | ```
|
889 |
|
890 | Create the following routes :
|
891 |
|
892 | ```
|
893 | GET => /api/1/tests
|
894 | ```
|
895 | ___
|
896 | ## Reactive Values
|
897 |
|
898 | See [Hacktiv](https://github.com/Champii/Hacktiv) documentation for more informations
|
899 |
|
900 | #### Intro
|
901 |
|
902 | Reactive programing benefits don't have to be proven. It just works.
|
903 |
|
904 | You can watch for a fonction and it will be re-executed when reactive values
|
905 | inside have changed.
|
906 |
|
907 | The following exemple will print successively 1 and 2
|
908 |
|
909 | ```coffeescript
|
910 | val = new N.Watch.Value 1
|
911 |
|
912 | # The function is executed a first time
|
913 | N.Watch ->
|
914 | console.log val()
|
915 |
|
916 | # This call will cause the function to be reexecuted.
|
917 | val 2
|
918 | ```
|
919 |
|
920 | #### Resource
|
921 |
|
922 | You just saw that Nodulator provide the default Hacktiv library.
|
923 |
|
924 | You can also watch for data-sets to change to retrigger the function :
|
925 |
|
926 | ```coffeescript
|
927 | Tests = N 'test'
|
928 |
|
929 | [...]
|
930 |
|
931 | N.Watch ->
|
932 | Tests.Fetch 1, (err, test) ->
|
933 |
|
934 | Tests.Fetch 1
|
935 | .then (test) ->
|
936 | test.prop = 'value'
|
937 |
|
938 | # This call will cause the watching function to be reexecuted.
|
939 | test.Save()
|
940 | ```
|
941 |
|
942 | It works also with List:
|
943 |
|
944 | ```coffeescript
|
945 | N.Watch ->
|
946 | # Get a double array
|
947 | Tests.List [
|
948 | {prop1: 'value1'}
|
949 | {prop1: 'value2'}
|
950 | ], (err, test) ->
|
951 | [...]
|
952 |
|
953 |
|
954 | Tests.Fetch {prop1: 'value1'}
|
955 | .then (test) ->
|
956 | test.prop = 'value3'
|
957 |
|
958 | # This call will cause the watching function to be reexecuted.
|
959 | test.Save()
|
960 | ```
|
961 |
|
962 |
|
963 | #### Errors
|
964 |
|
965 | Every Resource have his own reactive Error property that can be watched:
|
966 |
|
967 | ```coffeescript
|
968 | N.Watch ->
|
969 | console.error Tests.error()
|
970 |
|
971 | # will produce an error and refresh the console.log above
|
972 | Tests.Fetch {unknownProp: 'val'}, (err, test) ->
|
973 | ```
|
974 |
|
975 | Very usefull with `flipDone: true` option.
|
976 |
|
977 | ___
|
978 | ## Db Systems
|
979 |
|
980 | #### Abstraction
|
981 |
|
982 | We defined a driver interface for some DB implementations.
|
983 |
|
984 | It's based on SQL `Table` concept. (see [lib/connectors/sql/index.coffee](https://github.com/Champii/Nodulator/blob/master/lib/connectors/sql/index.coffee))
|
985 |
|
986 | ```coffeescript
|
987 | Table.Find(id, done)
|
988 | Table.FindWhere(fields, where, done)
|
989 | Table.Select(fields, where, options, done)
|
990 | Table.Save(blob, done)
|
991 | Table.Insert(blob, done)
|
992 | Table.Update(blob, where, done)
|
993 | Table.Delete(id, done)
|
994 | ```
|
995 |
|
996 | Every `Resource` have an associated `Table` instance that links to the good
|
997 | table/document in the good DB driver system
|
998 |
|
999 | #### Mysql
|
1000 |
|
1001 | Built-in `MySQL` implementation ([node-mysql](https://github.com/felixge/node-mysql/))
|
1002 | for `Nodulator`
|
1003 |
|
1004 | Check [lib/connectors/sql/Mysql.coffee](https://github.com/Champii/Nodulator/blob/master/lib/connectors/sql/Mysql.coffee)
|
1005 |
|
1006 | #### MongoDB
|
1007 |
|
1008 | Built-in `MongoDB` implementation ([mongous](https://github.com/amark/mongous))
|
1009 | for `Nodulator`
|
1010 |
|
1011 | Check [lib/connectors/sql/Mongo.coffee](https://github.com/Champii/Nodulator/blob/master/lib/connectors/sql/Mongo.coffee)
|
1012 |
|
1013 | #### SqlMem
|
1014 |
|
1015 | Special DB driver, built on RAM.
|
1016 |
|
1017 | It provides same options as others systems do, but nothing is stored. When you
|
1018 | stop the server, everything is deleted.
|
1019 |
|
1020 | Check [lib/connectors/sql/SqlMem.coffee](https://github.com/Champii/Nodulator/blob/master/lib/connectors/sql/SqlMem.coffee)
|
1021 |
|
1022 | ___
|
1023 | ## Other stuff
|
1024 |
|
1025 | #### Bus
|
1026 |
|
1027 | There is a `N.bus` object that is basicaly an `EventEmitter`. Every
|
1028 | objects in `Nodulator` use this bus.
|
1029 |
|
1030 | Here are the emitted events:
|
1031 |
|
1032 | - On a new `Resource` being inserted in DB, sends it after a `Serialize()` call
|
1033 | - `N.bus.emit 'new_' + resource_name, @Serialize()`
|
1034 |
|
1035 | - On a `Resource` being updated in DB, sends it after a `Serialize()` call
|
1036 | - `N.bus.emit 'update_' + resource_name, @Serialize()`
|
1037 |
|
1038 | - On a `Resource` being deleted from DB, sends it after a `Serialize()` call
|
1039 | - `N.bus.emit 'delete_' + resource_name, @Serialize()`
|
1040 |
|
1041 | Exemple
|
1042 |
|
1043 | ```coffeescript
|
1044 | Players = N 'player'
|
1045 |
|
1046 | N.on 'new_player', (player) ->
|
1047 | [...] # Do something with this brand new player
|
1048 | ```
|
1049 |
|
1050 | You can override default `Bus` by setting new class to N.Bus :
|
1051 |
|
1052 | ```coffeescript
|
1053 | N = require 'nodulator'
|
1054 | NewBus = require './NewBus'
|
1055 |
|
1056 | N.Bus = NewBus
|
1057 | ```
|
1058 |
|
1059 | Always set new `Bus` before any new `Resource` call or any added `Module`
|
1060 |
|
1061 | ___
|
1062 | ## Modules
|
1063 |
|
1064 | #### Usage
|
1065 |
|
1066 | To inject a module into `Nodulator`, preceed this way :
|
1067 |
|
1068 | ```coffeescript
|
1069 | N = require 'nodulator'
|
1070 | ModuleName = require 'nodulator-ModuleName'
|
1071 |
|
1072 | N.Use ModuleName
|
1073 | ```
|
1074 |
|
1075 | Replace `ModuleName` with the module's name you want to load
|
1076 |
|
1077 | #### Module Hacking
|
1078 |
|
1079 | If you want to create a new module for `Nodulator`, you have to export a single
|
1080 | function, taking `Nodulator` as parameter :
|
1081 |
|
1082 | ```coffeescript
|
1083 | module.exports = (Nodulator) ->
|
1084 | [...] # Your module here
|
1085 | ```
|
1086 |
|
1087 | You can extend anything you want, as the whole `Nodulator` object is passed to your function.
|
1088 |
|
1089 | Be carefull to `server/loadOrder.json`.
|
1090 |
|
1091 | Watch how [other modules](#compatible-modules-and-dependencies) are made !
|
1092 |
|
1093 | ___
|
1094 | ## Project Generation
|
1095 | You can get global `Nodulator` :
|
1096 |
|
1097 | ```
|
1098 | $> npm install -g nodulator
|
1099 | $> Nodulator
|
1100 | Usage: Nodulator (init) | ((install | install-dev | remove) moduleName1 [, moduleName2 [, ...]])
|
1101 | ```
|
1102 |
|
1103 | Nodulator provides a way of installing `Nodulator`, modules and dependencies easely
|
1104 | ```
|
1105 | # If no arguments, install or remove Nodulator
|
1106 | $> Nodulator install
|
1107 | $> Nodulator install-dev
|
1108 | $> Nodulator remove
|
1109 |
|
1110 | # Will install nodulator-angular and every dependencies (if any)
|
1111 | $> Nodulator install angular
|
1112 |
|
1113 | # Will install nodulator-angular, nodulator-account, and all their dependencies (if any)
|
1114 | $> Nodulator install angular account
|
1115 |
|
1116 | # Will create local link instead of a full install of nodulator-angular and every dependencies (if any)
|
1117 | # It's used to avoid reinstalling locally a Nodulator package under development
|
1118 | $> Nodulator install-dev angular
|
1119 |
|
1120 | # Will remove nodulator-socket
|
1121 | $> Nodulator remove socket
|
1122 | ```
|
1123 |
|
1124 | Then you can launch the `init` process :
|
1125 | ```
|
1126 | $> Nodulator init
|
1127 | ```
|
1128 |
|
1129 | It creates the following structure if non-existant :
|
1130 | ```
|
1131 | main.coffee
|
1132 | package.json
|
1133 | settings/
|
1134 | server/
|
1135 | ├── index.coffee
|
1136 | ├── loadOrder.json
|
1137 | ├── processors/
|
1138 | │ └── index.coffee
|
1139 | └── resources/
|
1140 | └── index.coffee
|
1141 | ```
|
1142 |
|
1143 | And then find for every `Nodulator` modules installed, and call their
|
1144 | respective `init` method.
|
1145 |
|
1146 | It generate a `main.coffee` and a `package.json` with every modules pre-loaded.
|
1147 |
|
1148 | The `server` folder is auto-loaded (check `server/index.coffee` and every
|
1149 | `index.coffee` in subfolders).
|
1150 |
|
1151 | Folders load order is defined in `server/loadOrder.json`, and is automaticaly
|
1152 | managed by new modules installed (they care of the order)
|
1153 |
|
1154 | You can immediately start to write `Resource` in `server/resources` !
|
1155 |
|
1156 | ___
|
1157 | ## Developers
|
1158 |
|
1159 | Never forget that I'm always available at contact@champii.io for any questions
|
1160 | (Job related or not ;-)
|
1161 |
|
1162 | ___
|
1163 | ## Contributors
|
1164 |
|
1165 | - [Champii](https://github.com/champii)
|
1166 | - [SkinyMonkey](https://github.com/skinymonkey)
|
1167 |
|
1168 | ___
|
1169 | ## ToDo
|
1170 |
|
1171 | By order of priority
|
1172 |
|
1173 | - Paginated Resource
|
1174 | - When cache expire, remove correspondant Watcher /!\
|
1175 | - Inherit associations (care for abstract Resources)
|
1176 | - Remove an existing route easely
|
1177 | - Better query on Resource (gt, gte, lt, lte, not, range, ...)
|
1178 | - Migration system
|
1179 | - Auto wrap new methods in `Resource`
|
1180 | - Association Polymorphism
|
1181 | - Watch a specific field
|
1182 | - Relations not only based on id but on every property types
|
1183 | - Persistant sessions in Console
|
1184 | - 'Unique' field
|
1185 | - 0bject OwnRoute that perform from logged user (/api/1/player or /api/1/tasks for exemple)
|
1186 | - Decentralize modules config
|
1187 | - Scaling (cluster, distributed bus)
|
1188 | - Split Resource into smaller Classes
|
1189 | - List return a resource that can act on each item (Set, Add, ...) ( Extend Array ? )
|
1190 | - Better tests
|
1191 | - Request
|
1192 | - Multi Driver fetch/list
|
1193 | - Db
|
1194 | - SqlMem
|
1195 | - Mysql
|
1196 | - Mongo
|
1197 | - Ids
|
1198 | - HABTM tables
|
1199 | - Cache
|
1200 | - Config oveloading
|
1201 | - Schema
|
1202 | - HasOne
|
1203 | - HasMany
|
1204 | - HasOneThrough
|
1205 | - HasManyThrough
|
1206 | - HasAndBelongsToMany
|
1207 |
|
1208 |
|
1209 | ___
|
1210 | ## ChangeLog
|
1211 | XX/XX/XX: current (not released yet)
|
1212 | -
|
1213 |
|
1214 | 28/11/15: 0.1.0
|
1215 | - LiveScript ! \o/
|
1216 | - Lib Folder reorganisation
|
1217 | - Added benchmarks folder
|
1218 | - Replaced @instance in Route by req._instance.
|
1219 | - Added a Request class to handle Resource in Route
|
1220 | - The Route class can take a Resource as property. Also, Routes can be Instanciated.
|
1221 | - Changed every 'Nodulator' call by 'N', more readable
|
1222 | - Added a debug system with 'debug'
|
1223 | - Console mode to "connect" to an existing/running Nodulator instance and perform standard calls
|
1224 | - Joined Every modules into this git repo for a stronger compatibility
|
1225 | - Shortcut the N.Resource() into a simple N()
|
1226 | - Added a 'Watch' for both Instance and Class of Resource, that allow more flexibility
|
1227 | - Added a virtual field filled when the resource is.
|
1228 | - Added HasOne, HasMany, BelongsTo and BelongsToMany calls. Buggy at the moment and not standard
|
1229 | - Added _WrapDebug() Wrapper
|
1230 | - Web server now starts only when needed (when first Route is being declared)
|
1231 | - Fixed validation fails
|
1232 | - Added _CreateUnwrapped
|
1233 | - Added a instance.Watch() call, to make the instance to auto-update when part of it change
|
1234 | - Schema 'strict' or 'free'
|
1235 | - When in Association, if the localKey doesnt exists, it is created on the fly
|
1236 | - Better HasOne, HasMany and BelongsTo.
|
1237 | - HasOneThrough, HasManyThrough
|
1238 | - HasAndBelongsToMany
|
1239 | - Resources can override global db config to put different models on different db systems
|
1240 | - Resource instance can be created on different db than default
|
1241 | - Added Cache over Redis
|
1242 | - Added @Hydrate() function to populate properties and associations from cache
|
1243 | - Added configuration for cache
|
1244 | - Better configuration for db
|
1245 | - Internal driver is now fixed to be the default Nodulator driver.
|
1246 | - Globalized ids management by external table
|
1247 | - internal_ids are now stored on default driver and are automaticaly updated with last Ids values
|
1248 | - Chainable calls !
|
1249 | - Create can now take a promise instead of an id
|
1250 | - Better Remove() for MayHas*() associations
|
1251 | - Schema is now inherited by copy
|
1252 | - Internal() Field property that is not put in the JSON produced by ToJSON(), so not sent to any client but saved to DB anyway
|
1253 | - JSON and object validation type
|
1254 | - Each resource is available through N.Resourcename (exemple for 'player' : N.Player)
|
1255 | - When a route is attached to a Resource, it is now available as Resource.Route
|
1256 |
|
1257 | 21/07/15: v0.0.19
|
1258 | - Added `SingleRoute` object, for manipulating Singleton `Resource`
|
1259 | - Removed `req.instances` from every `Route`
|
1260 | - Added tests for `SingleRoute`
|
1261 | - Route proxy methods for `@_All()` are now generated at runtime
|
1262 | - Renamed `DefaultRoute` to `MultiRoute`
|
1263 | - Added a `default` field to config schema
|
1264 | - `Resource.Init()` now returns the `Resource` itself, for chaining purpose.
|
1265 | - Added tests for resource association
|
1266 | - Tests are now executed in specific order
|
1267 | - You can now give an array as schema type for a field, in order to retrive
|
1268 | multiple resources based on id
|
1269 | - Added Javascript support
|
1270 | - Added an output line to tell the user when the framework is listening and
|
1271 | to which port
|
1272 | - Fetch and Create can now take one argument or an array of arguments
|
1273 | - Fixed bugs on resource association:
|
1274 | - ToJSON() now call child ToJSON() instead of Serialize()
|
1275 | - ToJSON() call check if given association exists
|
1276 | - Added 'distantKey' in relational schema to fetch relation that have that
|
1277 | resource id as given key
|
1278 | - Added maxDepth field to resource config in order to limit the relationnal
|
1279 | fetch. There is also a Resource.DEFAULT_DEPTH constant that is used when nothing is precised.
|
1280 | - Added argument to Resource.Init(): You can give the config object in order
|
1281 | to avoid recursive require when two way model association
|
1282 | - Removed Doc section. It will be on the website documentation.
|
1283 | - Code in Init() has been splited for code clarity
|
1284 | - Load order has changed between resources and socket
|
1285 | - Added a @_type variable defining the typename of an instance
|
1286 | - Fixed a bug in model association: no field in schema if array with no 'type'
|
1287 | - Improved 'arrayOf' type check
|
1288 | - Added function to default schema value (is that a possible virtual field ?)
|
1289 | - MultiRoute::Get() now can take query arguments
|
1290 | - Added Resource::ExtendSafe() method to preserve associated models while extending a Resource
|
1291 | - Modified Route::MultiRoute and Route::SingleRoute to use Resource::ExtendSafe()
|
1292 | - Removed app parameter from Route constructor
|
1293 | - Route classes can now be instanciated without any Resources
|
1294 | - Removed ListBy and FetchBy for simplicity
|
1295 | - Resource::Deserialize() is now a private call : Resource::_Deserialize()
|
1296 | - Removed the mandatory Init function call !
|
1297 | - Added Promises if no callback given.
|
1298 | - Routes are now instantiated when attached, not when Init. This helps the new lazy Init system
|
1299 | - List can now take an array
|
1300 | - Added Hacktiv support for Resources
|
1301 | - Added a ChangeWatcher for Resources that watch for the result of a query to make change
|
1302 | - You can now add a `flipDone: true` to the N.Config() call to have callback like (data, err) ->
|
1303 | - Added Wrappers class to regroup every wrappers.
|
1304 | - Added Wrappers for Promises, FlipDone, and for WatchArgs
|
1305 | - Extend now dont need to be abstract to work
|
1306 | - Added tests for promisesm FlipDone and reactive watching
|
1307 |
|
1308 | 04/05/15: v0.0.18
|
1309 | - You can specify a 'store' general config property in order to switch to redis-based sessions
|
1310 |
|
1311 | 03/05/15: v0.0.17
|
1312 | - You can now specify a property type in schema without wrapping it in a object like {type: "string"}
|
1313 |
|
1314 | 15/04/15: v0.0.16
|
1315 | - Removed redis references for sessions
|
1316 |
|
1317 | 14/04/15: v0.0.15
|
1318 | - Minor changes in `Route` to fit [Nodulator-Account](https://github.com/Champii/Nodulator-Account) new release
|
1319 |
|
1320 | 10/04/15: v0.0.14
|
1321 | - Resource 'user' is no longer a reserved word
|
1322 | - Resources with name finishing with 'y' are now correctly put in plural form in route name
|
1323 |
|
1324 | 09/04/15: v0.0.13
|
1325 | - Better model association and validation
|
1326 | - Pre-fetched resources in `Route.All()` are now put in `@instance` instead of `req[@resource.lname]`
|
1327 | - Updated README
|
1328 | - Updated Mongo driver
|
1329 |
|
1330 | 20/01/15: v0.0.12
|
1331 | - Fixed bug on FetchBy
|
1332 |
|
1333 | 20/01/15: v0.0.11
|
1334 | - Fixed tests
|
1335 | - Added travis support for tests
|
1336 | - Added model associations
|
1337 | - Added schema and model validation
|
1338 | - Changed `FetchBy` and `ListBy` prototype. Now take an object instead of a key/value pair.
|
1339 | - Added `Create()` method into `Resource`
|
1340 | - Added `limit` and `offset` to both Mysql and SqlMem
|
1341 |
|
1342 | 07/01/15: v0.0.10
|
1343 | - Added Philosophy section
|
1344 | - Added multiple package name support in package generation
|
1345 | - Fixed some bugs with modules
|
1346 |
|
1347 | 03/01/15: v0.0.9
|
1348 | - Separated `AccountResource` into a new module [Nodulator-Account](https://github.com/Champii/Nodulator-Account)
|
1349 | - Changed README
|
1350 |
|
1351 | 02/01/15: v0.0.8
|
1352 | - Fixed Route middleware issue
|
1353 |
|
1354 | 02/01/15: v0.0.7
|
1355 | - Separated `Socket` into a new module [Nodulator-Socket](https://github.com/Champii/Nodulator-Socket)
|
1356 | - Added new methods for `@Get()`, `@Post()`, `@Delete()`, `@Put()`, `@All()` in `Route`
|
1357 | - Replace old method `@All()` into `@_All()`. Is now a private call.
|
1358 | - Improved README (added [Modules](#modules) section)
|
1359 | - Global `Nodulator` now manage dependencies installation
|