Permalink
Browse files

starting to work on validations

  • Loading branch information...
Radagaisus committed Jun 5, 2012
1 parent f12b6ea commit b4d0defd846922118e8fd341af751bf57f92aee2
Showing with 125 additions and 32 deletions.
  1. +4 −0 Cakefile
  2. +16 −9 README.md
  3. +38 −2 lib/commands.coffee
  4. +65 −17 lib/orpheus.coffee
  5. +2 −4 test/orpheus.spec.coffee
View
@@ -7,6 +7,10 @@ task 'build', "Compiles to JavaScript", ->
task 'test', "Testing using Jasmine-Node", ->
run 'jasmine-node test/orpheus.spec.coffee --verbose --color --forceexit --coffee'
+
+task 'dev', "Start developing stuff", ->
+ run 'npm install'
+ run 'jasmine-node test/orpheus.spec.coffee --verbose --color --forceexit --coffee'
task 'docs', "Create documenations using Docco", ->
run 'docco lib/*.coffee'
View
@@ -122,14 +122,21 @@ A stable release will be out soon
client: redis.createClient()
prefix: 'bookapp'
```
+## Validations ##
-## Test ##
-`jasmine-node test/orpheus.spec.coffee --verbose --color --coffee --forceexit`
-## Planned ##
-- `@map` - delete, setnx and minor fixes
-- type and cap for lists
-- Default validations (for type)
-- Validation helpers
-- `@json` data type
-- Denormalization helpers
+## Development ##
+### Test ###
+`cake test`
+
+### Contribute ###
+
+- `cake dev`
+- Add your tests.
+- Do your thing.
+- `cake dev`
+
+## Roadmap ##
+
+- Validations
+- Promises
View
@@ -1,6 +1,42 @@
# Redis Commands
#-------------------------------------#
module.exports =
+ validations:
+ # String
+ hsetnx: true
+ hset: true
+
+ # Number
+ hincrby: true
+ hincrbyfloat: true
+
+ # List
+ lpush: true # several values, 1
+ lpushx: true
+ rpush: true # several values, 1
+ rpushx: true
+
+ # Set
+ sadd: true # several values, 1
+
+ # Zset
+ zadd: true # several values, reverted, 2
+ zincrby: true # reverted
+
+ # Hash
+ hmset: true # several values, 2
+
+ # Validations does not support:
+ # - zinterstore
+ # - zunionstore
+ # - sunionstore
+ # - smove
+ # - sinterstore
+ # - sdiffstore
+ # - lset
+ # - linsert
+ # - brpoplpush and friends
+
str: [
'hdel',
'hexists',
@@ -66,7 +102,7 @@ module.exports =
'zrangebyscore',
'zrank',
'zrem',
- 'zremreangebyrank',
+ 'zremrangebyrank',
'zremrangebyscore',
'zrevrange',
'zrevrangebyscore',
@@ -102,7 +138,7 @@ module.exports =
command_map:
add:
num: 'hincrby'
- str: 'hset' # we don't optimize with hmset
+ str: 'hset' # we don't optimize with hmset
set: 'sadd'
zset: 'zincrby'
list: 'lpush'
View
@@ -7,6 +7,7 @@ os = require 'os' # id generation
inflector = require './inflector' # pluralize
commands = require './commands' # redis commands
command_map = commands.command_map
+validation_map = commands.validations
log = console.log
@@ -100,8 +101,9 @@ class Orpheus
return @
# Add a validation function to a field
- # e.g. @validate 'name', (name) ->
- # if name is 'jay' then true else message: 'beep!'
+ # Example:
+ # @validate 'name', (name) ->
+ # if name is 'jay' then true else 'invalid!'
validate: (key, func) ->
@validations[key] ||= []
@validations[key].push func
@@ -159,7 +161,7 @@ class OrpheusAPI
# Redis multi commands
@_commands = []
- @validation_errors = []
+ @validation_errors = new OrpheusValidationErrors
# new or existing id
@id = id or @_generate_id()
@@ -178,6 +180,7 @@ class OrpheusAPI
# shorthand, use add instead of sadd
@[prel][f[1..]] = @[prel][f]
+
# Extract related models information,
# one by one, based on an array of ids
# e.g. user(10).books.get ['dune', 'valis']
@@ -207,11 +210,31 @@ class OrpheusAPI
@[key] = {}
for f in commands[value.type]
- do (key, f) =>
+ type = value.type
+ do (key, f, type) =>
@[key][f] = (args...) =>
# add multi command
@_commands.push _.flatten [f, @_get_key(key), args]
+ # Run validation, if needed
+ validation = validation_map[f]
+ if validation and @validations[key]
+ log "you should validate me"
+ if type is 'str'
+ log "you string", @validations
+ # Regular validations
+ for v in @validations[key]
+ if _.isFunction v
+ result = v args...
+ log "result: ", result
+ unless result is true
+ @validation_errors.add key,
+ msg: result
+ command: f
+ args: args
+ value: args[0]
+ return @ # no need to check the extra commands
+
# Extra commands: mapping
@_extra_commands(key, f, args) if @model[key].options.map
@@ -221,6 +244,7 @@ class OrpheusAPI
# str: h, num: h, list: l, set: s, zset: z
@[key][f[1..]] = @[key][f] if f[0] is commands.shorthands[value.type]
+
# create the add, set and del commands
# based on the commands map they call
# the respective command for the key,
@@ -230,16 +254,6 @@ class OrpheusAPI
do (f) =>
@[f] = (o) ->
for k, v of o
-
- # Run Validations
- if @validations[k]
- for valid in @validations[k]
- msg = valid(v)
- unless msg is true
- msg = new Error(msg.message)
- msg.type = 'validation'
- @validation_errors.push msg
-
# Add the Command. Note we won't actually
# execute any of this commands if the
# validation has failed.
@@ -303,7 +317,7 @@ class OrpheusAPI
delete: (fn) ->
# flush commands and validations
@_commands = []
- @validation_errors = []
+ @validation_errors.empty()
hdel_flag = false # no need to delete a hash twice
for key, value of @model
@@ -391,8 +405,9 @@ class OrpheusAPI
# execute the multi commands
exec: (fn) ->
- if @validation_errors.length
- return fn @validation_errors, false, @id
+ unless @validation_errors.valid()
+ log "heyyy"
+ return fn null, @validation_errors, @id
@redis
.multi(@_commands)
@@ -406,4 +421,37 @@ class OrpheusAPI
Orpheus.trigger 'error', err
fn err, res, @id
+# Orpheus Validation errors
+#-------------------------------------#
+class OrpheusValidationErrors
+
+ constructor: ->
+ @errors = {}
+
+ valid: ->
+ _.isEmpty @errors
+
+ invalid: ->
+ !@valid()
+
+ add: (field, error) ->
+ # Add date, useful for logging
+ error.date = new Date().getTime()
+
+ @errors[field] || = []
+ @errors[field].push error
+
+ empty: ->
+ @errors = {}
+
+ toResponse: ->
+ # 400 is Bad Request
+ # See http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.1
+ obj = {status: 400}
+
+ # Add the validation error messages
+ for k,v of @errors
+ obj[k] = (m.msg for m in v)
+ obj
+
module.exports = Orpheus
View
@@ -615,7 +615,7 @@ describe 'Validation', ->
class Player extends Orpheus
constructor: ->
@str 'name'
- @validate 'name', (s) -> if s is 'almog' then true else message: 'String should be almog'
+ @validate 'name', (s) -> if s is 'almog' then true else 'String should be almog'
player = Player.create()
player('15').set
@@ -626,9 +626,7 @@ describe 'Validation', ->
player('15').set
name: 'chiko'
.exec (err, res, id) ->
-
- expect(err[0].type).toBe 'validation'
- expect(err[0].toString()).toBe 'Error: String should be almog'
+ expect(res.toResponse().status).toBe 400
done()

0 comments on commit b4d0def

Please sign in to comment.