Mirmo Dynamics

Si tu kiffes pas reunoi, t'écoutes pas et puis c'est tout.

To content | To menu | To search

Wednesday 1 July 2009

Help Symfony: fix one bug per day and win some gifts !

Today is the first day of the "1 day / 1 ticket" Symfony event.

Starting from today, each ticket you close (or help closing) from this list by either submitting a patch, submitting unit tests, etc will earn you points. At the end of the month, gifts are attributed to people with the most points.

This is a fun and exciting way to help your (hopefuly) favorite opensource project ;)

For more details on rules, please see the official page.

Monday 15 June 2009

subversion, tags and externals

It's been brought to my attention today (by my fellow developper hartym) that there is a HUGE problem with tags in subversion.

The problem is very simple, when you create a tag for your project, and that your project uses svn:externals (quite a common situation), externals in the tags ARE NOT FROZEN. Which means that, if you checkout your tag at a T time, and an external gets modified at T+x, a checkout at T+x will not be the same than the one at T.

Wow.

I'm going to seriously consider switching to git.

Sunday 14 June 2009

Debriefing Symfony Live 2009

Bon ben voilà, vendredi dernier était le grand jour pour moi: ma première conférence. Que dire, sinon que les conditions n'auraient pas pu etre pires ? J'avais déjà un peu les chocopétoches, parceque perso parler en public, j'ai un peu du mal, je n'étais pas non plus entièrement satisfait des slides, mais j'imagine que c'est normal, et puis je parlais devant pas mal de personnes qui m'impressionnent un peu. Bref, autant dire que vers 8h30 (ma conf étant a 9h), je faisais pas trop le fier.

8h30, c'est l'heure à laquelle mon portable a sonné pour m'annoncer que mon co-conférencier serait à la bourre. Ok super, c'est lui qui a la dernière version des slides, c'est lui qui fait la première partie, wow, trop bien. Ni une ni deux, j'en parle à mon boss, qui décide de swapper sa conf (10h) avec la mienne. Le coeur léger (malgrès le fait que parler après mon boss, ça me foutait un peu les boules (il parle très bien)), je m'en vais décharger ma haine sur twitter.

8h58, mon boss vient me voir: écoute, les gens viennent voir symfony 2, là y'a pas tout le monde, si on leur dit a 10h quand ils vont arriver que ben désolé symfony 2 c'était à 9h finalement, ça le fait pas, donc tu vas faire ta conf. Décomposition. Mais je vais remplacer Hugo dans ta conf.

Ok. Très bien. Je vais brancher mon pc sur le rétro, obligé de rebooter sous windows pour avoir la sortie VGA qui marche (on ne ricane pas dans le fond, et oui ça doit surement marcher sous linux, mais pas trop le temps de chercher là), je ressors le vieux PDF des slides de y'a un mois, et zou, on part en petite impro. Fabien (mon boss) commence à parler, c'est chouette, ça se passe bien, jusqu'a environ une vingtaine de slide, où on se rend compte que, ben, y'a plus de slides. Gros gros moment de solitude. A partir de là, c'est freestyle complet.A mon tour de parler, la gorge désséchée, une petite tremblote de la main qui tient le micro, mais ça va, j'arrive quand meme a débiter mon petit laius, à quelques oublis près.

On finit sur la petite séance de questions, qui s'est plutot bien passé (merci à la personne qui a parlé de code coverage, c'est typiquement le truc qui était dans les slides et que j'ai oublié).

Bref, au final, je ne suis pas mort, personne ne m'as hué à la sortie, et ça me fait une expérience vraiment enrichissante du blablatage en public.

Quelques leçons que j'ai tiré de tout ça:

  • toujours, TOUJOURS, etre pret à etre laché seul dans la nature, meme si on a une confiance aveugle en son co-conférencier, on ne sait jamais ce qui peut arriver
  • NE PAS se mettre devant le rétroprojecteur, ça illumine
  • parler en public, quand on maitrise assez son sujet, c'est pas si sorcier que ça

