Confused by Coffeescript - coffeescript

I'm a complete novice so sorry if this is a silly question.
I'm writing a Hubot script. I don't understand what I'm doing wrong. I need some help please. I'm trying to get scores from MLB.
I have this...
module.exports = (robot) ->
robot.hear /score/i, (msg) ->
url = 'http://mlb.mlb.com/gdcross/components/game/mlb/year_2016/month_03/day_12/master_scoreboard.json'
msg.send(getData(url))
getData = (url) ->
robot.http(url)
.get() (err, res, body) ->
result = JSON.parse(body)
console.log(result.data.games.game[1].home_team_city)
team = result.data.games.game[1].home_team_city
When I run the above the console.log statement prints "Boston" but the robot prints "[Object Object]" How do I get the robot to print "Boston." Note: I intend to re-use the getData function for a bunch of other responses.
Thanks for the help.

The request doesn't return the result, because it's asynchronous. Meaning the result will only be available after the server responded.
It probably returns a Promise or something similar, or something to listen to the response.
Try this (I streamlines a bit as a coffeescript introduction, I hope it's still clear):
url = 'http://mlb.mlb.com/gdcross/components/game/mlb/year_2016/month_03/day_12/master_scoreboard.json'
module.exports = (robot) ->
robot.hear /score/i, getData
getData = (msg) ->
robot
.http url
.get() (err, res, body) ->
team = JSON.parse(body).data.games.game[1].home_team_city
console.log team
msg.send team

you just need to use a callback... like so:
module.exports = (robot) ->
robot.hear /score/i, (msg) ->
url = 'http://mlb.mlb.com/gdcross/components/game/mlb/year_2016/month_03/day_12/master_scoreboard.json'
getData url, (cb) ->
msg.send(cb)
getData = (url, successCallback) ->
robot.http(url)
.get() (err, res, body) ->
result = JSON.parse(body)
console.log(result.data.games.game[1].home_team_city)
team = result.data.games.game[1].home_team_city
successCallback(team)

Related

Coffescript Nested Do Loop / Async Nested Loop

I want to save into two collections in my mongoDB. This operations are async so I use for and do in coffee.
for machine in machines
do(machine) ->
//if machine does not exist
for part in machine.parts
do(part) ->
//if not part not exists --> save
//push part to machine parts list
//save machine
The machine parts are empty later in the db. How can I make the first do loop wait for the second do loop to finish?
EDIT Real Code Example:
recipeModel = require('../models/recipe.model')
ingredientModel = require('../models/ingredient.model')
#Save Recipe into the database
async.map recipes, (recipe, next) ->
recipeDBObject = new recipeModel()
recipeDBObject.href = recipe.href
recipeDBObject.ingredients = []
recipeModel.find({ href: recipe.href }, (err, recipeFound) ->
return next err if err
return next null, recipeFound if recipeFound.length > 0
recipeDBObject.title = recipe.title
ingredientsPushArray = []
console.log recipe.href
async.map recipe.zutaten, (ingredient, cb) ->
#Save all ingredients
ingredient.idName = ingredient.name.replace(/[^a-zA-Z0-9]+/gi, "").toLowerCase()
ingredientModel.find({ idName: ingredient.idName }, (err, ingredientFound) ->
return next err if err
if ingredientFound.length >0
ingredientDBObject = ingredientFound[0]
else
ingredientDBObject = new ingredientModel()
ingredientDBObject.name = ingredient.name
ingredientDBObject.save()
recipeDBObject.ingredients.push({"idName":ingredient.idName, "name":ingredient.name, "amount":ingredient.amount})
return cb(null, true)
)
recipeDBObject.ingredients = ingredientsPushArray
recipeDBObject.save()
return next(null, true)
)
I still don't get it working. Recipes are saved, node builds the ingredients array but it neither saves the ingredients nor does it save the array into the recipes.
EDIT 2:
async.map recipes,
(recipe, next) ->
recipeDBObject = new recipeModel()
recipeDBObject.href = recipe.href
recipeDBObject.ingredients = []
recipeModel.find({ href: recipe.href }, (err, recipeFound) ->
return next err if err
return next null, recipeFound if recipeFound.length > 0
recipeDBObject.title = recipe.title
ingredientsPushArray = []
ingredientsArray = []
async.map recipe.zutaten,
(ingredient, cb) ->
#Save all ingredients
ingredient.idName = ingredient.name.replace(/[^a-zA-Z0-9]+/gi, "").toLowerCase()
ingredientModel.find({ idName: ingredient.idName }, (err, ingredientFound) ->
return next err if err
ingredientsArray.push({"idName":ingredient.idName, "name":ingredient.name, "amount":ingredient.amount})
if ingredientFound.length >0
return cb(null, true)
else
ingredientDBObject = new ingredientModel()
ingredientDBObject.name = ingredient.name
ingredientDBObject.idName = ingredient.idName
ingredientDBObject.save((err) ->
#console.log "some erros because required is empty" if err
return cb err if err
#console.log "ingredient saved"
return cb(null, true)
)
(err, ingredientsArray) ->
console.log "This is never logged"
return err if err
recipeDBObject.ingredients = ingredientsArray
recipeDBObject.save((err)->
return err if err
return next(null, true)
)
)
)
(err) ->
console.log "show me the errors: ", err if err
Now the ingredients are saved but the recipes aren't.
Interesting ressources:
http://www.hacksparrow.com/managing-nested-asynchronous-callbacks-in-node-js.html
The easiest way is to use some module for for managing asynchronous control flow, for example
async
promise-based solutions (e.g. when, bluebird, Q)
co for ES6 generator-based control flow
Here are some simple examples.
Using async.map
async = require 'async'
async.map machines,
(machine, next) ->
# Process single machine object
Machine.findById machine._id, (err, found) ->
return next err if err # return error immediately
return next null, found if found # return the object we found
async.map machine.parts,
(part, cb) ->
# Save part to DB and call cb callback afterward
Part.create part, cb
(err, parts) ->
return next err if err # propagate error to the next handler
# All parts have been saved successfully
machine.parts = parts
# Save machine to DB and call next callback afterward
Machine.create machine, next
(err, machines) ->
if err
# Something went wrong
else
# All machine objects have been processed successfully
Using promises and when module
When = require 'when'
machines_to_save = When.filter machines, ({_id}) ->
Machine.findById(_id).then (found) -> not found
When.map machines_to_save, (machine) ->
When.map machine.parts, (part) ->
Part.create part
.then (parts) ->
machine.parts = parts
Machine.create machine
.then (saved_machines) ->
# All machines are saved
.otherwice (err) ->
# Something went wrong

CoffeeScript declare and use global funcitons

Folks, given the following code... how would one re-use the returnResult and returnError functions? Is it possible to have their scope be global to all files that are required.
fs = require 'fs'
module.exports.syncJSON = (req, res)->
returnResult = (data) ->
res.send data
returnError = (data) ->
res.send data
jsonFileContents = fs.readFileSync('sample.json', 'utf8')
returnResult(jsonFileContents)
module.exports.asyncJSON = (req, res)->
fs.readFile 'sample.json', (err, data) ->
if err
returnError(err)
else
returnResult(data.toString())
returnResult = (data) ->
res.send data
returnError = (data) ->
res.send data
I think what you're asking about is how to use returnError and returnResult in both of the exports (.asyncJSON and .syncJSON). Globals are generally a bad idea. What if someone else overwrites your global? You'll have a weird and intermittent failure. If so, all you need to do is move them outside their current scope and pass in the response object:
s = require 'fs'
returnResult = (res, data)->
res.send data
returnError = (res, err) ->
res.send err
module.exports.syncJSON = (req, res)->
jsonFileContents = fs.readFileSync('sample.json', 'utf8')
returnResult(res, jsonFileContents)
module.exports.asyncJSON = (req, res)->
fs.readFile 'sample.json', (err, data) ->
if err
returnError(res, err)
else
returnResult(res, data.toString())

Playframework WS API response processing

I use The Play WS API from PlayFramework to communicate with external API.
I need to process the received data, but don't know how. I get a response, and I want to pass it to other function like an JSON Object. How I can achieve that?
Code I use you can see below.
Thanks!
def getTasks = Action {
Async {
val promise = WS.url(getAppProperty("helpdesk.host")).withHeaders(
"Accept" -> "application/json",
"Authorization" -> "Basic bi5sdWJ5YW5vdjoyMDEzMDcwNDE0NDc=" ).get()
for {
response <- promise
} yield Ok((response.json \\ "Tasks"))
}
}
I get a response, and I want to pass it to other function like an JSON Object.
I'm not sure I understand your question, but I'm guessing you want to transform the json you receive from the WS call prior to returning to the client, and that this transformation could take several lines of code. If this is correct, then you just need to add curly brackets around your yield statement so you can do more work on the response:
def getTasks = Action {
Async {
val promise = WS.url(getAppProperty("helpdesk.host")).withHeaders(
"Accept" -> "application/json",
"Authorization" -> "Basic bi5sdWJ5YW5vdjoyMDEzMDcwNDE0NDc=" ).get()
for {
response <- promise
} yield {
// here you can have as many lines of code as you want,
// only the result of the last line is yielded
val transformed = someTransformation(response.json)
Ok(transformed)
}
}
}
I took a look at the doc, and you could try:
Async {
WS.url(getAppProperty("helpdesk.host")).withHeaders(
"Accept" -> "application/json",
"Authorization" -> "Basic bi5sdWJ5YW5vdjoyMDEzMDcwNDE0NDc=" ).get().map{
response => Ok(response.json \\ "Tasks")
}
}

Get value from nested functions in Coffeescript

i want to get a result from another function (anonymous-function), but i don't know how because my result-function is a nested function. Maybe I have to use a callback somehow?
Here my code:
render: ->
console.log #getCommunities()
return this
getCommunities: ->
dpd.communities.get (result, err) ->
return console.log(err) if err
result
getCommunities() returns always 'undefined'.
Does anyone know how to solve this problem?
Thanks,
rojan
How about this?
render: ->
#getCommunities (err, result) ->
if err then throw err
console.log result
return this
getCommunities: (callback) ->
dpd.communities.get (result, err) ->
return console.log(err) if err
callback(null, result)
fyi, it's a best practice to pass err as the first arg to callbacks in node, assuming this is in fact node...just guessing by the err callback

$q defer and promises and how to use them to load the data for a controller before the view is rendered

This has been the most relevant thing I found: https://stackoverflow.com/a/11972028/110233
It seems to work fine when I only want to return one thing, but I'm unsure on how to return multiple things when the other things depend on the first thing.
Since that's kind of obtuse, here's a small example on what I'm currently doing:
window.EventRosterCtrl = ($scope, subevent) ->
$scope.subevent = subevent
EventRosterCtrl.resolve =
subevent: (SubEvent, $route) ->
deferred = $q.defer()
SubEvent.get {subevent_id: $route.current.pathParams.subevent_id}, (subevent) ->
deferred.resolve subevent
return deferred.promise
And here's an example of what I would want to do:
window.EventRosterCtrl = ($scope, subevent, addresses) ->
$scope.subevent = subevent
$scope.addresses = addresses
EventRosterCtrl.resolve =
subevent: (SubEvent, $route) ->
deferred = $q.defer()
SubEvent.get {subevent_id: $route.current.pathParams.subevent_id}, (subevent) ->
deferred.resolve subevent
return deferred.promise
addresses: (User) ->
deferred = $q.defer()
# how do you get subevent called first and how would you access it here?
for participant in subevent.participants
User.get {user_id: participant.user}, (user) ->
addresses[participant._id] = user.address
deferred.resolve addresses
return deferred.promise
you need to chain promisses using the .then()
var promise = firstOperation();
promise = promise.then(function(value) {
// do some more work
return value; // it can be another promise
});
return promise; // this one will be resolved when both steps are resolved
Ok, so a you can't control it that way, but a work around that comes to mind (when one resolution is dependent on another finishing first) is just put everything in one object and resolve that. What ended up working best for me was making a service, but to follow my original example:
window.EventRosterCtrl = ($scope, info) ->
$scope.subevent = info.subevent
$scope.addresses = info.addresses
EventRosterCtrl.resolve =
info: (SubEvent, User, $route, $q) ->
deferred = $q.defer()
resolvedInfo = {}
SubEvent.get {subevent_id: $route.current.pathParams.subevent_id}, (subevent) ->
resolvedInfo.subevent = subevent
for participant in subevent.participants
User.get {user_id: participant.user}, (user) ->
addresses[participant._id] = user.address
if addresses.length is subevent.participants.length
resolvedInfo.addresses = addresses
deferred.resolve resolvedInfo
return deferred.promise
The EventRosterCtrl is only initialized when the subevent is ready, so the following should work:
window.EventRosterCtrl = ($scope, subevent) ->
$scope.subevent = subevent
for participant in subevent.participants
User.get {user_id: participant.user}, (user) ->
$scope.addresses[participant._id] = user.address
EventRosterCtrl.resolve =
subevent: (SubEvent, $route) ->
deferred = $q.defer()
SubEvent.get {subevent_id: $route.current.pathParams.subevent_id}, (subevent) ->
deferred.resolve subevent
return deferred.promise