user.ops, so that it's now available in the API. Also add handling of
200 errors to simply notify and continue on up to server (friendly messages)
This commit is contained in:
Tyler Renelle 2013-12-12 15:57:01 -07:00
parent aac5cc6d39
commit ab5bd6d5af
2 changed files with 124 additions and 6 deletions

View file

@ -10478,9 +10478,15 @@ var process=require("__browserify_process");(function() {
});
}
if (scheduleMisses > 0) {
api.score(user, task, 'down', {
times: scheduleMisses,
cron: true
user.ops.score({
params: {
id: task.id,
direction: 'down'
},
query: {
times: scheduleMisses,
cron: true
}
});
}
}
@ -10862,7 +10868,40 @@ var process=require("__browserify_process");(function() {
/*
This wraps the user prototype, giving it functions
User is now wrapped (both on client and server), adding a few new properties:
* getters (_statsComputed, tasks, etc)
* user.fns, which is a bunch of helper functions
These were originally up above, but they make more sense belonging to the user object so we don't have to pass
the user object all over the place. In fact, we should pull in more functions such as cron(), updateStats(), etc.
* user.ops, which is super important:
If a function is inside user.ops, it has magical properties. If you call it on the client it updates the user object in
the browser and when it's done it POSTs to the server, calling `src/controllers/user.js#OP_NAME` (the exact same name
of the op function). The first argument `req` is `{query, body, params}`, it's what the express controller function
expects. Because of this, we don't construct op functions in standard way (function(arg1,arg2,arg3){}), but instead
the first arg is {query, body, params} and the second is a callback(err, results). `query` is the most commonly-used,
but body is useful for updates/creates (eg, tasks), and params are used in those cases too (eg, {op:'updateTask',{params:{id:task.id}, body:task}}})
This needs refinement: see http://stackoverflow.com/questions/4024271/rest-api-best-practices-where-to-put-parameters
If `src/controllers/user.js#OP_NAME` doesn't exist, it's automatically added. It runs the code in user.ops.OP_NAME
to update the user model server-side, then performs `user.save()`. You can see this in action for `user.ops.buy`. That
function doesn't exist on the server - so the client calls it, it updates user in the browser, auto-POSTs to server, server
handles it by calling `user.ops.buy` again (to update user on the server), and then saves. We can do this for
everything that doesn't need any code different from what's in user.ops.OP_NAME for special-handling server-side. If we
*do* need special handling, just add `src/controllers/user.js#OP_NAME` to override the user.ops.OP_NAME, and just be
sure to call user.ops.OP_NAME at some point within the overridden function.
TODO
* Allow different error handling - like "200 - not enough gold" (which shouldn't be treated as 500)
* Is this the best way to wrap the user object? I thought of using user.prototype, but user is an object not a Function.
user on the server is a Mongoose model, so we can use prototype - but to do it on the client, we'd probably have to
move to $resource for user
* Move to $resource!
* Migrate the remaining parts: Tags, feeding pets, etc
BUGS
* User registration doesn't refresh, and you have to click "Play" again (and refresh *yet again* after getting in)
* Daily repeats don't have the days showing up (translations issue?)
*/
@ -10897,6 +10936,55 @@ var process=require("__browserify_process");(function() {
}
return task;
},
feed: function(req, cb) {
var egg, evolve, food, pet, potion, userPets, _ref, _ref1, _ref2, _ref3;
_ref2 = [(_ref = req.query) != null ? _ref.pet : void 0, content.food[(_ref1 = req.query) != null ? _ref1.food : void 0]], pet = _ref2[0], food = _ref2[1];
_ref3 = pet.split('-'), egg = _ref3[0], potion = _ref3[1];
if (!(pet && food)) {
return cb("?food=__&pet=__ are both required in query, and must be valid");
}
if (content.specialPets[pet]) {
return cb("Can't feed this pet.");
}
if (user.items.mounts[pet] && (userPets[pet] >= 50 || food.name === 'Saddle')) {
return cb("You already have that mount");
}
userPets = user.items.pets;
evolve = function() {
userPets[pet] = 0;
user.items.mounts[pet] = true;
if (pet === user.items.currentPet) {
user.items.currentPet = "";
}
return cb({
code: 200,
message: "You have tamed " + egg + ", let's go for a ride!"
}, req);
};
if (food.name === 'Saddle') {
evolve();
} else {
if (food.target === potion) {
userPets[pet] += 5;
cb({
code: 200,
message: "" + egg + " really likes the " + food.name + "!"
}, req);
} else {
userPets[pet] += 2;
cb({
code: 200,
message: "" + egg + " eats the " + food.name + " but doesn't seem to enjoy it."
}, req);
}
if (userPets[pet] >= 50 && !user.items.mounts[pet]) {
evolve();
}
}
user.items.pets[pet] = userPets[pet];
user.items.food[food.name]--;
return typeof cb === "function" ? cb(null, req) : void 0;
},
buy: function(req, cb) {
var item, key, _ref;
key = req.query.key;

View file

@ -344,7 +344,8 @@ api.cron = (user, options={}) ->
_.times daysMissed, (n) ->
thatDay = moment(now).subtract('days', n + 1)
scheduleMisses++ if api.shouldDo(thatDay, repeat, user.preferences)
api.score(user, task, 'down', {times:scheduleMisses, cron:true}) if scheduleMisses > 0
if scheduleMisses > 0
user.ops.score({params:{id:task.id, direction:'down'}, query:{times:scheduleMisses, cron:true}})
switch type
when 'daily'
@ -647,6 +648,35 @@ api.wrap = (user) ->
cb? null, req
task
feed: (req, cb) ->
[pet, food] = [req.query?.pet, content.food[req.query?.food]]
[egg, potion] = pet.split('-')
return cb("?food=__&pet=__ are both required in query, and must be valid") unless pet and food # TODO validation on pet string
return cb("Can't feed this pet.") if content.specialPets[pet]
return cb("You already have that mount") if user.items.mounts[pet] and (userPets[pet] >= 50 or food.name is 'Saddle')
userPets = user.items.pets
evolve = ->
userPets[pet] = 0
user.items.mounts[pet] = true
user.items.currentPet = "" if pet is user.items.currentPet
cb {code:200, message:"You have tamed #{egg}, let's go for a ride!"}, req
if food.name is 'Saddle'
evolve()
else
if food.target is potion
userPets[pet] += 5
cb {code:200, message: "#{egg} really likes the #{food.name}!"}, req
else
userPets[pet] += 2
cb {code:200, message: "#{egg} eats the #{food.name} but doesn't seem to enjoy it."}, req
evolve() if userPets[pet] >= 50 and not user.items.mounts[pet]
user.items.pets[pet] = userPets[pet]
user.items.food[food.name]--
cb? null, req
buy: (req, cb) ->
{key} = req.query
item = if key is 'potion' then content.potion else content.gear.flat[key]
@ -694,7 +724,7 @@ api.wrap = (user) ->
user.stats.lvl-- if user.stats.lvl > 1
# Lose a stat point
lostStat = randomVal _.reduce ['str','con','per','int'], ((m,k)->(m[k]=k if user.stats[v];m)), {}
lostStat = randomVal _.reduce(['str','con','per','int'], ((m,k)->(m[k]=k if user.stats[v];m)), {})
user.stats[lostStat]-- if lostStat
# Lose a gear piece