Et surtout, ce que je retiendrais le plus: je pense que ce qui me faisait le plus peur avant la conf, c'était le public. Quand on parle en public, on peut penser avoir à défendre son steack, et le défendre contre qui ? le public. Il n'en est rien. On est meme au contraire en position de force par rapport au public, parcequ'on possède une connaissance qu'il convoite (sinon il ne serait pas là). Donc voilà, le public, ce n'est pas l'ennemi, c'est un ami qui nous veut du bien.

Dernière chose: ça me désole vraiment que la conf ce soit déroulée comme ça, et pour m'excuser, je vais publier une petite série d'articles sur les tests unitaires et fonctionnels, avec notament les exemples présents dans les slides qui n'ont pas pu etre montrés. Stay tuned.

PS: Les conférences étaient filmées, elles seront mises en lignes fin de semaine prochaine sur http://www.phptv.fr/

Quelques photos en attendant:

Wednesday 6 May 2009

Speaking at Symfony Live 09

For those not following me on twitter (shame on you), I will be speaking about unit and functional testing in Symfony (with a tiny bit of continuous integration too) at Symfony Live 2009. I will not be alone since my workmate Hugo Hamon will be under the spotlights with me. Also, the talk will be given in french.

Hope to see you there :)

Thursday 2 April 2009

SQLSTATE[HY000]: General error: 1005 Can't create table '*' (errno: 150)

You may or may have not already stumbled upon this rather obscure error message (no, I'm not starting a serie on obscures error messages). Well the first thing to think when you encounter this is your foreign keys. This message generally denotes a failure in foreign keys creation, for example when the two columns you are trying to link are not of the exact same type (that is, you can't link an integer(4) to an integer(11)).

Now what if you are absolutely certain that your two columns are of the same type ? I just had the problem with this piece of doctrine schema (simplified for example purpose):

Media:
  columns:
    image:           { type: string(255), notnull: true, unique: true }

ExplorationMission:
  columns:
    probe_image:         { type: integer, notnull: true }
  relations:
    ProbeImage:          { class: Media, local: probe_image, foreign: id, onDelete: 'SET NULL' }

Can you spot the problem ?

It's rather obvious when you know it, but I spent rather some time cursing mysql in vain. The point is that there is a major logic error in this schema. it declares the probe_image as NOT NULL while it gently asks the FK to set it null on delete. See ?

Hope it saves some frustration :-)

Tuesday 31 March 2009

Ajax query failing unexpectedly ?

I was gently doing some ajax black voodoo when I came upon this obscure-ish error in my firebug console:

Error: uncaught exception: [Exception... "Component returned failure code: 0x805e000a [nsIXMLHttpRequest.open]"  nsresult: 0x805e000a (<unknown>)"

What the fuck I though. And then after some (useless) firefox rebooting, I decided to ask the all-mighty Google.

No shit, he said, I know what you are talking about, and thou shall find more informations at this place, and to point me to this thread, where you can read the following:

AdBlock Plus was killing it HAH!

And then I disabled AdBlock Plus, and exactly as the writings told, it worked.

Monday 30 March 2009

Customizing actions in symfony's admin generator

A quick tip about symfony's admin generator: if you want to have a custom action, with the following generator.yml bit for example (actions prefixed with an underscore are builtin admin-gen actions):

form:
  actions:
    _delete:  ~
    _cancel:  ~
    _save:    ~
    _save_and_add: ~
    custom: ~

You can totally customize the link generated for this action (to add javascript for example) using the generator helper in your module's lib/ directory:

public function linkToCustom($object, $params)
{
  return '<a href="#" onclick="console.log(\'woohoo\'); return false;">log woohoo</a>';
}

You get passed the current object as an argument, and whatever parameters you passed in the generator.yml:

    custom: { label: 'WOOHOO' }
public function linkToCustom($object, $params)
{
  return '<a href="'.url_for($this->getUrlForAction('custom')).'" onclick="console.log(\'woohoo\'); return false;">'.__($params['label']).'</a>';
}

Also, as you can see that standard helpers are available too.

Friday 20 March 2009

Symfony Live les 11 et 12 Juin 2009

Sensio Labs organise les 11 et 12 juin l'événement Symfony Live. Voilà tout est presque dit, cliquouillez le lien pour plus d'infos.

Symfony Live Flyer

Wednesday 18 March 2009

Automatically cd to a symfony project's root in vim

I made a little vimscript to automatically cd to the project root of a symfony project. It makes it easier for me to use the ctags :-)

