2
\$\begingroup\$

I made a simple app that fetches the favourite programming language of a Github user, by simply inserting their username. The full code is uploaded on Github, please feel free to fork it from https://github.com/elmargasimov/lovelanguage.

The stack:

  • Yo Backbone generator from Yeoman
  • Require.js
  • Underscore
  • Sass

Background to the code:

  1. I have declared top level controllers that I initialise from my App router.
  2. Inside my controllers I initialise the views and models.
  3. I have two models, one which is the User model and the other one is a sub model of the users starred collection.
  4. I also have two views, one top level view that fetches the user info and that acts as the glue between the User view and model.

Router (app.js)

var AppRouter = Backbone.Router.extend({
    routes: {
        '/*' : 'index',
        '*about' : 'about'
    },
    initialize: function() {
        // Declare top level app controllers
        this.controllers = {};
    },
    index: function() {
        // Create new fetchUserController instance
        this.controllers.fetchUserController = new FetchUserController();
    },
    about: function() {
        this.controllers.aboutController = new AboutController();
    },
    start: function () {
        Backbone.history.start();
        console.log("Launch App");
    }

});

UserModel (user.js)

var UserModel = Backbone.Model.extend({
    defaults: {
        "avatar_url": "",
        "login": "",
        "name": "",
        "id": null,
        "html_url": "",
        "favourite_language": "",
    },
    fetchStarred: function () {
        var _this = this;
        // Create new starred model instance
        this.starred = new StarredModel();
        // Set the root url to {this.urlRoot}/starred
        this.starred.urlRoot = this.urlRoot + "/starred";
        // Fetch model attributes
        this.starred.fetch({
            success: function (model, response, options) {
                // Create a new langueages array to store all the languages of the starred repo's
                var languages = [];
                // Loop over the response
                _.each(response, function (index) {
                    // Only grab the language key from the list of attributes
                    var language = index.language
                    // For each language retrieved push to 'languages' array
                    languages.push(language);
                });
                // Set the languages attribute of the starred model to the languages array
                _this.starred.set('languages', languages);
                // Compute the mode language of the user
                _this.starred.mode(languages);
                // Make a reference to the mode language
                var modeLanguage = _this.starred.attributes.modeLanguage;
                // Set the user's favourite language to the mode language
                _this.set('favourite_language', modeLanguage);
            }
        });
    }
});

StarredModel (starred.js)

var StarredModel = Backbone.Model.extend({
    defaults: {
        languages: [],
        modeLanguage: "",
        languageMap: {}
    },
    mode: function(array) {
        var _this = this;

        // If the languages array is empty, return false
        if(array.length === 0) {
            console.log("Cannot compute mode: Empty array");
            return false;
        }

        var languageMap = {},
            modeLanguage,
            maxCount = 1;

        _.each(array, function(index) {
            // Start at the index and loop for each language
            var language = index;
            // If language does not yet exist in the languageMap
            if(typeof languageMap[language] === "undefined") {
                // Set language key and value equal to 1
                languageMap[language] = 1;
            } else {
                // Else add 1 to the count
                languageMap[language] += 1;
            }
            // If language value is greater than 1
            if(languageMap[language] > maxCount) {
                // Set modeLanguage equal to the key of the current language
                modeLanguage = language;
                // Set new highest count to value of the current language
                maxCount = languageMap[language];
            }
            // Save the languageMap and modeLanguage as an attribute of this model
            _this.set("languageMap", languageMap);
            _this.set("modeLanguage", modeLanguage);
        });

        // Return the mode language
        return modeLanguage;
    }
});

FetchUserModel (fetchuser.js)

var FetchuserView = Backbone.View.extend({
    el: $('form.ll-fetch-user-form'),
    events: {
        submit: 'fetch'
    },
    template: JST['app/scripts/templates/fetchuser.ejs'],
    fetch: function (e) {
        e.preventDefault();
        var _this = this;
        // Hook up to username input field and remove all empty white space
        var usernameInput = this.$el.find('#ll-github-username-input').val().replace(/\s/g, '');
        // Set urlRoot
        this.model.urlRoot = "https://api.github.com/users/" + usernameInput;
        // Fetch model attributes
        this.model.fetch({
            success: function (model, response, options) {
                _this.userInfo();
            }
        });
    },
    userInfo: function () {
        // Create new user view and pass in this model instance
        var userInfo = new UserView({model: this.model});
        // Render the view and insert top level element into '.ll-content'
        $('section.ll-content').html(userInfo.render().el);
        // Unbind this view, so we don't end up with zombie views
        this.unbind();
        // Remove this view altogeter
        this.remove();
    }
});

UserView (userview.js)

var UserView = Backbone.View.extend({
    tagName: 'section',
    className: 'll-user-info',
    template: JST['app/scripts/templates/user.ejs'],
    initialize: function () {
        this.listenTo(this.model, 'change', this.render);
        this.model.fetchStarred();
    },
    render: function () {
        this.$el.html(this.template(this.model.toJSON()));
        return this;
    }
});
\$\endgroup\$
1
  • \$\begingroup\$ Wouldn't this.$el be undefined? Shouldn't it be this.el? \$\endgroup\$
    – Daniel
    Nov 19, 2013 at 18:44

1 Answer 1

4
\$\begingroup\$

Can't say much about the javascript in general as I don't use it but:

Please do not comment every line of code. This means you write your program twice: once in the comments and once in code. They are bound to drift apart and get out of sync and all of a sudden you have code which does the opposite of what the comment says and then you are in trouble. Bad comments are worse than no comments.

As Jeff Atwood pointed out quite nicely: Code tells you how and comments tell you why.

\$\endgroup\$
3
  • \$\begingroup\$ Hi @ChrisWue, I have a different view. While of course it is bad practice to comment on every line of code. As you pointed out so well, we should still make sure the reader knows WHY we wrote what we wrote. I have worked in places where due to the absence of comments, we had to throw away large chunks of code, since we didn't understand what it did or how it was related to its current application. My rule of thumb is, if it is simple and generically done, then don't comment. But if it is new and unique, then comment. \$\endgroup\$
    – Elmar
    Nov 19, 2013 at 10:31
  • 1
    \$\begingroup\$ @Elmar If the code makes sense, then it doesn't need a comment. When we are first learning a language or methodology what we think is new and unique is usually either 1. simple and generic or 2. poorly executed. In either case, the comments are not useful. For option 1 they are redundant. For option 2 they are probably incorrect. Your rule of thumb should be to write code that can be understood by anyone qualified to maintain that code. If looking up a command immediately makes it clear what it does, then you don't need a comment. Adding comments is like adding noise. \$\endgroup\$
    – Daniel
    Nov 19, 2013 at 18:40
  • 1
    \$\begingroup\$ @Elmar: First your comments don't comment why you wrote what you wrote. It just reiterates what each line of code is doing which you can see by looking at the code. And second, if you need to comment every line of code with the reason why you wrote it you have a problem anyway. I'm all in favor of good comments but they should be used appropriately. \$\endgroup\$
    – ChrisWue
    Nov 20, 2013 at 7:46

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.