Welcome to the Treehouse Community
Want to collaborate on code errors? Have bugs you need feedback on? Looking for an extra set of eyes on your latest project? Get support with fellow developers, designers, and programmers of all backgrounds and skill levels here with the Treehouse Community! While you're at it, check out some resources Treehouse students have shared here.
Looking to learn something new?
Treehouse offers a seven day free trial for new students. Get access to thousands of hours of content and join thousands of Treehouse students and alumni in the community today.
Start your free trialHossam Khalifa
17,200 PointsAdding subjects to the command line app
I started implementing the feature of choosing the subject to get the points for.This was easy but my problem was in displaying the error message.The error message is displayed for every user.But I only want to log it out once.
Here is app.js
var profile = require("./profile.js")
var subj = process.argv[2]
var users = process.argv.slice(3);
users.forEach(function(currentValue){
profile.get(currentValue,subj)
})
Here is profile.js
//Print out message
var http = require("http");
var subjs;
var profile;
var printMessage = function(username,badgeCount,points,subj){
var message = username + " has " + badgeCount + " total badges and "+ points+ " points in "+subj;
if(points === undefined ){
console.error("The subject:("+subj+") does not exist.Please choose a valid subject.Subjects are case senitive.Here is a subject list :"+ subjs);
return false;
} else{
console.log(message);
}}
//Print out ERROR messages
function printError(error){
console.error(error.message);
}
function get(username,subj){
//connect to API URL(https://teamtreehouse.com/username.json)
var req = http.get("http://teamtreehouse.com/"+username+".json",function(response){
var body="";
//Read the data from the response
response.on('data', function (chunk) {
body += chunk;
});
//Parse the data
response.on('end',function(){
if(response.statusCode === 200){
try { profile = JSON.parse(body);
subjs = Object.keys(profile.points).splice(1);
//Print the data out
printMessage(username,profile.badges.length,profile.points[subj],subj);
} catch(error){
//Parsing Error
printError(error);
}
}else{
//StatusCode Error
printError({message:"There was an error getting the profile for "+ username +" ("+http.STATUS_CODES[response.statusCode]+")" +" please try again later"})
}
});
//conncetion Error
req.on("error",printError)
})
}
module.exports.get = get;
I know why it is displaying twice but I can not find any solution to displaying only once.
1 Answer
Sean T. Unwin
28,690 PointsGood idea, Hossam Khalifa .
One way to implement the display of the error message only once is explained below. The re-factored files, formatted and with comments, are pasted after the explanation.
This will be 2 step process - Step 1 will concentrate on handling the users array that was passed to start the app and Step 2 will focus on the subject that was passed to start the app.
-
Handling the users array
- Declare variables to handle unknown user(s) error
- Add a third parameter to
get()
function - Refactor parsing portion of
get()
- Create function to print unknown users error message
-
Handling the subject
- Declare variables to handle unknown subject error
- Refactor
printMessage()
- Refactor parsing portion of
get()
(again)
Step 1: Handling the users array
Declare variables to handle unknown user(s) error
- In
profile.js
aftervar profile;
and before the function declarations insert
// Used for status code error message output
// Array for users that were not found
var unknownUsers = [];
// Counter for users not returned
var counter = 0;
Add a third parameter to get()
function
- In
app.js
changeprofile.get(currentValue,subj)
by adding a third variable (users.length
) to the function call so it looks like:
profile.get(currentValue, subj, users.length)
- In
profile.js
change theget()
function declaration, in order to handle the third parameter, to:
// Add third parameter of numUsers
function get(username,subj, numUsers) {
Refactor parsing portion of get()
in profile.js
- Immediately following the line,
response.on('end',function() {
insert
// Increase the counter as we loop through the users array
counter ++;
- Within the
try
block, after theprintMessage()
function call, insert
// Print error message of unknown/ not found users if there are any
printUnknownUsersError(counter, numUsers);
- Within the
else
portion of the status codeif
statement replace theprintError()
function call with
// Add falsey username to array
unknownUsers.push(username);
// Print error message of unknown/ not found users if there are any
// We need to call this here in case all users were not found
printUnknownUsersError(counter, numUsers);
Create function to print unknown users error message
- In
profile.js
above theget()
function declaration add the following function:
function printUnknownUsersError(counter, numUsers) {
// At the last item of users and at least 1 unknown user esixts
if (counter === numUsers && unknownUsers.length > 0) {
var message = "\n"; // newline for spacing before this error message
message += 'There was an error getting the profile for ';
// Display the users in the order they were called initially and convert array to string
message += unknownUsers.reverse().join(', ');
// If statusCode is 200 (because at least one username was correctly returned)
// we make an assumption that if a username isn't found it's because it
// doesn't exist, i.e. Not Found error code message
// Similar to if no results were found and using http.STATUS_CODES[response.statusCode]
message += ' (Not Found). Please check the spelling or try again later';
//StatusCode Error
printError({ message: message });
// Not at end of users array so carry on
} else {
return;
}
}
Step 2: Handling the subject
Declare variables to handle unknown subject error
- In
profile.js
aftervar counter = 0;
and before the function declarations insert
// Trigger for exit
var time2go = false;
- Immediately following the line,
response.on('end',function() {
, but abovecounter ++;
, insert
// Trigger for valid subject
var isSubj = false;
Refactor printMessage()
- Our main goal here is to set
time2go = true
when an invalid subject is found so we can print the error message only once then exit the app - A secondary goal is to conform the usage of the
message
variable to sense of standards and readability, i.e.similar to how we utilized the variable of the same name inprintUnknownUsersError()
- Another secondary goal is to return the error message, which will be handled by the
catch
block (explained below), instead of printing to the console from within the function - Replace the current body of the
printMessage()
function with
var message = "";
// The subject given is invalid
if(points === undefined) {
// Print invalid subject message only once
time2go = true;
message += "\nThe subject:("; // Add a new line for readability
// Attach the subject that was provided
message += subj;
message += ") does not exist. ";
message += "Please choose a valid subject. Subjects are case senitive.";
message += "\nHere is a subject list: ";
// Attach the list of subjects and format so that a
// space follows the comma between valid subjects
// for better readability
message += subjs.join(', ');
// Send message to be output by error handler in catch block
return {message: message};
} else {
// Print user's profile info
message = username + " has " + badgeCount + " total badges and "+ points+ " points in "+subj;
console.log(message);
}
Refactor parsing portion of get()
(again)
+Within response.on('end',function() {
and after var isSubj = undefined;
, but before counter++;
insert
// Invalid subject so quit
if (time2go) {
return false;
}
- Within the
try
block, immediately aftersubjs = Object.keys(profile.points).splice(1);
, but before the closing bracket of thetry
block, replace the code with
// Check existing subjects to verify if subj is valid
for (var i = 0; i <= subjs.length; i++) {
// Valid subject
if (subjs[i] === subj) {
isSubj = true;
}
}
// Invalid subject
if (!isSubj) {
// Print the invalid subject message
// - Exit try block and send to catch block
throw printMessage("", "", undefined, subj);
} else {
//Print the data out
printMessage(username,profile.badges.length,profile.points[subj],subj);
// Print error message of unknown/ not found users if there are any
printUnknownUsersError(counter, numUsers);
}
Files
app.js
:
var profile = require("./profile.js");
// Create String of potential subject
var subj = process.argv[2];
// Create Array of potential users
var users = process.argv.slice(3);
// Loop through each user, get their info and output
users.forEach(function(currentValue) {
profile.get(currentValue,subj, users.length);
});
profile.js
:
/* Retrieve a user's profile info
* and
* Print out message(s)
*/
var http = require("http");
// A user's returned profile
var profile = {};
// Array for existing subjects
var subjs = [];
// Array for users not returned
var unknownUsers = [];
// Counter for looping through provided users array
var counter = 0;
// Trigger for exit
var time2go = false;
function printMessage(username,badgeCount,points,subj) {
var message = "";
// The subject given is invalid
if(points === undefined) {
// Print invalid subject message only once
time2go = true;
message += "\nThe subject:("; // Add a new line for readability
// Attach the subject that was provided
message += subj;
message += ") does not exist. ";
message += "Please choose a valid subject. Subjects are case senitive.";
message += "\nHere is a subject list: ";
// Attach the list of subjects and format so that a
// space follows the comma between valid subjects
// for better readability
message += subjs.join(', ');
// Send message to be output by error handler in catch block
return {message: message};
} else {
// Print user's profile info
message = username + " has " + badgeCount + " total badges and "+ points+ " points in "+subj;
console.log(message);
}
}
function printUnknownUsersError(counter, numUsers) {
// At the last item of users and at least 1 unknown user esixts
if (counter === numUsers && unknownUsers.length > 0) {
var message = "\n"; // newline for spacing before this message
message += 'There was an error getting the profile for ';
// Display the users that were not found and convert array to string
message += unknownUsers.join(', ');
// If statusCode is 200 (because at least one username was correctly returned)
// we make an assumption that if a username isn't found it's because it
// doesn't exist, i.e. Not Found error code message
// Similar to if no results were found and using http.STATUS_CODES[response.statusCode]
message += ' (Not Found). Please check the spelling or try again later';
//StatusCode Error
printError({ message: message });
} else {
// Not at end of users array so carry on
return;
}
}
//Print out Error messages
function printError(error) {
console.error(error.message);
}
function get(username,subj, numUsers) {
//connect to API URL(https://teamtreehouse.com/username.json)
var req = http.get("http://teamtreehouse.com/" + username + ".json",function(response) {
var body="";
//Read the data from the response
response.on('data', function (chunk) {
body += chunk;
});
//Parse the data
response.on('end',function() {
// Trigger for valid subject
var isSubj = false;
// Invalid subject so quit
if (time2go) {
return false;
}
// Increase the counter as we iterate through the users array
counter ++;
if (response.statusCode === 200) {
try {
// Store the returned profile
profile = JSON.parse(body);
// Store a list of current subjects
subjs = Object.keys(profile.points).splice(1);
// Check existing subjects to verify if subj is valid
for (var i = 0; i <= subjs.length; i++) {
// Valid subject
if (subjs[i] === subj) {
isSubj = true;
}
}
// Invalid subject
if (!isSubj) {
// Print the invalid subject message
// - Exit try block and send to catch block
throw printMessage("", "", undefined, subj);
} else {
//Print the data out
printMessage(username,profile.badges.length,profile.points[subj],subj);
// Print error message of unknown/ not found users if there are any
printUnknownUsersError(counter, numUsers);
}
} catch(error) {
//Parsing Error
printError(error);
}
} else {
// Add falsey username to array
unknownUsers.push(username);
// Print error message of unknown/ not found users if there are any
// We need to call this here in case all users were not found
printUnknownUsersError(counter, numUsers);
}
});
//Conncetion Error
req.on("error",printError);
});
}
// Expose method
module.exports.get = get;
Changelog:
Jan. 8, 2015: Added step 2 for handling of the bad subject error message to be output only once
Hossam Khalifa
17,200 PointsHossam Khalifa
17,200 Pointsdid you try this code?? because it dosen't work for me
Sean T. Unwin
28,690 PointsSean T. Unwin
28,690 PointsYes, I tried the code. I just tried again now by creating new files copied from the 'Files' section above. It works for me.
What type of error are you seeing?
note: The following does not affect my original post from functioning in any way. It is an enhancement only.
In the test I just ran I did find that the
unknownUsersError
would print even if there weren't any found so I added a check to only print that error message if unknown users were found.Within the
printUnknownUsersError
function, at approx. line 25 inprofile.js
, I changed theif
statement from:if (counter === numUsers) {
to:
if (counter === numUsers && unknownUsers.length > 0) {
Hossam Khalifa
17,200 PointsHossam Khalifa
17,200 PointsI copied the above code for profile.js and for app.js and I get this error message The subject:(subjject) does not exist.Please choose a valid subject.Subjects are case senitive.Here is a subject list :HTML,CSS,Design,JavaScript,Ruby,PHP,WordPress,iOS,Android,Development Tools,Busin ess,Python,Java printed three times
Sean T. Unwin
28,690 PointsSean T. Unwin
28,690 PointsWhat are you typing into the console to run
app.js
including the parameters?Hossam Khalifa
17,200 PointsHossam Khalifa
17,200 Pointsnode app.js iS hossamkhalifa mostafamabrouk chalkers
iS as a wrong subject
Sean T. Unwin
28,690 PointsSean T. Unwin
28,690 PointsAh, I see.
So it is working the way I intended to code it as it will only print out one error message if one or more students are not found.
I didn't test for a bad subject with multiple users. I was concentrating on the users array passed in as I thought that was what you wanted.
Let me see what I can come up with when I have the time.
Sean T. Unwin
28,690 PointsSean T. Unwin
28,690 PointsHossam Khalifa, I had some time to work on this today and I have updated my original post with a 'step 2' for dealing with an unknown subject's error message so that it will only be output one time.
Please, read through my answer above again to see my process of printing only one error message for an unknown subject. Futhermore, if the subject does exist, but a user does not then output an error message for an unknown user or users.
I hope I have fulfilled your intention. It sure was a fun experiment. If you have any further questions, feel free to ask. :-)
Hossam Khalifa
17,200 PointsHossam Khalifa
17,200 PointsThank you very much for your help,it was really fun for me also. :D. But my only question is how or wich portion of the code in the get function exited the loop in the app.js.I don't understand how to exit it using the get() function.
Sean T. Unwin
28,690 PointsSean T. Unwin
28,690 PointsThe exiting of the app on an unknown subject error is ultimately done when the global variable
time2go
istrue
. Near the top of theresponse.on('end')
function there is anif
statement that checks for this.If
time2go
istrue
then the statementreturn false
exits theresponse.on('end')
function and since this is effectively the end of theget()
function (in other words that part is the last procedure of the function) there is nothing left for the app to do so it quits.