Mixing HTTP and WebSocket routes in a Koa-based application

I’ve started to use the koa-websocket package. And it took me some time to figure out how to mix HTTP and WebSocket routes in a single Koa-based application. I’m not sure if this solution is obvious, but I’m sharing it anyway.

First of all, we’ll need two separate routers for regular HTTP and WebSocket routes:

// Creating a Koa app instance.
const app = require('koa')();
// "Websockifying" the application.
const socket = (require('koa-websocket'))(app);
// Loading router package
const router = require('koa-router');

// Here they are, our 2 routers
const http = router();
const ws = router();

Then, we can write our routes, plugging them to the specific router:

http.get('/', function *(next) {
    this.status = 200;
    this.body = 'Hello!';
});

ws.get('/socket', function *(next) {
    this.websocket.send('Hey!');
    this.websocket.on('message', function (message) {
        console.log(message);
    });
});

Finally, let’s make the app use the routers we created:

app.use(http.routes()).use(http.allowedMethods());
app.ws.use(ws.routes()).use(ws.allowedMethods());

Notice that the second router was added to app.ws instead of app directly.

And… That’s it.

Photo credit: Aron Van de Pol

SplFileObject is faster than fopen/fgets

I had some memory issues recently when reading a file line-by-line with PHP. So I found that SplFileObject is faster and uses less memory than fopen followed by a loop with fgets.

Here is a simple before & after code:

Before

$handle = fopen($path, 'r');
if ($handle) {
    while (($buffer = fgets($handle, 1024)) !== false) {
        // Do something with $buffer ...
    }
    if (!feof($handle)) {
        throw new Exception('Error: unexpected fgets() fail');
    }
    fclose($handle);
}

After

$file = new SplFileObject($path);
while (!$file->eof()) {
    $buffer = $file->current();
    // Do something with $buffer ...
    $file->next();
}

UPDATE
2015/09/08 09:13AM

Maybe you notice an incompatible result when comparing SplFileObject against fopen followed by fgets. So I recommend you to make some tests on your own. Distinctions in server setup and PHP version may lead to different results, for instance.

Simple Gallery now as a jQuery plugin

One year ago I wrote a very simple image gallery script, based on jQuery. Now I updated it and completely rewrote it as a jQuery plugin. The code is available on GitHub: https://github.com/straube/simple-gallery

Photo credit: Jessica Ruscello

Regras de ouro para o trabalho remoto

Enquanto escrevo esse post, calculo que fazem mais ou menos 5 anos que eu trabalho remoto. Nesse tempo, tive uma passagem de 1 ano por um emprego in loco, mas nunca deixei de tocar as minhas empresas em paralelo, atendendo os clientes, planejando e executando tudo que fosse necessário.

Trabalhando de casa ou de espaços de coworking, aprendi na prática muita coisa sobre como se relacionar com os clientes e fornecedores, como planejar, acompanhar e executar projetos, e sobre como se virar quando tudo dá errado, ou chega perto disso. Com a minha recente mudança para os Estados Unidos, trabalhar remoto ganhou um significado muito mais importante, porque se antes eu podia ir visitar o cliente ou dar um jeito de passar um dia ou dois por perto dele, essa alternativa não existe mais.

Somando tudo isso, achei que seria útil escrever um texto com algumas dicas de trabalho remoto, mas aí encontrei esse artigo do Diego Eis, no Tableless, e ele já falou tudo aqui, então aproveitem:
6 dicas para se dar bem em freelas e trabalhos remotos

Propel ModelCriteria : leftJoin + useQuery

When using leftJoin and useQuery from a ModelCriteria (Query class), you can set the join type, this way:

// ...
->leftJoinFoo()
->useFooQuery(null, \Criteria::LEFT_JOIN)
// ...
->endUse()
// ...

 

Propel + Symfony2 : Debugando queries em comandos

Quando no ambiente de desenvolvimento, em um projeto baseado no Symfony2, usar o webprofiler na interface web (a partir da barra que fica no rodapé das páginas) é uma mão na roda em várias situações. Mas no console geralmente não temos essa facilidade tão a mão, porém não é impossível acessá-la. Especificamente para as queries executadas através do Propel, é possível usar o seguinte trecho para fins de debug:

$profiler = $this->getContainer()->get('profiler');
$db = $profiler->get('propel');
$db->collect(new \Symfony\Component\HttpFoundation\Request(), new \Symfony\Component\HttpFoundation\Response()); // Stubs, não são usados pelo profiler
var_dump($db->getQueries());

Você pode dar uma olhada na classe Symfony\Bridge\Propel1\DataCollector\PropelDataCollector e conferir os métodos disponíveis.

Outros profilers podem ser acessados através do container, mas como a requisição (request) e a resposta (response) não estão disponíveis no console, pode ser que nem todos funcionem como esperado.

Testando comandos do Symfony que usam serviços da aplicação

Se você tem um comando no Symfony2 que usa serviços da aplicação, como um ORM por exemplo, e seguir o modelo que a documentação do framework fornece para escrever testes unitários, poderá ver erros com os a seguir:

Fatal error: Call to undefined method Symfony\Component\Console\Application::getKernel() in [...]/vendor/symfony/symfony/src/Symfony/Bundle/FrameworkBundle/Command/ContainerAwareCommand.php on line [...]

ao acessar o kernel da aplicação, a partir do comando, ou:

Fatal error: Call to a member function getParameter() on a non-object in [...]

ao tentar acessar algum método do container.

O primeiro caso, acontece quando você usa a classe Symfony\Component\Console\Application ao invés de Symfony\Bundle\FrameworkBundle\Console\Application. Verifique os uses da sua classe de testes e ajuste se for necessário.

A segunda exceção é lançada quando você está usando a classe Application correta, mas usando um kernel inválido na sua instanciação. Talvez exista outro modo de fazer isso, mas para mim funcionou extender a classe Symfony\Bundle\FrameworkBundle\Test\WebTestCase no lugar de \PHPUnit_Framework_TestCase e construir a aplicação da seguinte forma:

$application = new Application(self::createClient()->getKernel());