holy moly

pangratz prattles

GitHub Dashboard #3

Goal of this iteration: Implement list of showing watched repositories for a defined username.

The goal of this iteration is to implement the feature of showing the watched repositories for a given username. Before I started I removed some leftovers from ember-skeleton template; namely the store, state manager and states as well as the unused main_page template. Now all unused files are removed and the project layout is finally ready.

First of all I renamed the namespace from App to Dashboard and updated the corresponding test in app/tests/core_test.js.

Now it’s time to start to implement first feature: show watched repositories for a given username. To communicate with the GitHub API I implemented Dashboard.GitHubDataSource. The watchedRepositories method takes an username, a target object and the name of the callback method which shall be invoked with the data gathered from the API. The ajax call has the dataType jsonp so the response includes the result of the API call as well as the HTTP header information. See documentation at developer.github.com. Inside _updateLimits the rate limiting information is stored on the data source itself, so we can track when we run out of allowed requests for the API. A note about invoking the callback: only the relevant data property of the JSONP response object is passed (line 19). The complete Dashboard.GitHubDataSource is implemented as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
require('dashboard/core');

Dashboard.GitHubDataSource = Ember.Object.extend({
  PREFIX: 'https://api.github.com',

  ajax: function(url, target, callback) {
    Ember.$.ajax({
      url: this.PREFIX + url,
      dataType: 'jsonp',
      context: this,
      success: function(data) {
        this._ajaxSuccess(target, callback, data);
      }
    });
  },

  _ajaxSuccess: function(target, callback, response) {
    this._updateLimits(response);
    Ember.tryInvoke(target, callback, [response.data]);
  },

  _updateLimits: function(response) {
    if (response.meta) {
      this.set('remaining', response.meta['X-RateLimit-Remaining']);
      this.set('limit', response.meta['X-RateLimit-Limit']);
    }
  },

  watchedRepositories: function(username, target, callback) {
    this.ajax('/users/%@/watched'.fmt(username), target, callback);
  }
});

Now that there is a data source for querying the API, I created a controller Dashboard.RepositoriesController which holds all repositories which shall be shown in the view. It has a method loadWatchedRepositories which asks the data source to load the watched repositories for the specific username and then adds them via the addObjects methods to the content property of the array. The base class Ember.ArrayController allows to sort the content by specific property names on the items. The repositories shall be sorted by full_name, which is the username and the name of the repository joined by a /. This property needs to be defined in the sortProperties array:

1
2
3
4
5
6
7
8
9
require('dashboard/core');

Dashboard.RepositoriesController = Ember.ArrayController.extend({
  // 'full_name'.w() is a short form for ['full_name']
  sortProperties: 'full_name'.w(),
  loadWatchedRepositories: function(username) {
    this.get('dataSource').watchedRepositories(username, this, 'addObjects');
  }
});

Next step is to create a basic template for the repositories: Just iterate over the controller of the template’s view and show each full name of the repository, add a link to the repository and show the description.

lib/templates/repositories.handlebars (repositories.handlebars) download
1
2
3
4
5
6
7
8
<ul>
{{#each repository in controller}}
    <li>
        <a {{bindAttr href="repository.html_url"}} >{{repository.full_name}}</a>
        {{repository.description}}
    </li>
{{/each}}
</ul>

The final step is to assemble everything together. That’s what the app/main.js is for. First a GitHubDataSource and a RepositoriesController is instantiated. The data source is set on the controller. Afterwards the method loadWatchedRepositories is invoked to load all watched repositories. Finally, an Ember.View with the repositories template is created, the controller is set and the view is added to the document via append():

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
require('dashboard/core');
require('dashboard/github_data_source');
require('dashboard/controller');

Dashboard.gitHubDataSource = Dashboard.GitHubDataSource.create();
Dashboard.set('repositoriesController', Dashboard.RepositoriesController.create({
  content: [],
  dataSourceBinding: 'Dashboard.gitHubDataSource'
}));
Dashboard.get('repositoriesController').loadWatchedRepositories('pangratz');

Ember.View.create({
  templateName: 'repositories',
  controllerBinding: 'Dashboard.repositoriesController'
}).append();

Roundup

In this iteration I’ve created a data source which communicates with the GitHub API and passes fetched results to a callback. The callback is in this case a controller which holds all repositories, which shall be shown. A template iterates over the controller’s items (repositories) and shows some basic information. The result of the changes in this post are available at tag v0.0.3 ((changes)).

Currently only the first 30 watched repositories are fetched from the API. Pagination as described here will be the topic of an upcoming iteration. Also, it’s time to implement a Router which will coordinate the creation of views and controllers.

Comments