"-------------------------------------------------------------------------------
"  Description: Finds and cd to the symfony root of the project
"    Copyright: Copyright (C) 2009 Geoffrey Bachelet
"   Maintainer: Geoffrey Bachelet
"      Version: 1.0
"-------------------------------------------------------------------------------

if exists('find_symfony_root_loaded')
  finish
endif

let find_symfony_root_loaded = 1

if ! exists('find_symfony_root_symfony_executable')
  let find_symfony_root_symfony_executable = 'symfony'
endif

" root detection when opening a new vim seems to work
" only if these two events are bound. not sure why.
autocmd BufWinEnter,BufRead * call FindSymfonyRoot()

function FindSymfonyRoot()
  let l:cwd = GetAbsoluteDirname(@%)
  let l:symfony_root = findfile(g:find_symfony_root_symfony_executable, l:cwd.';')
  let l:symfony_root = GetAbsoluteDirname(l:symfony_root)
  if strlen(l:symfony_root) != 0
    execute 'cd '.l:symfony_root
  endif
endfunction

function GetAbsoluteDirname(path)
  let l:path = a:path
  " gets the dirname
  if !isdirectory(l:path)
    let l:path = strpart(l:path, 0, strridx(l:path, '/'))
  endif

  " makes it absolute
  if match(l:path, '/') != 0
    let l:path = getcwd().'/'.l:path
  endif

  return l:path
endfunction

note for later: add vimscript support to geshi

Or see the highlighted version on gist.

Tuesday 17 March 2009

Rewrite rule to add missing www

  RewriteEngine on
  RewriteCond %{SERVER_NAME} !^www\..*$
  RewriteRule ^/(.*)$ http://www.%{SERVER_NAME}/$1 [L,QSA,R=302]

From the manual, using %1 as a back reference from the RewriteCond in the RewriteRule should work, but it didn't on my installation. Weird.

Thursday 12 March 2009

How to render a partial in an action with symfony 1.1+

public function executeFoobar()
{
  return $this->renderPartial('my_partial');
}

This is an attempt to take over the first place on google for the query how to render partial action symfony.

Tuesday 10 March 2009

Using the timestamp data type in doctrine fixtures

Please note that to be valid, you have to enclose the value within single quotes. For example, say you have the following (totally useless) schema:

Foobar:
  columns:
    published_at: { type: timestamp }

And you want to create fixtures for this table. You might go this way (please also note that symfony allows php in fixtures files):

Foobar:
  foobar_1:
    published_at: <?php echo time(); ?>

Which won't work, since the Doctrine_Validator_Timestamp expects a date in the Y-m-d H:i:s format. So maybe you'll try this one:

    published_at: <?php echo date('Y-m-d H:i:s'); ?>

Which still doesn't work, since the value gets converted to an unix timestamp.

The right way is (note the enclosing single quotes):

   published_at: '<?php echo date('Y-m-d H:i:s'); ?>'

Yay o//

This behavior is explained in this ticket might help.

Sunday 8 March 2009

having fun with vim, an event tracker

The idea of this script came by realizing that the main reason I (almost) never fill my timesheet, is because I just forget it. How easier could it be if my editor (namely vim) could fill it for me ? Or at least, help me fill it. This script is meant to help this process. It will hook to every configured event (in the code below, BufRead and BufNewFile) and add a log line to the log file. Easy heh ? Of course, the script it self won't be of much use, I'll have to add some more events to track as well as writting some kind of frontend for it, with reporting & all.

if exists('trackloaded')
  finish
endif

" list of autocmd events:
" http://www.vim.org/htmldoc/autocmd.html#autocmd-events
let trackevents = ['BufRead', 'BufNewFile', 'BufEnter', 'BufWrite', 'BufLeave']
let trackfile   = '/tmp/timetrack.dat'
let trackloaded = 1

for event in trackevents
  execute 'autocmd '.event.' * call Track("'.event.'")'
endfor

unlet event

function Track(event)
  let l:filename = @%
  if l:filename != g:trackfile
    if match(l:filename, '/') != 0
      let l:filename = getcwd().'/'.l:filename
    endif
    silent execute '!echo `date +\%s` '.a:event.' '.l:filename.' >> '.g:trackfile
  endif
