Adding organization repos to Travis-CI: Part I

It was a nice FridayPorras and I were at the BeBanjOffice and  we were working on adding organization support to Travis-CI.

First, we needed to find where the user repos were being fetch. Said and done, we found that travis is using the ServiceHookController to ask the server for repos (and to update them too, but don’t hurry, we’ll get there in part II). The controller was using the github_repositories method in the User model. Finally, we arrived to Travis::GithubApi, where all our questions found answers.

repositories_for_user just relied on Octokit#repositories for getting the repos, so we added all the code after the plus sign. You might think that this code lacks the proper security, is wrong and that it shouldn’t be there (and damn it, you’re right!) but there is no easy way to retrieve only the repos where the user has admin privileges (we wasted hours on this and a quick message to Github confirmed it).  Basically, it asks for all organizations for the given user (beware! You need to make your membership public, otherwise it won’t work) and then flat_map them to the repos in the given organization (whether the user can administrate them or not).

Later, the each block was added so we could know whether the repo belongs to the user or to an organization.

Stop! Hammertime!

We needed a way to let the user know that a repo actually belongs to an organization, so we added that info to the template (that {{#if content.organization_name}} is ours :D) and added some style so it could look this awesome:

Travis CI - Distributed build platform for the open source community

We had some tough time trying to get it vertically centered, but after realizing that Github has its tags the same way as we do, we stop trying to fix it. And they still look awesome.

More coming in part II.

Anidamiento de recursos con REST en Ruby on Rails ó cómo acceder de dos maneras distintas a un mismo controlador utilizando REST con Ruby On Rails.

Después de un título tan largo, lo primero es meter en situación. Tengo una web que tiene temas, en cada tema hay más temas y descargas. Estos se muestran con los controladores topics y downloads respectivamente. El problema venía puesto que los controladores se tenían que poder acceder de dos formas. En el caso de las descargas:

1.- /downloads
2.- /topics/:topic_id/downloads

En el caso de los temas:

1.- /topics
2.- /topics/:topic_id/topics

Aproximación: Crear un segundo controlador.

Empecemos por el tema de los temas y sus subtemas. En este caso utilizaremos un segundo controlador al que llamaremos subtopic. Para poder acceder de la forma buscada hay que sustituir en el archivo config/routes.rb la línea

map.resources :topics
por lo siguiente:

map.resources :topics do |topic|
topic.resources :subtopics, :path_prefix => "topics/:topic_id"
end

Después, se eliminan los métodos delete, edit, show y update, pues sólo se necesitan los métodos create, index y new. Además, lo más lógico sería implementar los métodos create, index y new de forma que podamos aprovechar las vistas del controlador topic. Aún así, tenemos repetición, lo que viola el principio DRY que rige a Rails. Por lo tanto, veamos cómo podemos conseguir el resultado obtenido de una forma más sencilla y elegante.

Fase final: las cosas bien hechas.

En este caso cogeremos como ejemplo las descargas de un tema. Para ello, al igual que en el ejemplo anterior, hay que modificar el routes.rb añadiéndole las siguientes líneas

map.resources :topics do |topic|
topic.resources :downloads, :path_prefix => "topics/:topic_id", :name_prefix => 'topic_'
end

map.resources :downloads

Hay que recalcar dos aspectos de estas rutas. En primer lugar, el :name_prefix. De esta forma, podremos diferenciar entre si hay que crear un path del tipo /downloads/1 o del tipo /topics/:topic_id/downloads simplemente precediendo el nombre de los métodos que construyen el path con topic_. Es decir, si utilizamos el método edit_download_path(download) obtenemos la ruta http://localhost:3000/downloads/2;edit. En cambio, al utilizar el método topic_edit_download_path(download.topic,download) (hay que recalcar que el :name_prefix va antes del nombre normal del método y que los parámetros se pasan según el orden en el que se encuentran en la ruta) obtenemos la ruta http://localhost:3000/topics/1/downloads/2;edit.
Espero que este artículo os haya sido tan interesante (aunque me exprese como el culo) como para mí fue el investigar cómo se hace esta monería con REST, Ruby y Ruby on Rails.