Permalink
Browse files

default values for getting stuff

  • Loading branch information...
1 parent 0a932f7 commit bbfb09bbe29f58911a89a4fbb783022c4239d332 @Radagaisus committed Nov 3, 2012
Showing with 104 additions and 12 deletions.
  1. +31 −1 README.md
  2. +2 −0 history.md
  3. +41 −11 lib/orpheus.coffee
  4. +30 −0 test/orpheus.spec.coffee
View
@@ -212,7 +212,7 @@ user('putin')
```
### Getting the Model ID
-Only when new models are created `.exec()` receives the model ID as the last argument.
+When new models are created `.exec()` receives the model ID as the last argument.
```coffee
user()
@@ -223,6 +223,7 @@ user()
.exec()
```
+
### Separate Callbacks
Just like with the `multi` command you can supply a separate callback for specific commands.
@@ -247,6 +248,35 @@ player('danny').when( ->
.exec()
```
+### Default Values
+Use the `default` option to pass a default value to all types:
+
+```coffee
+class User extends Orpheus
+ constructor: ->
+ @str 'name', default: 'John Doe'
+
+ user = User.create()
+
+ user('nope')
+ .name.get()
+ .exec (err, res) ->
+ log res.name # 'John Doe'
+```
+
+Note that default values will be returned in all the get commands of the type. So if you have `{someData: true}` as a default for a zset, you will get that back when you request a `zrank` of a non-existent member:
+
+```
+# User Model
+@zset 'visits', default: {'/': 0}
+
+# Query
+user('rage')
+ .visits.rank('/404.html') # No such visit, default is returned
+ .exec (err, res) ->
+ log res.visits # unexpected default zset value: {'/': 0}
+```
+
### Relations
```coffee
View
@@ -8,6 +8,8 @@
- Everything is now being tested with redis 2.6.
+- Added default values for all types, just pass `@str 'name', default: 'John Doe'`.
+
# 0.1.10
- Removed the @private property. Undocumented `getall` function removed as well. Use customized getters or remove the private properties in your controllers.
View
@@ -68,12 +68,21 @@ class Orpheus extends EventEmitter
# Create the model
super()
+
+ # Creates a dynamic function that addes a field for
+ # a specific type 'f'. For example, `create_field('str')`
+ # will create the function `@str(field, options)` that
+ # gets the field name and its options and pushes
+ # them to the model schema.
+ #
+ # - f - the type, as string: 'str', 'num', etc
+ #
create_field: (f) ->
@[f] = (field, options = {}) ->
throw new Error("Field name already taken") if @model[field]
@model[field] =
- type: f
+ type : f
options: options
return field
@@ -213,6 +222,21 @@ class OrpheusAPI
# Used when retrieving information from redis
# This schema tells us how to convert the reponse
+
+
+ # The response schema is used to figure out how
+ # to convert the multi command array response to
+ # a model object.
+ #
+ # We populate the schema when the commands are
+ # issued.
+ #
+ # @_res_schema.push
+ # type: type # The field type: 'num'
+ # name: key # The field name: 'points'
+ # with_scores: false # used on zset commands with
+ # # scores, as they are converted
+ # # to either objects or arrays
@_res_schema = []
# New or existing id
@@ -454,18 +478,19 @@ class OrpheusAPI
@exec fn
- # Convert the results back from the multi
- # values we get from node redis. The response
+ # Converts the results back from the multi
+ # array values we get from node redis. The response
# schema, @_res_schema, is populated when we
# add commands, and now, based on the types it
# stored, we convert everything back to an object.
- _create_getter_object: (res) ->
+ _create_getter_object: (res) =>
new_res = {}
for s,i in @_res_schema
+
# Convert numbers from their string representation
if s.type is 'num'
- new_res[s.name] = Number res[i]
+ new_res[s.name] = Number(res[i])
# Convert zsets with scores to a key->value hash
else if s.type is 'zset' and s.with_scores
@@ -478,9 +503,14 @@ class OrpheusAPI
else
new_res[s.name] = res[i]
- # Remove Empty values: null, undefined and []
- if not new_res[s.name] or (_.isEmpty(new_res[s.name]) and _.isObject(new_res[s.name]))
- delete new_res[s.name]
+ # Use the field's defaults or delete the field if
+ # it's empty (undefined, null, {}, [])
+ if not new_res[s.name] or (_.isObject(new_res[s.name]) and _.isEmpty(new_res[s.name]))
+
+ if _.isUndefined @model[s.name].options.default
+ then delete new_res[s.name]
+ else new_res[s.name] = @model[s.name].options.default
+
return new_res
@@ -493,11 +523,11 @@ class OrpheusAPI
exec: (fn) ->
fn ||= -> # noop
+ # Check for validation errors
unless @validation_errors.valid()
if @error_func
- return @error_func @validation_errors, @id
- else
- return fn @validation_errors, false, @id
+ then return @error_func @validation_errors, @id
+ else return fn @validation_errors, null, @id
@redis
.multi(@_commands)
View
@@ -306,6 +306,7 @@ describe 'Get', ->
.err ->
expect(1).toBe 2
.exec (res) ->
+ log res
expect(res.str).toBe 'str'
expect(res.num).toBe 2
expect(res.set1).toBe 1
@@ -1003,3 +1004,32 @@ describe 'Relations', ->
user('rada').add_email_reply '#142', ->
done()
+
+# Defaults
+# -------------------------
+describe 'Defaults', ->
+
+ it 'Returns a default value', (done) ->
+ class User extends Orpheus
+ constructor: ->
+ @str 'name', default: 'John Doe'
+ @num 'points', default: 0
+ @hash 'reviews', default:
+ no_reviews: null
+ @list 'activities', default: []
+ @set 'experience', default: []
+ @zset 'troll', default: 'trololol'
+ @str 'sup'
+
+ user = User.create()
+
+ user('john')
+ .get (err, res) ->
+ expect(res.name).toBe 'John Doe'
+ expect(res.points).toBe 0
+ expect(res.reviews.no_reviews).toBe null
+ expect(res.activities.length).toBe 0
+ expect(res.experience.length).toBe 0
+ expect(res.troll).toBe 'trololol'
+ expect(res.sup).toBeUndefined() # No default
+ done()

0 comments on commit bbfb09b

Please sign in to comment.