endfunction

You might note from this snippet that I'm not very comfortable with vimscripting :-) If you have a better way of doing this (I'm thinking mostly about the file writting here), don't hesitate to tell me.

Wednesday 25 February 2009

symfony api opensearch plugin

In case you didn't notice, as of today (well, yesterday really) the symfony's api documentation features an opensearch plugin with autocompletion, that you can use straight from the comfort of your favorite browser (given that it supports the opensearch standard, which is the case for at least firefox and IE, although you won't get autocomplete in this last case).

More informations on the symfony blog.

Monday 23 February 2009

symfony: use your own View class

The view class name in symfony is determined per-module, it means that you need to use a module-level configuration setting via the module.yml configuration file. Say you want to use the myView class to handle your view in the default module of your frontend application, create the file sf_root_dir/apps/frontend/config/module.yml and put the following in it:

default:
  view_class:  my

Symfony will add the View suffix for you. Of course, you have to take care of making this class available to the framework. The sf_root_dir/apps/frontend/lib/myView.class.php file would be a good place for this.

As often with sfConfig, you could have put the module.yml config file in sf_root_dir/config/ to make it global to your whole project.

PS: extending the sfPHPView class could be a good idea to get you started too ;)

Thursday 29 January 2009

Replacing short tags with proper PHP tags

This is a little script I made to get rid of those damned short tags.

<?php
 
while ($file = trim(fgets(STDIN)))
{
  $content = file_get_contents($file);
 
  $search  = array('/<\?=/', '/<\?(?!php|xml)/');
  $replace = array('<?php echo ', '<?php ');
 
  if ($content != ($new_content = preg_replace($search, $replace, $content)))
  {
    file_put_contents($file, $new_content);
  }
}

Just put this in a file, short_tags.php for example, and run something like:

$ find . -name "*.php" | php ./short_tags.php

