Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Doc generation script; Improve api

  • Loading branch information...
commit 3ea557ed312f187785ca48fe351148a1dd4fd308 1 parent 8eb9931
@wdavidw authored
View
84 docs/client.md
@@ -0,0 +1,84 @@
+---
+language: en
+layout: page
+title: "
+Client connection"
+date: 2012-03-11T12:51:16.819Z
+comments: false
+sharing: false
+footer: false
+navigation: ron
+github: https://github.com/wdavidw/node-ron
+---
+
+
+The client wraps a redis connection and provides access to records definition
+and manipulation.
+
+Internally, Ron use the [Redis client for Node.js](https://github.com/mranney/node_redis).
+
+<a name="ron"></a>`ron([options])` Client creation
+--------------------------------
+
+`options` Options properties include:
+
+* `name` A namespace for the application, all keys with be prefixed with "#{name}:". Default to "ron"
+* `redis` Provide an existing instance in case you don't want a new one to be created.
+* `host` Redis hostname.
+* `port` Redis port.
+* `password` Redis password.
+* `database` Redis database (an integer).
+
+Basic example:
+```coffeescript
+ron = require 'ron'
+client = ron
+ host: '127.0.0.1'
+ port: 6379
+```
+
+
+<a name="get"></a>`get(schema)` Records definition and access
+-------------------------------------------
+Return a records instance. If the `schema` argument is an object, a new
+instance will be created overwriting any previously defined instance
+with the same name.
+
+`schema` An object defining a new schema or a string referencing a schema name.
+
+Define a record from a object:
+```coffeescript
+client.get
+ name: 'users'
+ properties:
+ user_id: identifier: true
+ username: unique: true
+ email: index: true
+
+```
+Define a record from function calls:
+```coffeescript
+Users = client.get 'users'
+Users.identifier 'user_id'
+Users.unique 'username'
+Users.index 'email'
+
+```
+Alternatively, the function could be called with a string
+followed by multiple schema definition that will be merged.
+Here is a valid example:
+```coffeescript
+
+client.get 'username', temporal: true, properties: username: unique: true
+```
+
+
+<a name="quit"></a>`quit(callback)` Quit
+---------------------
+Destroy the redis connection.
+
+`callback` Received parameters are:
+
+* `err` Error object if any.
+* `status` Status provided by the redis driver
+
View
104 docs/index.md
@@ -0,0 +1,104 @@
+---
+language: en
+layout: page
+title: "Redis ORM for NodeJs"
+date: 2012-03-10T15:16:01.006Z
+comments: false
+sharing: false
+footer: false
+navigation: ron
+github: https://github.com/wdavidw/node-ron
+---
+
+Redis ORM for NodeJs
+====================
+
+Installation
+------------
+
+```bash
+npm install ron
+```
+
+Usage
+-----
+
+```javascript
+ron = require('ron');
+// Client connection
+client = ron({
+ port: 6379
+ host: '127.0.0.1'
+ name: 'auth'
+});
+// Schema definition
+Users = client.get('users');
+Users.property('id', {identifier: true});
+Users.property('username', {unique: true});
+Users.property('email', {index: true, type: 'email'});
+Users.property('name', {});
+// Record manipulation
+Users.create(
+ {username: 'ron', email: 'ron@domain.com'},
+ function(err, user){
+ console.log(err, user.id);
+ }
+)
+```
+
+The library provide
+-------------------
+
+* Documented and tested API
+* Records access with indexes and unique values
+* Records are pure object, no extended class, no magic
+
+Client API
+----------
+
+* [Client::constructor](client.html#ron)
+* [Client::get](client.html#get)
+* [Client::quit](client.html#quit)
+
+Schema API
+----------
+
+* [Records::hash](schema.html#hash)
+* [Records::identifier](schema.html#identifier)
+* [Records::index](schema.html#index)
+* [Records::property](schema.html#property)
+* [Records::name](schema.html#name)
+* [Records::serialize](schema.html#serialize)
+* [Records::temporal](schema.html#temporal)
+* [Records::unique](schema.html#unique)
+* [Records::unserialize](schema.html#unserialize)
+* [Records::validate](schema.html#validate)
+
+Records API
+-----------
+
+* [Records::all](records.html#all)
+* [Records::clear](records.html#clear)
+* [Records::count](records.html#count)
+* [Records::create](records.html#create)
+* [Records::exists](records.html#exists)
+* [Records::get](records.html#get)
+* [Records::id](records.html#id)
+* [Records::list](records.html#list)
+* [Records::remove](records.html#remove)
+* [Records::update](records.html#update)
+
+Run tests
+---------
+
+Start a redis server on the default port
+```bash
+redis-server ./conf/redis.conf
+```
+
+Run the tests with mocha:
+```bash
+make test
+```
+
+
View
256 docs/records.md
@@ -0,0 +1,256 @@
+---
+language: en
+layout: page
+title: "
+Records access and manipulation"
+date: 2012-03-11T12:51:16.820Z
+comments: false
+sharing: false
+footer: false
+navigation: ron
+github: https://github.com/wdavidw/node-ron
+---
+
+
+Implement object based storage with indexing support.
+
+Identifier
+----------
+
+Auto generated identifiers are incremented integers. The next identifier is obtained from
+a key named as `{s.db}:{s.name}_incr`. All the identifiers are stored as a Redis set in
+a key named as `{s.db}:{s.name}_#{identifier}`.
+
+Data
+----
+
+Records data is stored as a single hash named as `{s.db}:{s.name}:{idenfitier}`. The hash
+keys map to the record properties and the hash value map to the values associated with
+each properties.
+
+Regular indexes
+---------------
+
+Regular index are stored inside multiple sets, named as
+`{s.db}:{s.name}_{property}:{value}`. There is one key for each indexed value and its
+associated value is a set containing all the identifiers of the records whose property
+match the indexed value.
+
+Unique indexes
+--------------
+
+Unique indexes are stored inside a single hash key named as
+`{s.db}:{s.name}_{property}`. Inside the hash, keys are the unique values
+associated to the indexed property and values are the record identifiers.
+
+<a name="all"></a>`all(callback)`
+---------------
+Return all records. Similar to the find method with far less options
+and a faster implementation.
+
+
+<a name="clear"></a>`clear(callback)`
+-----------------
+Remove all the records and the references poiting to them. This function
+takes no other argument than the callback called on error or success.
+
+`callback` Received parameters are:
+
+* `err` Error object if any.
+* `count` Number of removed records on success
+
+Usage:
+```coffeescript
+ron.get('users').clear (err, count) ->
+ return console.error "Failed: #{err.message}" if err
+ console.log "#{count} records removed"
+```
+
+
+<a name="count"></a>`count(callback)`
+-----------------
+Count the number of records present in the database.
+
+
+<a name="create"></a>`create(records, [options], callback)`
+--------------------------------------
+Insert one or multiple record. The records must not already exists
+in the database or an error will be returned in the callback. The records objects
+passed in the function are returned in the callback with their new identifier property.
+
+`records` Record object or array of record objects.
+
+`options` Options properties include:
+
+* `identifiers` Return only the created identifiers instead of the records.
+* `validate` Validate the records.
+* `properties` Array of properties to be returned.
+* `milliseconds` Convert date value to milliseconds timestamps instead of `Date` objects.
+* `seconds` Convert date value to seconds timestamps instead of `Date` objects.
+
+`callback` Called on success or failure. Received parameters are:
+
+* `err` Error object if any.
+* `records` Records with their newly created identifier.
+
+Records are not validated, it is the responsability of the client program calling `create` to either
+call `validate` before calling `create` or to passs the `validate` options.
+
+
+<a name="exists"></a>`exists(records, callback)`
+---------------------------
+Check if one or more record exist. The existence of a record is based on its
+id or any property defined as unique. The provided callback is called with
+an error or the records identifiers. The identifiers respect the same
+structure as the provided records argument. If a record does not exists,
+its associated return value is null.
+
+`records` Record object or array of record objects.
+
+`callback` Called on success or failure. Received parameters are:
+
+* `err` Error object if any.
+* `identifier` Record identifiers or null values.
+
+
+<a name="get"></a>`get(records, [options], callback)`
+-----------------------------------
+Retrieve one or multiple records. If options is an array, it is considered
+to be the list of properties to retrieve. By default, unless the `force`
+option is defined, only the properties not yet defined in the provided
+records are fetch from Redis.
+
+`options` All options are optional. Options properties include:
+
+* `properties` Array of properties to fetch, all properties if not defined.
+* `force` Force the retrieval of properties even if already present in the record objects.
+* `accept_null` Skip objects if they are provided as null.
+
+`callback` Called on success or failure. Received parameters are:
+
+* `err` Error object if the command failed.
+* `records` Object or array of object if command succeed. Objects are null if records are not found.
+
+
+<a name="id"></a>`id(records, [options], callback)`
+----------------------------------
+Extract record identifiers or set the identifier to null if its associated record could not be found.
+
+The method doesn't hit the database to validate record values and if an id is
+provided, it wont check its existence. When a record has no identifier but a unique value, then its
+identifier will be fetched from Redis.
+
+`records` Record object or array of record objects.
+
+`options` Options properties include:
+
+* `accept_null` Skip objects if they are provided as null.
+* `object` Return an object in the callback even if it recieve an id instead of a record.
+
+Use reverse index lookup to extract user ids:
+```coffeescript
+
+Users.get 'users', properties:
+ user_id: identifier: true
+ username: unique: true
+Users.id [
+ {username: 'username_1'}
+ {username: 'username_2'}
+], (err, ids) ->
+ should.not.exist err
+ console.log ids
+
+```
+Use the `object` option to return records instead of ids:
+```coffeescript
+
+Users.get 'users', properties:
+ user_id: identifier: true
+ username: unique: true
+Users.id [
+ 1, {user_id: 2} ,{username: 'username_3'}
+], object: true, (err, users) ->
+ should.not.exist err
+ ids = for user in users then user.user_id
+ console.log ids
+```
+
+
+<a name="list"></a>`list([options], callback)`
+---------------------------
+List records with support for filtering and sorting.
+
+`options` Options properties include:
+
+* `where` Hash of property/value used to filter the query.
+* `operation` Redis operation in case of multiple `where` properties, default to `union`.
+* `sort` Name of the property by which records should be ordered.
+* `direction` One of `asc` or `desc`, default to `asc`.
+* `properties` Array of properties to be returned.
+* `milliseconds` Convert date value to milliseconds timestamps instead of `Date` objects.
+* `seconds` Convert date value to seconds timestamps instead of `Date` objects.
+
+`callback` Called on success or failure. Received parameters are:
+
+* `err` Error object if any.
+* `records` Records fetched from Redis.
+
+Using the `union` operation:
+```coffeescript
+
+Users.list
+ where: group: ['admin', 'redis']
+ operation: 'union'
+ direction: 'desc'
+, (err, users) ->
+ console.log users
+
+```
+An alternative syntax is to bypass the `where` option, the exemple above
+could be rewritten as:
+```coffeescript
+
+Users.list
+ group: ['admin', 'redis']
+ operation: 'union'
+ direction: 'desc'
+, (err, users) ->
+ console.log users
+```
+
+
+<a name="remove"></a>`remove(records, callback)`
+---------------------------
+Remove one or several records from the database. The function will also
+handle all the indexes referencing those records.
+
+
+<a name="update"></a>`update(records, [options], callback)`
+--------------------------------------
+Update one or several records. The records must exists in the database or
+an error will be returned in the callback. The existence of a record may
+be discovered through its identifier or the presence of a unique property.
+
+`records` Record object or array of record objects.
+
+`options` Options properties include:
+
+* `validate` Validate the records.
+
+`callback` Called on success or failure. Received parameters are:
+
+* `err` Error object if any.
+* `records` Records with their newly created identifier.
+
+Records are not validated, it is the responsability of the client program calling `create` to either
+call `validate` before calling `create` or to passs the `validate` options.
+
+Updating a single record:
+```coffeescript
+
+Users.update
+ username: 'my_username'
+ age: 28
+, (err, user) -> console.log user
+```
+
View
183 docs/schema.md
@@ -0,0 +1,183 @@
+---
+language: en
+layout: page
+title: "
+Schema definition"
+date: 2012-03-11T12:51:16.820Z
+comments: false
+sharing: false
+footer: false
+navigation: ron
+github: https://github.com/wdavidw/node-ron
+---
+
+
+A schema is defined once and must no change. We dont support schema migration at the moment. The `Records`
+class inherit all the properties and method of the shema.
+
+`ron` Reference to the Ron instance
+
+`options` Schema definition. Options include:
+
+* `name` Name of the schema.
+* `properties` Properties definition, an object or an array.
+
+Record properties may be defined by the following keys:
+
+* `type` Use to cast the value inside Redis, one of `string`, `int`, `date` or `email`.
+* `identifier` Mark this property as the identifier, only one property may be an identifier.
+* `index` Create an index on the property.
+* `unique` Create a unique index on the property.
+* `temporal` Add creation and modification date transparently.
+
+Define a schema from a configuration object:
+```coffeescript
+
+ron.get 'users', properties:
+ user_id: identifier: true
+ username: unique: true
+ password: true
+
+```
+Define a schema with a declarative approach:
+```coffeescript
+
+Users = ron.get 'users'
+Users.indentifier 'user_id'
+Users.unique 'username'
+Users.property 'password'
+
+```
+Whichever your style, you can then manipulate your records:
+```coffeescript
+
+users = ron.get 'users'
+users.list (err, users) -> console.log users
+```
+
+<a name="hash"></a>`hash(key)`
+-------------
+Utility function used when a redis key is created out of
+uncontrolled character (like a string instead of an int).
+
+
+<a name="identifier"></a>`identifier(property)`
+------------------------
+Define a property as an identifier or return the record identifier if
+called without any arguments. An identifier is a property which uniquely
+define a record. Inside Redis, identifier values are stored in set.
+
+
+<a name="index"></a>`index([property])`
+-------------------
+Define a property as indexable or return all index properties. An
+indexed property allow records access by its property value. For example,
+when using the `list` function, the search can be filtered such as returned
+records match one or multiple values.
+
+Calling this function without any argument will return an array with all the
+indexed properties.
+
+Example:
+```coffeescript
+User.index 'email'
+User.list { filter: { email: 'my@email.com' } }, (err, users) ->
+ console.log 'This user has the following accounts:'
+ for user in user
+ console.log "- #{user.username}"
+```
+
+
+<a name="property"></a>`property(property, [schema])`
+------------------------------
+Define a new property or overwrite the definition of an
+existing property. If no schema is provide, return the
+property information.
+
+Calling this function with only the property argument will return the schema
+information associated with the property.
+
+It is possible to define a new property without any schema information by
+providing an empty object.
+
+Example:
+```coffeescript
+
+User.property 'id', identifier: true
+User.property 'username', unique: true
+User.property 'email', { index: true, type: 'email' }
+User.property 'name', {}
+```
+
+
+<a name="name"></a>`name()`
+--------
+Return the schema name of the current instance.
+
+Using the function :
+```coffeescript
+Users = client 'users', properties: username: unique: true
+console.log Users.name() is 'users'
+```
+
+
+<a name="serialize"></a>`serialize(records)`
+--------------------
+Cast record values before their insertion into Redis.
+
+Take a record or an array of records and update values with correct
+property types.
+
+
+<a name="temporal"></a>`temporal([options])`
+---------------------
+Define or retrieve temporal definition. Marking a schema as
+temporal will transparently add two new date properties, the
+date when the record was created (by default "creation"), and the date
+when the record was last updated (by default "modification").
+
+
+<a name="unique"></a>`unique([property])`
+--------------------
+Define a property as unique or retrieve all the unique properties if no
+argument is provided. An unique property is similar to a index
+property but the index is stored inside a Redis hash. In addition to being
+filterable, it could also be used as an identifer to access a record.
+
+Example:
+```coffeescript
+
+User.unique 'username'
+User.get { username: 'me' }, (err, user) ->
+ console.log "This is #{user.username}"
+```
+
+
+<a name="unserialize"></a>`unserialize(records, [options])`
+---------------------------------
+Cast record values to their correct type.
+
+Take a record or an array of records and update values with correct
+property types.
+
+`options` Options include:
+
+* `properties` Array of properties to be returned.
+* `milliseconds` Convert date value to milliseconds timestamps instead of `Date` objects.
+* `seconds` Convert date value to seconds timestamps instead of `Date` objects.
+
+
+<a name="validate"></a>`validate(records, [options])`
+------------------------------
+Validate the properties of one or more records. Return a validation
+object or an array of validation objects depending on the provided
+records arguments. Keys of a validation object are the name of the invalid
+properties and their value is a string indicating the type of error.
+
+`records` Record object or array of record objects.
+
+`options` Options include:
+
+* `throw` Throw errors on first invalid property instead of returning a validation object.
+* `skip_required` Doesn't validate missing properties defined as `required`, usefull for partial update.
+
View
16 lib/Client.coffee
@@ -4,12 +4,14 @@ Schema = require './Schema'
Records = require './Records'
###
-Ron client
-==========
+
+Client connection
+=================
+
The client wraps a redis connection and provides access to records definition
and manipulation.
-Internall, Ron rely on the [Redis client for Node.js](https://github.com/mranney/node_redis).
+Internally, Ron use the [Redis client for Node.js](https://github.com/mranney/node_redis).
###
module.exports = class Client
@@ -23,10 +25,10 @@ module.exports = class Client
* `name` A namespace for the application, all keys with be prefixed with "#{name}:". Default to "ron"
* `redis` Provide an existing instance in case you don't want a new one to be created.
- * `host` Redis database hostname.
- * `port` Redis database port.
- * `password` Redis databse password.
- * `database` Integer defining the redis database.
+ * `host` Redis hostname.
+ * `port` Redis port.
+ * `password` Redis password.
+ * `database` Redis database (an integer).
Basic example:
ron = require 'ron'
View
159 lib/Records.coffee
@@ -2,30 +2,25 @@
Schema = require './Schema'
###
+
Records access and manipulation
===============================
-Implement object based storage with indexing support.
+Implement object based storage with indexing support.
Identifier
----------
-Auto generated identifiers are incremented integers. The next identifier is stored in
-a key named as `{s.db}:{s.name}_incr`.
+Auto generated identifiers are incremented integers. The next identifier is obtained from
+a key named as `{s.db}:{s.name}_incr`. All the identifiers are stored as a Redis set in
+a key named as `{s.db}:{s.name}_#{identifier}`.
Data
----
Records data is stored as a single hash named as `{s.db}:{s.name}:{idenfitier}`. The hash
keys map to the record properties and the hash value map to the values associated with
-each properties.
-
-Identifiers and unique indexes
-------------------------------
-
-Unique indexes are stored inside a single hash key named as
-`{s.db}:{s.name}_{property}`. Inside the hash, the hash keys store the unique values
-associated to the indexed property and the hash values store the record identifiers.
+each properties.
Regular indexes
---------------
@@ -33,7 +28,14 @@ Regular indexes
Regular index are stored inside multiple sets, named as
`{s.db}:{s.name}_{property}:{value}`. There is one key for each indexed value and its
associated value is a set containing all the identifiers of the records whose property
-match the indexed value.
+match the indexed value.
+
+Unique indexes
+--------------
+
+Unique indexes are stored inside a single hash key named as
+`{s.db}:{s.name}_{property}`. Inside the hash, keys are the unique values
+associated to the indexed property and values are the record identifiers.
###
module.exports = class Records extends Schema
@@ -43,9 +45,10 @@ module.exports = class Records extends Schema
super ron, schema
###
- `all(callback)` Return all records
- ----------------------------------
- Similar to the find method with far less options and a faster implementation.
+ `all(callback)`
+ ---------------
+ Return all records. Similar to the find method with far less options
+ and a faster implementation.
###
all: (callback) ->
@@ -61,17 +64,17 @@ module.exports = class Records extends Schema
callback null, records
###
- `clear(callback)` Clear all the records
- ---------------------------------------
+ `clear(callback)`
+ -----------------
Remove all the records and the references poiting to them. This function
- takes no other argument than the callback called on error or success.
+ takes no other argument than the callback called on error or success.
`callback` Received parameters are:
* `err` Error object if any.
- * `count` Number of removed records on success
+ * `count` Number of removed records on success
- Usage:
+ Usage:
ron.get('users').clear (err, count) ->
return console.error "Failed: #{err.message}" if err
console.log "#{count} records removed"
@@ -125,7 +128,11 @@ module.exports = class Records extends Schema
return callback err if err
callback null, count
###
- Count the number of records present in the database.
+
+ `count(callback)`
+ -----------------
+ Count the number of records present in the database.
+
###
count: (callback) ->
{redis} = @
@@ -134,17 +141,18 @@ module.exports = class Records extends Schema
return callback err if err
callback null, count
###
- `create(records, [options], callback)` Create new records
- ----------------------------------------------
- Take a record object or multiple records and insert them. The records must not
- exists in the database or an error will be returned in the callback. The records objects
+
+ `create(records, [options], callback)`
+ --------------------------------------
+ Insert one or multiple record. The records must not already exists
+ in the database or an error will be returned in the callback. The records objects
passed in the function are returned in the callback with their new identifier property.
- `records` Record object or array of record objects.
+ `records` Record object or array of record objects.
`options` Options properties include:
- * `identifiers` Return only the created identifiers instead of the records.
+ * `identifiers` Return only the created identifiers instead of the records.
* `validate` Validate the records.
* `properties` Array of properties to be returned.
* `milliseconds` Convert date value to milliseconds timestamps instead of `Date` objects.
@@ -153,10 +161,10 @@ module.exports = class Records extends Schema
`callback` Called on success or failure. Received parameters are:
* `err` Error object if any.
- * `records` Records with their newly created identifier.
+ * `records` Records with their newly created identifier.
Records are not validated, it is the responsability of the client program calling `create` to either
- call `validate` before calling `create` or to passs the `validate` options.
+ call `validate` before calling `create` or to passs the `validate` options.
###
create: (records, options, callback) ->
@@ -220,19 +228,21 @@ module.exports = class Records extends Schema
callback null, if isArray then records else records[0]
###
- `exists(records, callback)` Check if one or more record exist
- -------------------------------------------------------------
- The existence of a record is based on its id or any property defined as unique.
- The provided callback is called with an error or the records identifiers. The
- identifiers respect the same structure as the provided records argument. If a
- record does not exists, its associated return value is null.
+ `exists(records, callback)`
+ ---------------------------
+ Check if one or more record exist. The existence of a record is based on its
+ id or any property defined as unique. The provided callback is called with
+ an error or the records identifiers. The identifiers respect the same
+ structure as the provided records argument. If a record does not exists,
+ its associated return value is null.
- `records` Record object or array of record objects.
+ `records` Record object or array of record objects.
`callback` Called on success or failure. Received parameters are:
* `err` Error object if any.
- * `identifier` Record identifiers or null values.
+ * `identifier` Record identifiers or null values.
+
###
exists: (records, callback) ->
{redis} = @
@@ -257,11 +267,12 @@ module.exports = class Records extends Schema
callback null, if isArray then recordIds else recordIds[0]
###
- `get(records, [options], callback)` Retrieve one or multiple records
- --------------------------------------------------------------------
- If options is an array, it is considered to be the list of properties to
- retrieve. By default, unless the `force` option is defined, only the properties
- not yet defined in the provided records are fetch from Redis.
+ `get(records, [options], callback)`
+ -----------------------------------
+ Retrieve one or multiple records. If options is an array, it is considered
+ to be the list of properties to retrieve. By default, unless the `force`
+ option is defined, only the properties not yet defined in the provided
+ records are fetch from Redis.
`options` All options are optional. Options properties include:
@@ -271,7 +282,7 @@ module.exports = class Records extends Schema
`callback` Called on success or failure. Received parameters are:
- * `err` Error object if the command failed
+ * `err` Error object if the command failed.
* `records` Object or array of object if command succeed. Objects are null if records are not found.
###
@@ -318,29 +329,23 @@ module.exports = class Records extends Schema
@unserialize records
callback null, if isArray then records else records[0]
###
- id(records, [options], callback)
- --------------------------------
- Extract record identifiers or set the identifier to null if its associated record could not be found.
+
+ `id(records, [options], callback)`
+ ----------------------------------
+ Extract record identifiers or set the identifier to null if its associated record could not be found.
The method doesn't hit the database to validate record values and if an id is
provided, it wont check its existence. When a record has no identifier but a unique value, then its
- identifier will be fetched from Redis.
-
-
- todo: With no argument, generate an new id
- todo: IF first argument is a number, genererate the number of new id
- If first argument is an object or an array of object, extract the id from those objects
-
- The id will be set to null if the record wasn't discovered in the database
+ identifier will be fetched from Redis.
- `records` Record object or array of record objects.
+ `records` Record object or array of record objects.
`options` Options properties include:
* `accept_null` Skip objects if they are provided as null.
* `object` Return an object in the callback even if it recieve an id instead of a record.
- Use reverse index lookup to extract user ids:
+ Use reverse index lookup to extract user ids:
Users.get 'users', properties:
user_id: identifier: true
@@ -352,7 +357,7 @@ module.exports = class Records extends Schema
should.not.exist err
console.log ids
- Use the `object` option to return records instead of ids:
+ Use the `object` option to return records instead of ids:
Users.get 'users', properties:
user_id: identifier: true
@@ -416,14 +421,14 @@ module.exports = class Records extends Schema
`list([options], callback)`
---------------------------
- List records with support for filtering and sorting.
+ List records with support for filtering and sorting.
`options` Options properties include:
- * `where`
- * `operation`
- * `sort`
- * `direction`
+ * `where` Hash of property/value used to filter the query.
+ * `operation` Redis operation in case of multiple `where` properties, default to `union`.
+ * `sort` Name of the property by which records should be ordered.
+ * `direction` One of `asc` or `desc`, default to `asc`.
* `properties` Array of properties to be returned.
* `milliseconds` Convert date value to milliseconds timestamps instead of `Date` objects.
* `seconds` Convert date value to seconds timestamps instead of `Date` objects.
@@ -431,7 +436,7 @@ module.exports = class Records extends Schema
`callback` Called on success or failure. Received parameters are:
* `err` Error object if any.
- * `records` Records fetched from Redis.
+ * `records` Records fetched from Redis.
Using the `union` operation:
@@ -517,10 +522,11 @@ module.exports = class Records extends Schema
multi.del tempkey if tempkey
multi.exec()
###
- Remove one or several records
- -----------------------------
- Take one or multiple records and remove them from the database as
- well as all the indexes referencing those records.
+
+ `remove(records, callback)`
+ ---------------------------
+ Remove one or several records from the database. The function will also
+ handle all the indexes referencing those records.
###
remove: (records, callback) ->
@@ -549,13 +555,13 @@ module.exports = class Records extends Schema
callback null, records.length
###
- `update(records, [options], callback)` Update one or several records
- --------------------------------------------------------------------
- Take a record object or multiple records and insert them. The records must
- exists in the database or an error will be returned in the callback. The existence
- of a record may be discovered through its identifier or the presence of a unique property.
+ `update(records, [options], callback)`
+ --------------------------------------
+ Update one or several records. The records must exists in the database or
+ an error will be returned in the callback. The existence of a record may
+ be discovered through its identifier or the presence of a unique property.
- `records` Record object or array of record objects.
+ `records` Record object or array of record objects.
`options` Options properties include:
@@ -564,12 +570,13 @@ module.exports = class Records extends Schema
`callback` Called on success or failure. Received parameters are:
* `err` Error object if any.
- * `records` Records with their newly created identifier.
+ * `records` Records with their newly created identifier.
Records are not validated, it is the responsability of the client program calling `create` to either
- call `validate` before calling `create` or to passs the `validate` options.
+ call `validate` before calling `create` or to passs the `validate` options.
- Updating a single record
+ Updating a single record:
+
Users.update
username: 'my_username'
age: 28
View
142 lib/Schema.coffee
@@ -5,9 +5,13 @@ isEmail = (email) ->
/^[a-z0-9,!#\$%&'\*\+\/\=\?\^_`\{\|}~\-]+(\.[a-z0-9,!#\$%&'\*\+\/\=\?\^_`\{\|}~\-]+)*@[a-z0-9\-]+(\.[a-z0-9\-]+)*\.([a-z]{2,})$/.test(email)
###
+
Schema definition
=================
+A schema is defined once and must no change. We dont support schema migration at the moment. The `Records`
+class inherit all the properties and method of the shema.
+
`ron` Reference to the Ron instance
`options` Schema definition. Options include:
@@ -21,20 +25,23 @@ Record properties may be defined by the following keys:
* `identifier` Mark this property as the identifier, only one property may be an identifier.
* `index` Create an index on the property.
* `unique` Create a unique index on the property.
-* `email` Validate the string as an email.
* `temporal` Add creation and modification date transparently.
-Sample
-------
+Define a schema from a configuration object:
+
+ ron.get 'users', properties:
+ user_id: identifier: true
+ username: unique: true
+ password: true
- ron.get
- name: 'users'
- properties:
- user_id: identifier: true
- username: unique: true
- password: true
+Define a schema with a declarative approach:
-and then you can manipulate your records
+ Users = ron.get 'users'
+ Users.indentifier 'user_id'
+ Users.unique 'username'
+ Users.property 'password'
+
+Whichever your style, you can then manipulate your records:
users = ron.get 'users'
users.list (err, users) -> console.log users
@@ -53,51 +60,30 @@ module.exports = class Schema
identifier: null
index: {}
unique: {}
- email: {}
if options.temporal
@temporal options.temporal
if options.properties
for name, value of options.properties
@property name, value
###
- Define property as en email
- ---------------------------
- Check that a property validate as an email
-
- Calling this function without any argument will return all the email
- properties.
-
- Example:
- User.unique 'username'
- User.get { username: 'me' }, (err, user) ->
- console.log "This is #{user.username}"
- ###
- email: (property) ->
- # Set the property
- if property?
- @data.properties[property] = {} unless @data.properties[property]?
- @data.properties[property].email = true
- @data.email[property] = true
- @
- # Get the property
- else
- @data.email
- ###
- Hash a key
- ----------
- This is a utility function used when redis key are created out of
- uncontrolled values.
+
+ `hash(key)`
+ -------------
+ Utility function used when a redis key is created out of
+ uncontrolled character (like a string instead of an int).
+
###
hash: (key) ->
key = "#{key}" if typeof key is 'number'
return if key? then crypto.createHash('sha1').update(key).digest('hex') else 'null'
###
- Define a property as an identifier
- ----------------------------------
- An identifier is a property which uniquely define a record.
- Internaly, those type of property are stored in set.
-
- Calling this function without any argument will return the identifier if any.
+
+ `identifier(property)`
+ ------------------------
+ Define a property as an identifier or return the record identifier if
+ called without any arguments. An identifier is a property which uniquely
+ define a record. Inside Redis, identifier values are stored in set.
+
###
identifier: (property) ->
# Set the property
@@ -111,21 +97,24 @@ module.exports = class Schema
else
@data.identifier
###
- Define a property as indexable or return all index properties
- -------------------------------------------------------------
- An indexed property allow records access by its property value. For example,
+
+ `index([property])`
+ -------------------
+ Define a property as indexable or return all index properties. An
+ indexed property allow records access by its property value. For example,
when using the `list` function, the search can be filtered such as returned
- records match one or multiple values.
+ records match one or multiple values.
Calling this function without any argument will return an array with all the
- indexed properties.
+ indexed properties.
- Example:
+ Example:
User.index 'email'
User.list { filter: { email: 'my@email.com' } }, (err, users) ->
console.log 'This user has the following accounts:'
for user in user
console.log "- #{user.username}"
+
###
index: (property) ->
# Set the property
@@ -138,22 +127,24 @@ module.exports = class Schema
else
Object.keys(@data.index)
###
- Retrieve/define a new property
+
+ `property(property, [schema])`
------------------------------
Define a new property or overwrite the definition of an
existing property. If no schema is provide, return the
- property information.
+ property information.
Calling this function with only the property argument will return the schema
- information associated with the property.
+ information associated with the property.
It is possible to define a new property without any schema information by
- providing an empty object.
+ providing an empty object.
- Example:
+ Example:
+
User.property 'id', identifier: true
User.property 'username', unique: true
- User.property 'email', { index: true, email: true }
+ User.property 'email', { index: true, type: 'email' }
User.property 'name', {}
###
@@ -165,7 +156,6 @@ module.exports = class Schema
@identifier property if schema.identifier
@index property if schema.index
@unique property if schema.unique
- @email property if schema.email
@
else
@data.properties[property]
@@ -212,10 +202,12 @@ module.exports = class Schema
record[property] = value.getTime()
if isArray then records else records[0]
###
- `temporal([options])` Define or retrieve temporal definition
- ------------------------------------------------------------
- Marking a schema as temporal will transparently add two new date properties,
- the date when the record was created (by default "creation"), and the date
+
+ `temporal([options])`
+ ---------------------
+ Define or retrieve temporal definition. Marking a schema as
+ temporal will transparently add two new date properties, the
+ date when the record was created (by default "creation"), and the date
when the record was last updated (by default "modification").
###
@@ -231,18 +223,20 @@ module.exports = class Schema
else
[ @data.temporal.creation, @data.temporal. modification ]
###
- Define a property as unique
- ---------------------------
- An unique property is similar to a unique one but,... unique. In addition for
- being filterable, it could also be used as an identifer to access a record.
-
- Calling this function without any argument will return an arrya with all the
- unique properties.
+
+ `unique([property])`
+ --------------------
+ Define a property as unique or retrieve all the unique properties if no
+ argument is provided. An unique property is similar to a index
+ property but the index is stored inside a Redis hash. In addition to being
+ filterable, it could also be used as an identifer to access a record.
- Example:
+ Example:
+
User.unique 'username'
User.get { username: 'me' }, (err, user) ->
console.log "This is #{user.username}"
+
###
unique: (property) ->
# Set the property
@@ -300,19 +294,19 @@ module.exports = class Schema
if isArray then records else records[0]
###
- `validate(records, [options])` Validate
- ---------------------------------------
+ `validate(records, [options])`
+ ------------------------------
Validate the properties of one or more records. Return a validation
object or an array of validation objects depending on the provided
records arguments. Keys of a validation object are the name of the invalid
- properties and their value is a string indicating the type of error.
+ properties and their value is a string indicating the type of error.
- `records` Record object or array of record objects.
+ `records` Record object or array of record objects.
`options` Options include:
* `throw` Throw errors on first invalid property instead of returning a validation object.
- * `skip_required` Doesn't validate missing properties defined as `required`, usefull for partial update.
+ * `skip_required` Doesn't validate missing properties defined as `required`, usefull for partial update.
###
validate: (records, options = {}) ->
View
67 lib/doc.coffee
@@ -0,0 +1,67 @@
+
+fs = require 'fs'
+mecano = require 'mecano'
+each = require 'each'
+
+date = -> d = (new Date).toISOString()
+
+convert_anchor = (text) ->
+ re_anchor = /`([\w]+)\(/g
+ text.replace re_anchor, (str, code) ->
+ # At least in FF, <a href="" /> doesn't close the tag
+ "<a name=\"#{code}\"></a>`#{code}("
+
+convert_code = (text) ->
+ re_code = /\n(\s{4}\s*?\w[\s\S]*?)\n(?!\s)/g
+ text.replace re_code, (str, code) ->
+ code = code.split('\n').map((line)->line.substr(4)).join('\n')
+ "\n```coffeescript\n#{code}\n```\n"
+
+each( ['Client', 'Schema', 'Records'] )
+.parallel( true )
+.on 'item', (next, file) ->
+ source = "#{__dirname}/#{file}.coffee"
+ destination = "#{__dirname}/../docs/#{file.toLowerCase()}.md"
+ fs.readFile source, 'ascii', (err, content) ->
+ return console.error err if err
+ re = /###\n([\s\S]*?)\n( *)###/g
+ re_title = /([\s\S]+)\n={2}=+([\s\S]*)/g
+ match = re.exec content
+ # docs += match[1]
+ match = re_title.exec match[1]
+ docs = """
+ ---
+ language: en
+ layout: page
+ title: "#{match[1]}"
+ date: #{date()}
+ comments: false
+ sharing: false
+ footer: false
+ navigation: ron
+ github: https://github.com/wdavidw/node-ron
+ ---
+ #{convert_code match[2]}
+ """
+ while match = re.exec content
+ # Unindent
+ match[1] = match[1].split('\n').map((line)->line.substr(4)).join('\n')
+ docs += convert_code convert_anchor match[1]
+ docs += '\n'
+ fs.writeFile destination, docs, next
+.on 'both', (err) ->
+ return console.error err if err
+ console.log 'Documentation generated'
+ destination = process.argv[2]
+ return unless destination
+ each( ['index', 'client', 'schema', 'records'] )
+ .on 'item', (next, file) ->
+ mecano.copy
+ source: "#{__dirname}/../docs/#{file}.md"
+ destination: destination
+ force: true
+ , next
+ .on 'both', (err) ->
+ return console.error err if err
+ console.log 'Documentation published'
+
View
2  package.json
@@ -18,6 +18,8 @@
"redis": "latest"
},
"devDependencies": {
+ "each": "latest",
+ "mecano": "latest",
"mocha": "latest",
"should": "latest"
},
View
59 readme.md
@@ -25,7 +25,7 @@ client = ron({
Users = client.get('users');
Users.property('id', {identifier: true});
Users.property('username', {unique: true});
-Users.property('email', {index: true, email: true});
+Users.property('email', {index: true, type: 'email'});
Users.property('name', {});
// Record manipulation
Users.create(
@@ -41,43 +41,42 @@ The library provide
* Documented and tested API
* Records access with indexes and unique values
-* Records are pure object, no extended class, no magic
+* Records are pure object, no state, no magic
Client API
----------
-* Client::constructor
-* Client::quit
-* Client::define
+* [Client::constructor](http://www.adaltas.com/projects/node-ron/client.html#ron)
+* [Client::get](http://www.adaltas.com/projects/node-ron/client.html#get)
+* [Client::quit](http://www.adaltas.com/projects/node-ron/client.html#quit)
Schema API
----------
-* Records::email
-* Records::hash
-* Records::identifier
-* Records::index
-* Records::property
-* Records::name
-* Records::serialize
-* Records::temporal
-* Records::unique
-* Records::unserialize
-* Records::validate
-
-Record API
-----------
-
-* Records::all
-* Records::clear
-* Records::count
-* Records::create
-* Records::exists
-* Records::get
-* Records::id
-* Records::list
-* Records::remove
-* Records::update
+* [Records::hash](http://www.adaltas.com/projects/node-ron/schema.html#hash)
+* [Records::identifier](http://www.adaltas.com/projects/node-ron/schema.html#identifier)
+* [Records::index](http://www.adaltas.com/projects/node-ron/schema.html#index)
+* [Records::property](schema.html#property)
+* [Records::name](http://www.adaltas.com/projects/node-ron/schema.html#name)
+* [Records::serialize](http://www.adaltas.com/projects/node-ron/schema.html#serialize)
+* [Records::temporal](http://www.adaltas.com/projects/node-ron/schema.html#temporal)
+* [Records::unique](http://www.adaltas.com/projects/node-ron/schema.html#unique)
+* [Records::unserialize](http://www.adaltas.com/projects/node-ron/schema.html#unserialize)
+* [Records::validate](http://www.adaltas.com/projects/node-ron/schema.html#validate)
+
+Records API
+-----------
+
+* [Records::all](http://www.adaltas.com/projects/node-ron/records.html#all)
+* [Records::clear](http://www.adaltas.com/projects/node-ron/records.html#clear)
+* [Records::count](http://www.adaltas.com/projects/node-ron/records.html#count)
+* [Records::create](http://www.adaltas.com/projects/node-ron/records.html#create)
+* [Records::exists](http://www.adaltas.com/projects/node-ron/records.html#exists)
+* [Records::get](http://www.adaltas.com/projects/node-ron/records.html#get)
+* [Records::id](http://www.adaltas.com/projects/node-ron/records.html#id)
+* [Records::list](http://www.adaltas.com/projects/node-ron/records.html#list)
+* [Records::remove](http://www.adaltas.com/projects/node-ron/records.html#remove)
+* [Records::update](http://www.adaltas.com/projects/node-ron/records.html#update)
Run tests
---------
View
8 test/client_get.coffee
@@ -59,12 +59,12 @@
properties:
id: identifier: true
username: unique: true
- email: { index: true, email: true }
+ email: { type: 'email', index: true }
name: {}
# Retrieve properties
Users.property('id').should.eql { name: 'id', identifier: true, type: 'int' }
Users.property('username').should.eql { name: 'username', unique: true }
- Users.property('email').should.eql { name: 'email', index: true, email: true }
+ Users.property('email').should.eql { name: 'email', index: true, type: 'email' }
Users.property('name').should.eql { name: 'name' }
client.quit next
@@ -74,12 +74,12 @@
# Define properties
Users.should.eql Users.property('id', identifier: true)
Users.should.eql Users.property('username', unique: true)
- Users.should.eql Users.property('email', { index: true, email: true })
+ Users.should.eql Users.property('email', { index: true, type: 'email' })
Users.should.eql Users.property('name', {})
# Retrieve properties
Users.property('id').should.eql { name: 'id', identifier: true, type: 'int' }
Users.property('username').should.eql { name: 'username', unique: true }
- Users.property('email').should.eql { name: 'email', index: true, email: true }
+ Users.property('email').should.eql { name: 'email', index: true, type: 'email' }
Users.property('name').should.eql { name: 'name' }
client.quit next
Please sign in to comment.
Something went wrong with that request. Please try again.