I would have done it with sed, but it doesn't seem to support PCRE, and I don't know how to do negative lookahead (the (?!php|xml) thingy) with POSIX based regexp (if it's even supported) :/

UPDATE: Actually, POSIX Regexps DO support negative lookahead (as well as positive lookahead and lookbehind) with the same syntax as PCRE regexps. But grep doesn't use POSIX regexps, it uses things called BRE and ERE that I've never heard of before. Too bad.

Friday 23 January 2009

Good morning, jour #3

Bon ben ce matin je me suis réveillé avant le début de la période de réveil de mon Axbo, du coup je me suis levé, puisque je commence a prendre cette habitude. Par contre j'ai remarqué que l'Axbo semble perdre du temps assez rapidement, il indiquait 7h ce matin quand mon iphone indiquait 7h30. A voir donc.

Sinon, voilà mes graphes pour les deux premiers jours:

2009-01-20.png

2009-01-21.png

En gros, plus les barres sont hautes, plus je bouge. On voit nettement que la première nuit (le 20), je me suis souvent réveillé, et sur la deuxième nuit on voit également très bien le moment où je me suis relevé dans la nuit. Passionnant hein.

Thursday 22 January 2009

Multiple domains for one symfony project, the basics, config file version

When I published my post about using a single symfony application to serve multiple domains using a database to store the site-specific data, there was some people to complain about the extra query you get on each request. Right. Using a (cached) config file instead is not that hard as long as you use it only to match a domain name against a numeric id. Storing more site-specific data is a bit more problematic, but we will solve this in a later post.

And because I'm lazy, I won't re-write the entire article, just highlight the differences between the two methods, so you might as well read the database version if you did not already.

Now, first thing, the config file. We will create a simple project-wide config file: /config/app.yml:

all:
  paris.carshop: 1
  auckland.carshop: 2

Then we detect the site and add a generic sfConfig entry for it. This is done, as before, in /config/ProjectConfiguration.class.php. We will only change the detectSite() method to take advantage of the cached config information:

public function detectSite(sfEvent $event)
  {
    sfConfig::add(array('site_id' => sfConfig::get('app_'.$_SERVER['HTTP_HOST'])));
  }

Easy heh ?

Now we just have to adapt the model classes to use this config entry. The CarTable::createQuery() method will look like:

/**
 * Creates a query, adding the site criteria automatically
 *
 * @return Doctrine_Query
 * @see Doctrine_Table::createQuery()
 */
 
public function createQuery($alias = '')
{
  $query = parent::createQuery($alias);
  $query->where('site_id = ?', sfConfig::get('site_id'));
  
  return $query;
}

The Car::save() method:

/**
 * Automatically populates the site_id field if necessary
 * 
 * @see sfDoctrineRecord::save()
 */
 
public function save(Doctrine_Connection $conn = null)
{
  if (empty($this->site_id))
  {
    $this->setSiteId(sfConfig::get('site_id'));
  }
 
  return parent::save();
}

And you're all set ! For the record, here are the other modified files from the last post:

config/doctrine/schema.yml:

Car:
  columns:
    site_id:      { type: integer }
    name:         { type: string(255) }
    description:  { type: clob }
    image:        { type: string(255) }

data/fixtures/ now contains only the Car fixtures:

Car:
  car_1:
    site_id: 1
    name: Peugeot 307
    description: Lorem ipsum dolor sit amet, consectetur adipiscing elit.
  car_2:
    site_id: 1
    name: Renault Laguna
    description: Maecenas tortor nunc, aliquam et, ultrices id, ornare consectetur, mauris.
  car_3:
    site_id: 2
    name: Subaru Impreza
    description: Ut accumsan diam et orci. Sed sit amet neque ac diam rutrum iaculis.

Thursday 22 January 2009

Good morning, jour #2

Deuxième jour. Ce matin il m'a réveillé à 7h pétante, c'est à dire la limite que je lui avais fixé. En gros il n'a pas trouvé de bon moment entre 6h30 et 7h. C'est peut-être du au fait que je me suis levé cette nuit pour aller boire, ça a du le perturber un peu. Je vérifierai ça ce soir sur les graphs.

Parlons des graphs justement. J'ai installé les logiciels fournis sur le site web, dont celui qui permet d'avoir des graphs d'analyse des mouvements pendant le sommeil. Soyons clair: je n'ai rien compris aux graphs. Tout s'installe très facilement, l'axbo est immédiatement reconnu, le logiciel est assez simple d'utilisation, mais juste les graphs, je n'y comprends rien. Bon j'ai pas regardé longtemps non plus, mais j'espère trouver quelques explications quelque part sur internet.

Du coup j'ai également uploadé le pack de sons disponible sur le site, mais je n'ai pas l'impression qu'on puisse uploader nos propres sons (dommage).

Dernière chose, la mise à jour du firmware, encore une fois rien à dire. On installe un petit logiciel (encore) qui va s'occuper de vérifier si une nouvelle version est à jour, et le cas échéant de mettre l'Axbo à jour.

Wednesday 21 January 2009

Good morning, jour #1

Aujourd'hui était donc le jour de mon premier réveil avec mon Axbo. Comme j'ai très mal dormi cette nuit (téléphone à une heure du mat, réveil toutes les heures), je ne peux pas trop encore juger de la qualité de mon réveil, on retiendra surtout que:

  • Le bracelet-éponge ne m'a absolument pas gêné pour dormir
  • L'heure est un peu difficile à lire de loin (pas assez de contraste)
  • Je me suis levé quand mon réveil a sonné (7h54, réglé pour 8h) sans trop de mal
  • Je n'ai pas osé me lever cette nuit pour aller boire de peur d'affoler le capteur du réveil

En plus de ces considérations, suite à une discussion hier sur IRC, et pour faire l'équilibre avec mon billet élogieux d'hier, signalons quand même que l'Axbo ne dispose pas d'une fonction snooze (justifié par le fait que quand il sonne, il faut se lever parce que c'est la Bonne Heure (c)). On a aussi déploré l'absence de synchronisation sur une horloge atomique, qui est effectivement nice to have, mais pas bloquant non plus. Dans un registre plus grave (pas la fin du monde non plus), l'alarme n'est pas réglable par jour. C'est à dire qu'il sonne tous les jours à la même heure (enfin dans la même fenêtre de temps quoi), donc pour le week-end il faut penser à désactiver l'alarme (il y a un raccourci pour ça je crois, et ce sera peut-être ajouté dans une mise à jour du firmware, allez savoir).

- page 1 of 38