Apache Flex/ Flex/ Sem categoria

Mapas para o Apache Flex

flex-maps

Se você tentou utilizar o Google Maps para a plataforma Flash nos últimos meses, você deve estar em um mar de apuros, o suporte a chave de requisição acabou e o projeto foi descontinuado e o Google Maps for Flash vai funcionar só até Setembro de 2014. Até o Yahoo Maps para Flash foi cancelado. E agora o que você pode fazer?

Pois é, muita gente ficou orfão de mapas em solução Flex, existe o MapQuest, só que não é tão bom no Brasil quanto nos EUA.

Se você já tem uma solução em Flex e que utilize o Google Maps, Yahoo, continue com ela, agora se você está pensando em colocar mapas em sua App Flex, o mais fácil será utilizar o Google Maps for Javascript e se comunicar com ele. Porém fica uma solução híbrida e pode afetar em alguma parte o workflow que você teria se tivesse dentro da própria aplicação.

Apresento a vocês uma solução Open-source que pode salvar muito desenvolvedor Flex que ficou abandonado.

Flex-Map

é uma solução Open-source que vai te ajudar a criar mapas para suas Apps Flex sem se preocupar mais com os distribuidores de mapas.

O grande problema com mapas hoje do Flex é que existem 1 alternativa para 1 distribuidor, como Google, Nokia, Microsoft, Openstreet. E se você for utilizar algum deles você acaba criando várias bibliotecas para administrar. Então é ai onde o Flex-Maps entra, com 1 só biblioteca você pode escolher qual distribuidor de mapas você quer para sua App.

Atualmente eu deixei suportando apenas os seguintes distribuidores:

  • Open Street
  • Nokia Here
  • Yahoo Maps
  • Microsoft Bing
  • Nasa Blue Marble

Construindo sua App com Mapas

É bem fácil implementar essa solução de mapas em suas App Flex, basta fazer o seguinte.

Ir ao repositório do Github. Baixar o projeto e criar um novo projeto no Flash Builder, adicionar o SWC disponível no Github e adicionar o seguinte código em sua App.
flex-maps

<?xml version="1.0" encoding="utf-8"?>
<s:Application xmlns:fx="http://ns.adobe.com/mxml/2009" 
			   xmlns:s="library://ns.adobe.com/flex/spark" 
			   xmlns:mx="library://ns.adobe.com/flex/mx" minWidth="955" minHeight="600" xmlns:flex="org.apache.flex.maps.flex.*">
		<s:layout>
			<s:VerticalLayout/>
		</s:layout>	 
 
		<fx:Script>
			<![CDATA[
				import spark.events.IndexChangeEvent;
 
				protected function onProviderChanged(event:IndexChangeEvent):void
				{
					myMap.provider = provider.selectedItem.value;
				}
 
			]]>
		</fx:Script>
		<s:HGroup>
			<s:Button width="82" height="22" label="Zoom In" click="myMap.map.zoomIn(event)"/>
			<s:Button label="Zoom Out" click="myMap.map.zoomOut(event)"/>
				<s:ComboBox id="provider" change="onProviderChanged(event)">
						<s:dataProvider>
							<s:ArrayList>
								<fx:Object label="NASA BLUE MARBLE" value="BLUE_MARBLE"/>
								<fx:Object label="Open Street Map" value="OPEN_STREET_MAP"/>
								<fx:Object label="Nokia HERE Normal" value="HERE_MAP_NORMAL"/>
								<fx:Object label="Nokia HERE Hybrid" value="HERE_MAP_HYBRID"/>
								<fx:Object label="Yahoo Aerial" value="YAHOO_AERIAL"/>
								<fx:Object label="Yahoo Hybrid" value="YAHOO_HYBRID"/>
								<fx:Object label="Yahoo Road" value="YAHOO_ROAD"/>								
								<fx:Object label="Nokia HERE TERRAIN" value="HERE_MAP_TERRAIN"/>
								<fx:Object label="Microsoft Hybrid" value="MICROSOFT_HYBRID"/>
								<fx:Object label="Microsoft Hybrid" value="MICROSOFT_HYBRID"/>
								<fx:Object label="Microsoft Aerial" value="MICROSOFT_AERIAL"/>
								<fx:Object label="Microsoft ROAD" value="MICROSOFT_ROAD"/>
							</s:ArrayList>
						</s:dataProvider>
				</s:ComboBox>
		</s:HGroup>
 
 
				<flex:FlexMap showControls="true" id="myMap"
									 width="100%" extent="-9.4384955,-40.3835929,12,0,0" height="100%" 
									 draggable="true" provider="HERE_MAP_HYBRID" 
									 zoom="12" center="-9.4384955,-40.3835929,12,0,0"
									 doubleClick="myMap.map.onDoubleClick(event)" />
</s:Application>

Viu que é fácil?

O código fonte está disponível no Github, dê um fork. O mapa para o Google ainda está em desenvolvimento.

Pessoal

Bom final de semana

cocoa_cup

Depois de uma semana concorrida e muito código escrito, só posso dizer o seguinte. Venci.
Curtam o final de semana que vocês merecem.

Eu gosto muito de música, o reggae eu uso para me lembrar que eu preciso ser sempre alegre, deixo vocês com a alegria dos Toots e os Maytals

Firefox OS

Firefox OS disponibiliza novo simulador 3.0

Firefox_mobile_mozilla

Para quem anda investigando ou desenvolvendo aplicativos para a plataforma Firefox OS, a fundação Mozilla acaba de lançar a versão 3.0 ainda beta para desenvolvedores testar.

Dentre as novidades dessa release está a possibilidade de testar a rotação e entre as novas funções ainda as velhas, como por exemplo o debug remoto ainda não é possível no Windows.

Porém muita coisa foi consertada, menor número de bugs e o sistema operacional começa a tomar jeito de release final.

Eu espero que logo logo eles adotem o simulador Ripple para testar, vai ficar muito mais fácil debugar, empacotar e testar.

Quem quiser instalar, basta atualizar o Firefox para última versão 19 e baixar a extensão:

Mais informações em inglês você pode achar aqui.

Dev. Software/ PHP

Implementando REST com PHP

rest_sample

Não faz muito tempo que eu escrevi sobre REST aqui no blog, e no post eu prometi que iria continuar com o assunto implementando serviços REST em diversas linguagens PHP, Java, C++, Python e até mesmo em Javascript.

Se você não sabe o que é REST, antes de continuar lendo o post, aconselho você ler o post que escrevi sobre o assunto, assim você não fica boiando no assunto.

Configurando o ambiente

Quem não tem um ambiente local para testar com PHP, sugiro um software que eu utilizo localmente o XAMPP. É open-source e grátis.
Existem outros no mercado que também faz o mesmo, só que eu gosto do XAMPP por que é prático.

Implementando o REST

Para implementar o REST você pode utilziar diversas bibliotecas espalhadas por ai, tem várias publicações e projetos que te levam à um atalho mais rápido para criar tal mecanismo. As que eu achei mais fantásticas foram essas:

O que eu acho mais fantástico nessas implementações são a abstração, eles basicamente deixam você livre para fazer o que quiser, a curva de aprendizagem também é bem mole.

O que um difere do outro?

Alguns recursos extras, por exemplo o Guzzle oferece uma arquitetura de plug-ins, onde você pode implementar suporte já fora da caixa em Cache, Oauth, etc. Já o Slim não tem tudo isso, porém oferece uma API mais simples de se dar.

Nesse exemplo eu vou usar o Slim Framework, baixe ele direto do site, depois de baixar descompacte-o no diretório raiz do XAMPP (htdocs), para ser mais direto.

rest-php

Na imagem acima, você ver que eu deixei só o diretório Slim, o arquivo .htdocs e o index.php

[note color=”#005fff”]IMPORTANTE: O Arquivo de .htaccess existente no projeto tem as regras necessárias para criação de rotas das URLs necessárias, você precisa tê-lo para usar o Slim.[/note]

Ok, abra o arquivo index e você tem o seguinte conteúdo.

<?php
/**
 * Step 1: Require the Slim Framework
 *
 * If you are not using Composer, you need to require the
 * Slim Framework and register its PSR-0 autoloader.
 *
 * If you are using Composer, you can skip this step.
 */
require 'Slim/Slim.php';
 
\Slim\Slim::registerAutoloader();
 
/**
 * Step 2: Instantiate a Slim application
 *
 * This example instantiates a Slim application using
 * its default settings. However, you will usually configure
 * your Slim application now by passing an associative array
 * of setting names and values into the application constructor.
 */
$app = new \Slim\Slim();
 
/**
 * Step 3: Define the Slim application routes
 *
 * Here we define several Slim application routes that respond
 * to appropriate HTTP request methods. In this example, the second
 * argument for `Slim::get`, `Slim::post`, `Slim::put`, and `Slim::delete`
 * is an anonymous function.
 */
 
// GET route
$app->get('/', function () {
    $template = <<<EOT
<!DOCTYPE html>
    <html>
        <head>
            <meta charset="utf-8"/>
            <title>Slim Framework for PHP 5</title>
            <style>
                html,body,div,span,object,iframe,
                h1,h2,h3,h4,h5,h6,p,blockquote,pre,
                abbr,address,cite,code,
                del,dfn,em,img,ins,kbd,q,samp,
                small,strong,sub,sup,var,
                b,i,
                dl,dt,dd,ol,ul,li,
                fieldset,form,label,legend,
                table,caption,tbody,tfoot,thead,tr,th,td,
                article,aside,canvas,details,figcaption,figure,
                footer,header,hgroup,menu,nav,section,summary,
                time,mark,audio,video{margin:0;padding:0;border:0;outline:0;font-size:100%;vertical-align:baseline;background:transparent;}
                body{line-height:1;}
                article,aside,details,figcaption,figure,
                footer,header,hgroup,menu,nav,section{display:block;}
                nav ul{list-style:none;}
                blockquote,q{quotes:none;}
                blockquote:before,blockquote:after,
                q:before,q:after{content:'';content:none;}
                a{margin:0;padding:0;font-size:100%;vertical-align:baseline;background:transparent;}
                ins{background-color:#ff9;color:#000;text-decoration:none;}
                mark{background-color:#ff9;color:#000;font-style:italic;font-weight:bold;}
                del{text-decoration:line-through;}
                abbr[title],dfn[title]{border-bottom:1px dotted;cursor:help;}
                table{border-collapse:collapse;border-spacing:0;}
                hr{display:block;height:1px;border:0;border-top:1px solid #cccccc;margin:1em 0;padding:0;}
                input,select{vertical-align:middle;}
                html{ background: #EDEDED; height: 100%; }
                body{background:#FFF;margin:0 auto;min-height:100%;padding:0 30px;width:440px;color:#666;font:14px/23px Arial,Verdana,sans-serif;}
                h1,h2,h3,p,ul,ol,form,section{margin:0 0 20px 0;}
                h1{color:#333;font-size:20px;}
                h2,h3{color:#333;font-size:14px;}
                h3{margin:0;font-size:12px;font-weight:bold;}
                ul,ol{list-style-position:inside;color:#999;}
                ul{list-style-type:square;}
                code,kbd{background:#EEE;border:1px solid #DDD;border:1px solid #DDD;border-radius:4px;-moz-border-radius:4px;-webkit-border-radius:4px;padding:0 4px;color:#666;font-size:12px;}
                pre{background:#EEE;border:1px solid #DDD;border-radius:4px;-moz-border-radius:4px;-webkit-border-radius:4px;padding:5px 10px;color:#666;font-size:12px;}
                pre code{background:transparent;border:none;padding:0;}
                a{color:#70a23e;}
                header{padding: 30px 0;text-align:center;}
            </style>
        </head>
        <body>
            <header>
                <a href="http://www.slimframework.com"><img src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAHIAAAA6CAYAAABs1g18AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAABRhJREFUeNrsXY+VsjAMR98twAo6Ao4gI+gIOIKOgCPICDoCjCAjXFdgha+5C3dcv/QfFB5i8h5PD21Bfk3yS9L2VpGnlGW5kS9wJMTHNRxpmjYRy6SycgRvL18OeMQOTYQ8HvIoJKiiz43hgHkq1zvK/h6e/TyJQXeV/VyWBOSHA4C5RvtMAiCc4ZB9FPjgRI8+YuKcrySO515a1hoAY3nc4G2AH52BZsn+MjaAEwIJICKAIR889HljMCcyrR0QE4v/q/BVBQva7Q1tAczG18+x+PvIswHEAslLbfGrMZKiXEOMAMy6LwlisQCJLPFMfKdBtli5dIihRyH7A627Iaiq5sJ1ThP9xoIgSdWSNVIHYmrTQgOgRyRNqm/M5PnrFFopr3F6B41cd8whRUSufUBU5EL4U93AYRnIWimCIiSI1wAaAZpJ9bPnxx8eyI3Gt4QybwWa6T/BvbQECUMQFkhd3jSkPFgrxwcynuBaNT/u6eJIlbGOBWSNIUDFEIwPZFAtBfYrfeIOSRSXuUYCsprCXwUIZWYnmEhJFMIocMDWjn206c2EsGLCJd42aWSyBNMnHxLEq7niMrY2qyDbQUbqrrTbwUPtxN1ZZCitQV4ZSd6DyoxhmRD6OFjuRUS/KdLGRHYowJZaqYgjt9Lchmi3QYA/cXBsHK6VfWNR5jgA1DLhwfFe4HqfODBpINEECCLO47LT/+HSvSd/OCOgQ8qE0DbHQUBqpC4BkKMPYPkFY4iAJXhGAYr1qmaqQDbECCg5A2NMchzR567aA4xcRKclI405Bmt46vYD7/Gcjqfk6GP/kh1wovIDSHDfiAs/8bOCQ4cf4qMt7eH5Cucr3S0aWGFfjdLHD8EhCFvXQlSqRrY5UV2O9cfZtk77jUFMXeqzCEZqSK4ICkSin2tE12/3rbVcE41OBjBjBPSdJ1N5lfYQpIuhr8axnyIy5KvXmkYnw8VbcwtTNj7fDNCmT2kPQXA+bxpEXkB21HlnSQq0gD67jnfh5KavVJa/XQYEFSaagWwbgjNA+ywstLpEWTKgc5gwVpsyO1bTII+tA6B7BPS+0PiznuM9gPKsPVXbFdADMtwbJxSmkXWfRh6AZhyyzBjIHoDmnCGaMZAKjd5hyNJYCBGDOVcg28AXQ5atAVDO3c4dSALQnYblfa3M4kc/cyA7gMIUBQCTyl4kugIpy8yA7ACqK8Uwk30lIFGOEV3rPDAELwQkr/9YjkaCPDQhCcsrAYlF1v8W8jAEYeQDY7qn6tNGWudfq+YUEr6uq6FZzBpJMUfWFDatLHMCciw2mRC+k81qCCA1DzK4aUVfrJpxnloZWCPVnOgYy8L3GvKjE96HpweQoy7iwVQclVutLOEKJxA8gaRCjSzgNI2zhh3bQhzBCQQPIHGaHaUd96GJbZz3Smmjy16u6j3FuKyNxcBarxqWWfYFE0tVVO1Rl3t1Mb05V00MQCJ71YHpNaMcsjWAfkQvPPkaNC7LqTG7JAhGXTKYf+VDeXAX9IvURoAwtTFHvyYIxtnd5tPkywrPafcwbeSuGVwFau3b76NO7SHQrvqhfFE8kM0Wvpv8gVYiYBlxL+fW/34bgP6bIC7JR7YPDubcHCPzIp4+cum7U6NlhZgK7lua3KGLeFwE2m+HblDYWSHG2SAfINuwBBfxbJEIuWZbBH4fAExD7cvaGVyXyH0dhiAYc92z3ZDfUVv+jgb8HrHy7WVO/8BFcy9vuTz+nwADAGnOR39Yg/QkAAAAAElFTkSuQmCC" alt="Slim"/></a>
            </header>
            <h1>Welcome to Slim!</h1>
            <p>
                Congratulations! Your Slim application is running. If this is
                your first time using Slim, start with this <a href="http://www.slimframework.com/learn" target="_blank">"Hello World" Tutorial</a>.
            </p>
            <section>
                <h2>Get Started</h2>
                <ol>
                    <li>The application code is in <code>index.php</code></li>
                    <li>Read the <a href="http://docs.slimframework.com/" target="_blank">online documentation</a></li>
                    <li>Follow <a href="http://www.twitter.com/slimphp" target="_blank">@slimphp</a> on Twitter</li>
                </ol>
            </section>
            <section>
                <h2>Slim Framework Community</h2>
 
                <h3>Support Forum and Knowledge Base</h3>
                <p>
                    Visit the <a href="http://help.slimframework.com" target="_blank">Slim support forum and knowledge base</a>
                    to read announcements, chat with fellow Slim users, ask questions, help others, or show off your cool
                    Slim Framework apps.
                </p>
 
                <h3>Twitter</h3>
                <p>
                    Follow <a href="http://www.twitter.com/slimphp" target="_blank">@slimphp</a> on Twitter to receive the very latest news
                    and updates about the framework.
                </p>
            </section>
            <section style="padding-bottom: 20px">
                <h2>Slim Framework Extras</h2>
                <p>
                    Custom View classes for Smarty, Twig, Mustache, and other template
                    frameworks are available online in a separate repository.
                </p>
                <p><a href="https://github.com/codeguy/Slim-Extras" target="_blank">Browse the Extras Repository</a></p>
            </section>
        </body>
    </html>
EOT;
    echo $template;
});
 
// POST route
$app->post('/post', function () {
    echo 'This is a POST route';
});
 
// PUT route
$app->put('/put', function () {
    echo 'This is a PUT route';
});
 
// DELETE route
$app->delete('/delete', function () {
    echo 'This is a DELETE route';
});
 
/**
 * Step 4: Run the Slim application
 *
 * This method should be called last. This executes the Slim application
 * and returns the HTTP response to the HTTP client.
 */
$app->run();

Mude-o para isso aqui.

<?php
require 'Slim/Slim.php'; // chamando a biblioteca
 
\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim(); // registrando e inicializando o objeto no servidor
 
 
// GET route
$app->get('/', function () {
    $template = <<<EOT
        <h1>Implementando REST com slim framework</h1>
EOT;
    echo $template;
});
 
// POST route
$app->post('/post', function () {
    echo 'This is a POST route';
});
 
// PUT route
$app->put('/put', function () {
    echo 'This is a PUT route';
});
 
// DELETE route
$app->delete('/delete', function () {
    echo 'This is a DELETE route';
});
 
$app->run();

O que eu fiz foi remover os comentários excessivos que tinha no arquivo e coloquei uma mensagem customizada para mostrar que roda tranquilamente.

Toda vez que você for implementar um novo método no Slim Framework, basta você definir que tipo de comportamento ele possui e a ação que ele irá fazer.

Por exemplo quero criar um novo método chamado calcular.

$app->post('/calcular/:metodo',function($metodo) use ($app){
 
        $request = $app->request();
        $body = $request->post();
 
    switch($metodo){
            case 'somar':
                        echo $body['A'] + $body['B'];
            break;
            case 'dividir':
                        echo $body['A'] / $body['B'];
            break;
            case 'multiplicar':
                        echo $body['A'] * $body['B'];
            break;
            case 'diminuir':
                        echo $body['A'] - $body['B'];
            break;
    }
});

Eu posso usar as URIs com os métodos (/put, /post, /get, /delete).

E para testar meu serviço de REST como faço? Bom, você pode usar o cURL ou usar uma GUI para facilitar o processo, já que eu estou utilizando o navegador, eu vou adicionar uma extensão para o Google Chrome fazer isso. POSTMAN para nos salvar.

rest-gui

Na Webstore do google procure pelo POST MAN e você acha ele fácil, basta clicar em instalar e começar a testar.

rest-gui-demo

Coloco a URL, passo os parametros e voilá! Meu serviço de REST está implementado.

E como fica a requisição do servidor na aba de acesso? Vamos ver!

rest-php-call

Perfeito, eu posso criar vários serviços Web sem precisar escrever uma interface própria para testar, não existe mais desculpas esfarrapadas de esperar o Designer terminar a conversão de PSD para HTML/CSS para você começar a testar os serviços.

Esse é um belo exemplo simples de como implementar um serviço de REST. Agora vamos para um exemplo prático, digo mais completo.

Nesse exemplo usei o SQlite 3 para persistir os dados, você pode usar o que você quiser para persistência em PHP, o Slim não implica o uso de nenhum, já que dentro de suas chaves você pode escrever o que quiser.

[note color=”#007bff”]SQLite 3 só é suportado no PHP 5.4 em diante. Versões anteriores você pode usar o SQLite 2. Para saber qual versão que você tem use o php_info();[/note]

É bem simples o serviço de REST com uma API bem simplificada. O código abaixo contém as seguintes rotas que usaremos para o serviço.

  • GET /preload-inicial – Uso ele só 1 vez para carregar com dados falsos para ter algo para mostrar
  • GET /backup – Faz backup do banco
  • GET /clientes – Lista todos os clientes
  • GET /compras – Lista todas as compras do site
  • GET /compras/:id – Lista todas as compras de um cliente especifico pelo ID
  • PUT /cliente – Adicionar novos clientes
  • PUT /compra – Adicionar novas compras
  • POST /cliente/:id – Atualiza o cliente pelo ID
  • POST /compra/:id – Atualiza uma compra pelo ID
<?php
require 'Slim/Slim.php';
 
\Slim\Slim::registerAutoloader();
$app = new \Slim\Slim();
 
$response =array();
$db = new SQLite3('coisas.db');
 
 
// Uso essa função só para gerar dados fakes para ser usado no sistema
$app->get('/preload-inicial', function () use ($db,$response)
{       $value = rand(5,99);
        $db->exec('CREATE TABLE IF NOT EXISTS clientes(id INTEGER PRIMARY KEY AUTOINCREMENT, nome TEXT);');
        $db->exec('CREATE TABLE IF NOT EXISTS compras(id INTEGER PRIMARY KEY, cliente_id INTEGER, valor INTEGER, compra_realizada DATETIME);');
        $db->query("INSERT OR IGNORE INTO clientes (nome) VALUES ('Igor Costa');");
        $db->query("INSERT OR IGNORE INTO clientes (nome) VALUES ('Elly Costa');");
        $db->query("INSERT OR IGNORE INTO clientes (nome) VALUES ('Leonardo Sobral');");
        $db->query("INSERT OR IGNORE INTO clientes (nome) VALUES ('Francisco Brianezi');");
        for($i=1;$i<=4;$i++){
            $db->query("INSERT INTO compras (valor,cliente_id,compra_realizada) VALUES ($value,$i,datetime());");
        }
 
});
// adicionar cliente
$app->put('/cliente', function () use($db,$response,$app) {
    $request = $app->request();
    $cliente = $request->put('nome');
    $response =$db->exec("INSERT INTO clientes (nome) VALUES ('$cliente');");
    echo $response;
});
// adicionar nova compra
$app->put('/compra', function () use($db,$response,$app) {
    $request = $app->request();
    $valor = $request->put('valor');
    $cliente = $request->put('cliente_id');
    $response = $db->query("INSERT INTO compras (valor,cliente_id,compra_realizada) VALUES ($valor,$cliente,datetime());");
    echo $response;
});
 
 
// Ediar cliente
 
$app->post('/cliente/:id',function($id) use($db,$response,$app){
    $request = $app->request();
    $nome = $request->post('nome');
    $response = $db->exec("UPDATE clientes SET nome='$nome' WHERE id=$id;");
    echo json_encode($response);
});
 
$app->post('/compra/:id',function($id) use($db,$response,$app){
    $request = $app->request();
    $valor = $request->post('valor');
    $cliente = $request->post('cliente_id');
    $response = $db->exec("UPDATE compras SET valor='$valor', cliente_id='$cliente' WHERE id=$id;");
    echo json_encode($response);
});
 
// lista os clientes
$app->get('/clientes',function()use ($db,$response,$app){
 
   $app->contentType('application/json');
   $tarefas = $db->query("SELECT * FROM clientes");
        while ($row = $tarefas->fetchArray(SQLITE3_ASSOC)) {
            array_push($response,$row);
        }
      echo json_encode($response);
 
});
// lista todas as compras
$app->get('/compras',function() use($db,$response,$app){
   $app->contentType('application/json');
   $tarefas = $db->query("SELECT clientes.id,clientes.nome,COUNT(*) as total_compras
                          FROM compras INNER JOIN clientes ON compras.cliente_id = clientes.id 
                          GROUP BY clientes.nome ORDER BY clientes.nome ASC");
        while ($row = $tarefas->fetchArray(SQLITE3_ASSOC)) {
            array_push($response,$row);
        }
      echo json_encode($response);
 
});
// lista todas as compras por id do cliente
$app->get('/compras/:id',function($id) use ($db,$response){
 
    $tarefas = $db->query("SELECT * FROM compras INNER JOIN clientes 
        ON compras.cliente_id = clientes.id  WHERE clientes.id = $id");
        while ($row = $tarefas->fetchArray(SQLITE3_ASSOC)) {
            array_push($response,$row);
        }
      echo json_encode($response); 
});
$app->get('/backup',function() use($app){
    $date = date("Y-m-d");
    exec('mkdir backups');
    $file = 'backups/backup-'.$date . '.sql';
    exec("sqlite3 coisas.db .dump > ".$file);
    echo readfile($file);
});
 
// Deleta cliente
$app->delete('/deleta/cliente/:id', function ($id) use ($db,$response,$app) {
 
    // para remover compras existentes do cliente
    $db->exec("DELETE FROM compras WHERE cliente_id=$id;");
    $response =$db->query("DELETE FROM clientes WHERE id=$id;");
    echo $response; 
});
// Deleta compra
$app->delete('/deleta/compra/:id', function ($id) {
    $response =$db->query("DELETE FROM compras WHERE id=$id;");
    echo $response; 
});
 
$app->run();

Para consumir esse serviço você pode usar a seguinte interface.

rest-gui-php-demo

Agora você já sabe como implementar um serviço em RESTful com PHP usando o Slim Framework. Na próxima competição que você participar use algo assim, é hiper rápido e você não gasta mais que 1 hora para implementar.

O uso do SQLite eu aconselho para coisas bem simples, listas simples e pouco à médio volume de dados. Não queria criar um ERP com SQLite que você está perdendo seu tempo.

Para maiores detalhes sobre o Slim Framework, visite a documentação do projeto. É simples e bem intuitiva.

O código fonte está disponível também no Github.

Pessoal

9 anos no ar

bolo-aniversario

Hoje comemoro mais um aniversário do site. Ao todo são 9 anos escrevendo posts fresquinhos, deliciosos e cheios de novidades para você. HAHA!

Brincadeiras à parte, faz exatos 9 anos que iniciei o blog e em meus registros é o blog mais antigo de tecnologia que você possa achar ainda ativo.

Eu devo tudo isso à você que não deixa de visitar o blog todo santo dia. Algumas vezes erramos, outras vezes acertamos e faz parte da vida assim como você e quem vos escreve.

E durante todo esse tempo ajudei muita gente a realizar seus sonhos, conquistar novas posições. E o que é importante é isso, ajudar o próximo.

Quantas pessoas passaram por aqui nesses 9 anos?

Visitantes únicos : 5.644.391

Pageviews : 9.220.113

E como havia dito lá atrás, estamos mudando o rumo dos posts e estou gostando do resultado, os leitores realmente querem entrar nesse mundo mobile.

Até o próximo post.

BB10/ HTML5 / CSS3/ Mobile

Criando seu primeiro App com BBUI.js e Webworks para BB10

bb10

Uffa! Quem acompanha no twitter, viu que passei uma maratona para criar um App do zero para a plataforma BB10 gastando aproximadamente 29 horas, por simples 2 motivos.

1 – Conhecer o workflow de desenvolvimento da plataforma

2 – Ganhar um device específico para desenvolvedor.

Ok, minha aventura começa na quinta-feira as 10 da manhã, primeiro passo foi conhecer bem o webworks um SDK específico para a plataforma Blackberry desde a versão 5.0 em diante. Por que não o Phonegap? Veja só; Basicamente o phonegap funciona com muita coisa do webworks, então ao invés de tercerizar a API eu partir direto para a plataforma específica, não é assim que tem que ser a cadeia de distribuição? Alô administradores!

Antes que você leia todo o post, esse video abaixo mostra o que vamos fazer até o final do post.

Documentação

Primeira coisa que notei e me senti em casa, documentação, como estava acostumado com as documentações da Adobe, elas não fogem, vasta e muito bem detalhada, inclusive tem suporte a tudo quanto é tipo de desenvolvimento não só para HTML. A documentação mais completa acima de todas, sem dúvida é a do Cascades, que se fosse comparar à outras plataformas, seria a plataforma oficial.

Porém, a blackberry deixa bem claro na página de desenvolvedores, ela ama todas as tecnologias de desenvolvimento e ela deixa bem claro em sua página para os desenvolvedores.

A documentação do webworks é basicamente o empacotador, já que 80% da API do webworks é puramente HTML5. Isso mesmo! Não tem formula mágica, adorei essa parte do desenvolvimento por que frisa justamente a singularidade de uma só API. Claro que é importante notar que há exceções como toda regra, por exemplo APIs que são identicas ao HTML5.

O que é igual a api do HTML5:

  • Geo Localização
  • Captura de Midia( camera apenas)
  • Camera
  • File ( estrutura de arquivos)
  • Persistência de arquivos
  • Cache
  • Aceleração (acelerometro)
  • SMS, Chamar telefone, E-mail
  • Alerta ( javascript:alert(‘mensagem’);
  • Esquemas de URI

O que é específico da plataforma e requer o webworks de fato:

  • BBM
  • Calendário
  • Sensores( Giroscópio, Gravidade, Magnetometro, aproxiamção)
  • Toast
  • Microfone
  • Identity (para identificação do dispositivo)
  • Agenda de contatos
  • Pagamento ( in-App purchase)
  • Systema como nível de bateria e etc.

 

Comunidade e colaboratividade

Ambas andam juntos é claro, e a decisão dela de usar o Github para expor suas APIs, SDK de forma aberta ao público, facilitou ainda mais o trajeto do desenvolvedor aos seus SDK. Tanto é que existem repositórios completos só para listar contribuições dos desenvolvedores espalhados pelo mundo.

O mais importante não é nem os SDKs da Blackberry e sim o Github, você facilmente faz amizade com quem está por trás do projeto e vira e mexe você até contribui para melhorias. Essa idéia de social + código, foi um casamento da raposa, muito bem bolada!

bbuijs

BBUI.js Toolkit para aparência das Apps

Você já ouviu falar do Twitter Bootstrap, JQuery Mobile, Sencha Touch, Enyo, certo? Então adicione a lista o BBUI.JS, ele funciona só para os dispositivos da Blackberry, já que todo seu sistema de eventos é voltado para eventos do próprio navegador, se você tiver tempo, pode mudar a aparência, mais isso ai é meio que matar o Chinês achando que é barata. Sacô?!

É basicamente Javascript e extensívo uso do atributo data-bb-* . É um ponto de partida para você começar a criar algo que respeite o guia de interface da plataforma, só que não fique na regra, você pode mudar e usar o que você quiser.

[pullquote align=”left”]Você pode continuar usando seu framework preferido, lembre-se só da experiência mobile![/pullquote]

A parte de transições de telas built-in é bem legal, com vários efeitos pré fabricados e o mais interessante notar é a parte de navegação entre as telas, lembra muito o navigator do Flex Mobile; Só que usa de fato um Array simples para fazer o cast das telas pelo atributo data-bb-type=”screen”.

Na página do projeto, você encontra mais detalhes da documentação, como inicializar as coisas. Eu quero focar mais na parte Hello World! Quando você terminar de ler o post já sair com um App na mão.

Criando um App de demonstração

[note color=”#FFCC00″]No próprio site do Webworks, você encontra um guia passo-a-passo em inglês mais completo que este, se você quiser saber mais sobre como iniciar com o pé direito, vá direto ao site. http://developer.blackberry.com/html5/[/note]

Primeiro passo

Registre-se como desenvolvedor, é 0800, grátis, oba-oba, FREE, Paga não, Passe-livre.

Segundo passo

Requisite o acesso as chaves para assinar sua App, sem elas a festa não acontece. Elas funcionam para qualquer SDK que você vier usar.

[pullquote align=”left”]Dica importante: Lembre-se do PIN que você fornecer, esse PIN é uma espécie de RG.[/pullquote]

As chaves são obrigatória, elas tem um propósito: Assinar suas obras de arte “Seus Apps” para distribuição e teste.

Terceiro passo

Baixe o WebWorks, SDK Aqui. http://developer.blackberry.com/html5/download/ Dica( use a versão Gold, que é o botãozinho cor de ouro na página).
Para testes e desenvolvimento, vamos usar o Ripple Emulator, que é uma ferramenta para testes e emulação do seu HTML5 para dispositivos móveis, ele foi criado pela Blackberry (a.k.a RIM), e é hoje bastante utilizado.

[pullquote align=”left”]Siga as instruções de instalar o Ripple Emulator no Google Chrome.[/pullquote] Supondo que deu tudo certo nas suas instalações, siga as seguintes instruções:
Baixe as seguintes bibliotecas, elas são necessárias para iniciar o toolkit e acessar tags do HTML de forma mais fácil, ZeptoJS sem dúvida é a melhor para isso no ambito mobile, já que ele é leve, bem leve.

E quanto a ferramenta de desenvolvimento? Bom, use a que você achar melhor para editar páginas em HTML5, CSS3, Javascript. Eu gosto do Sublime Text. Mas não se apegue a esse detalhe, vai de sua escolha e ele não influencia o work-flow para esse exemplo.
Estrutura de uma App

É bem simples, basta ter um arquivo config.xml que é a espinha do projeto e um arquivo html para você indexar.

Vamos fazer o seguinte, veja um exemplo pronto de um arquivo de configuração de um projeto para criá-lo do zero.

<?xml version="1.0" encoding="utf-8"?>
<widget xmlns="http://www.w3.org/ns/widgets"
        xmlns:rim="http://www.blackberry.com/ns/widgets"
        version="1.0.0"
        id="com.apps.suaApp">
 
  <name>To-do-List</name>
  <content src="index.html" />
  <author rim:copyright="2013"
            href="http://www.igorcosta.com/"
            email = "igorcosta@gmail.com">Igor Costa</author>
 
 
  <description>Simples App com lista de tarefas, crie listas e tarefas especificas</description>
 
  <license href="http://www.apache.org/licenses/LICENSE-2.0">
        Licença caso sua App se aplique
  </license>
 
  <access uri="*" subdomains="true" />
  <access subdomains="true" uri="file:///store/home" />
  <access subdomains="true" uri="file:///SDCard" />
  <access subdomains="true" uri="local:///" />
 
  <feature id="blackberry.app">
        <param name="orientation" value="portrait" />
  </feature>
  <feature id="blackberry.app.event"/>
  <feature id="blackberry.system.event" />
  <feature id="blackberry.connection" />
  <feature id="blackberry.ui.toast" />
  <feature id="blackberry.ui.dialog" />
  <feature id="blackberry.io" />
  <feature id="blackberry.io.file" required="true" version="1.0.0.0" />
  <feature id="blackberry.utils"   required="true" version="1.0.0.0" />
  <feature id="blackberry.io.dir"  required="true" version="1.0.0.0" />
 
    <rim:splash src="splash-portrait-768x1280.png" />
    <rim:splash src="splash-landscape-1280x768.png" />
 
 
    <!-- para mais permissões visite 
    http://developer.blackberry.com/html5/documentation/rim_permit_element.html
	-->
    <rim:permissions>
        <rim:permit>access_location_services</rim:permit>
        <rim:permit>use_camera</rim:permit>
        <rim:permit>access_shared</rim:permit>
    </rim:permissions>
 
 
 </widget>

Salve esse arquivo com o nome config.xml em um diretório seu local, por exemplo localhost/bbui_app_exemplo
No mesmo diretório crie um arquivo de html, index.html como eu configurei ali acima para chamar logo que iniciar o arquivo index.html na tag content.

O arquivo config é bem simples e quase auto explicatório o que cada tag faz. A única coisa que você precisa ter em mente é que se você quer acessar a internet em domínios diferentes, a tag access te ajuda nisso, lembre-se de incluir sub-dominios caso exista.

Quarto passo

Criar a app propriamente. Quando você instala o Ripple Emulator no Chrome você pode usar a biblioteca do webworks direto na sua App, sem precisar colocar no diretório, já que você vai utiliza-lo para empacotar a App, basta fazer uma referência ao script dessa forma aqui.

<script type="text/javascript" src="local:///chrome/webworks.js"></script>

A estrutura do App ficou dessa forma.
[pullquote align=”left”]bbuijs-estrutura[/pullquote]

Para economizar tempo, use esse template que eu fiz do arquivo index.html, assim fica mais fácil de você replicar em sua própria máquina.

<!doctype html>
<html>
	<head>
	  <meta charset="utf-8">
	  <meta name="format-detection" content="telephone=no" />
	  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />  
	<title>Sua App</title>
 
	<!-- Estilos -->
	<link rel="stylesheet" type="text/css" href="css/style.css" />
	<link rel="stylesheet" type="text/css" href="css/bbui.css" />
 
	<!-- Javascript -->
	<script type="text/javascript" src="js/lib/zepto.js"></script>
	<script type="text/javascript" src="js/lib/bbui.js"></script>
    <script type="text/javascript" src="local:///chrome/webworks.js"></script>
 
	</head>
 
	<body>
		<div>
 
		</div>
	</body>
</html>

Ok, estamos prontos para começar a criar o App, mais que App? Para o própsito desse post vamos fazer um App simples, uma TO-DO-LIST, carregamos de um banco de dados local usando Web Storage, editamos e adicionamos itens.

Antes de criar qualquer coisa, deixa eu explicar mais ou menos como funciona o BBUIjs.
bbuijs-estrutura2

Ok, o BBUI.js além do nome ser sugestivo ele já possui um esquema de navegação no próprio toolkit, como você pode ver ai na imagem acima, basicamente o que você precisar em termos de interface básica o próprio toolkit oferece, porém se você quer algo mais complexo, sugiro utilizar outro, ele atende a uma única e exclusiva vantagem de ter a aparência nativa de um App. Quando você termina ela fica tão bem polida que parece muito com um App nativo.

O navegador entre telas funciona muito bem, e já possui efeitos de transição entre elas como Fade,Slide Left/Right,Slide Up/Down. Se você quiser um efeito de carta (flip) por exemplo, ai você vai ter que implementar. Como ele não atende exclusivamente apenas ao BB10, é bom notar algumas particularidades. Esses detalhes você pode conferir no Wiki do projeto.

Para inicializar o toolkit, você precisa escutar o evento webworksready, quando estiver pronto você pode iniciar com esses parametros.

            var webworksreadyFired = false;
            document.addEventListener('webworksready', function(e) {
 
                if (webworksreadyFired) return;
                webworksreadyFired = true;
                app.initialize(); //phonegap
 
                bb.init({actionBarDark: true,
                        controlsDark: true, // controles são pretos ou brancos
                        listsDark: false, // usar uma lista escura
                        bb10ForPlayBook: false, // é para playbook ou não
                        highlightColor: '#00A8DF', // cor de alguns elementos
                        onscreenready : function(element,id,params){
                            // use essa função para criar algum argumento
                        },
                        ondomready : function(element,id,params){
                          // depois que o DOM da tela estiver pronto você pode disparar algum evento antes 
                         // da tela ficar pronta.
                        }
 
                        });
 
                 // Depois de iniciado, chame a tela principal do App.
                 bb.pushScreen('Main.html', 'MainScreen');
            }, false);

Quinto passo

bbuijs-app-estrutura

Vou adotar essa estrutura de navegação do App. Simples e direta.

Como é que se cria as telas? Basicamente por esquema de atributo, o toolkit basicamente usa o atributo data-bb-* para definir as coisas.

Crie os .html conforme mostra a imagem e defina o HTML de cada um deles da seguinte forma.

<div data-bb-type="screen" data-bb-indicator="true" data-bb-back-caption="Voltar">
 
</div>

Para navegar entre as telas basta eu usar o método pushScreen(‘pagina.html’,’idPagina’); Por dentro do toolkit ele faz um popScreen() da tela atual e retorna com a tela anterior.

Então aqui vai o código atual das páginas que tenho.

index.html

<!doctype html>
<html>
	<head>
	  <meta charset="utf-8">
	  <meta name="format-detection" content="telephone=no" />
	  <meta name="viewport" content="user-scalable=no, initial-scale=1, maximum-scale=1, minimum-scale=1, width=device-width, height=device-height, target-densitydpi=device-dpi" />  
	<title>Sua App</title>
 
	<!-- Estilos -->
	<link rel="stylesheet" type="text/css" href="css/style.css" />
	<link rel="stylesheet" type="text/css" href="css/bbui.css" />
 
	<!-- Javascript -->
	<script type="text/javascript" src="js/libs/zepto.js"></script>
	<script type="text/javascript" src="js/libs/bbui.js"></script>
    <script type="text/javascript" src="local:///chrome/webworks.js"></script>
    <script type="text/javascript">
            var webworksreadyFired = false;
            document.addEventListener('webworksready', function(e) {
 
                if (webworksreadyFired) return;
                webworksreadyFired = true;
                bb.init({actionBarDark: true,
                        controlsDark: true,
                        listsDark: false,
                        bb10ForPlayBook: false,
                        highlightColor: '#C82C29',
                        onscreenready : function(element,id,params){
                        },
                        ondomready : function(element,id,params){
                        }
 
                        });
                 bb.pushScreen('Main.html', 'MainScreen');
            }, false);
 
        </script>          
	</head>
 
	<body>
		<div>
 
		</div>
	</body>
</html>

Tela Main.html

<div data-bb-type="screen" data-bb-indicator="true">
	<div data-bb-type="title" data-bb-caption="Principal"></div>
 
	 <ul>
	     <li><a href="#" onClick="bb.pushScreen('Listas.html','ListaScreen')"  title=""> ir para Lista de Tarefas</a></li>
	     <li> <a href="#" onClick="bb.pushScreen('Sobre.html','SobreScreen')"  title=""> ir para Sobre Tela</a> </li>
	     <li><a href="#" onClick="bb.pushScreen('Tarefas.html','ListaScreen')"  title="">ir para Tarefa</a></li>
	 </ul> 
</div>

Telas Sobre.html

<div data-bb-type="screen" data-bb-indicator="true">
	<div data-bb-type="title" data-bb-caption="Tarefas" data-bb-back-caption="Voltar"></div>
</div>

Telas Listas e Tarefas possuem o mesmo código fonte

<div data-bb-type="screen" data-bb-indicator="true">
	<div data-bb-type="title" data-bb-caption="Tarefas" data-bb-back-caption="Voltar"></div>
 
	<div data-bb-type="image-list" data-bb-images="none" data-bb-style="arrowlist">
		 <div data-bb-type="header">Lista de tarefas para fazer</div>
		 <div data-bb-type="item"  data-bb-accent-text="Strength" data-bb-title="Item 01">Descrição</div>
	</div>
</div>

Ok, interface montada, veja o que você tem até agora.

Sexto passo

Já que você tem montado as telas, hora de começar a programa-las, para persistência de banco de dados eu fiz uma classe em Javascript de forma literal, igual como você faz na declaração de um objeto JSON.

/* Copyright 2013 Igor Costa
 
 Licensed under the Apache License, Version 2.0 (the "License");
 you may not use this db except in compliance with the License.
 You may obtain a copy of the License at
 
   http://www.apache.org/licenses/LICENSE-2.0
 
 Unless required by applicable law or agreed to in writing, software
 distributed under the License is distributed on an "AS IS" BASIS,
 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 See the License for the specific language governing permissions and
 limitations under the License.
 
 
Simple class for WebStorage persistence, not big deal. Just for to-do-list demo
*/
var Db = {
	currentList:'',
	db : window.openDatabase('geba','0.1','Banco de dados simples para um App de to-do-list',2*1024*1024),
	listaSQL : 'CREATE TABLE IF NOT EXISTS listas (id  INTEGER,lista_nome TEXT NOT NULL, cor_lista TEXT NOT NULL DEFAULT "#f2f2f2",data_criada TEXT NOT NULL, PRIMARY KEY (id))',
	tarefasSQL: 'CREATE TABLE IF NOT EXISTS tarefas(id INTEGER,tarefa_nome  TEXT,completa  REAL,data_criada  TEXT,lista_id  INTEGER NOT NULL,PRIMARY KEY (id),FOREIGN KEY (lista_id) REFERENCES listas (id))',
	open : function () {
			try{
				if (window.openDatabase) {
					if(this.db != null){
						this.db.transaction(
				        function (transaction) {
				        	var demoData = new Date();
				        	// preload de 1 lista e 1 tarefa
				        	transaction.executeSql(Db.listaSQL, [], this.onSucess, this.errorHandler);
				        	transaction.executeSql(Db.tarefasSQL, [], this.onSucess, this.errorHandler);
				        	//To insert new records in the first demo, uncomment this than comment again.
				        	//transaction.executeSql('INSERT INTO listas (id,lista_nome,cor_lista,data_criada) VALUES (1,"Lista Demo","#aa0000","'+demoData+'")',[],Db.onSucess, Db.errorHandler);
				        	//transaction.executeSql('INSERT INTO tarefas (tarefa_nome,completa,data_criada,lista_id) VALUES ("Primeira tarefa demo","true","'+demoData+'",1)',[],Db.onSucess, Db.errorHandler);
				        	//transaction.executeSql('INSERT INTO tarefas (tarefa_nome,completa,data_criada,lista_id) VALUES ("Segunda tarefa demo","true","'+demoData+'",1)',[],Db.onSucess, Db.errorHandler);
				        	//transaction.executeSql('INSERT INTO tarefas (tarefa_nome,completa,data_criada,lista_id) VALUES ("Terceira tarefa demo","true","'+demoData+'",1)',[],Db.onSucess, Db.errorHandler);
				        });
				    }	
				}else{
					var msgSupport = "Doesn't support WebStorage";
					alert(msgSupport);
					console.log(msgSupport);
				}
 
			}catch(e){
					printMsg();
			}
	},
	close : function (){
 
	},
	executeSQL : function (sql){
 
	},
	editRecord : function (object){
 
	},
	deleteTarefa : function (id){
		try{
 
			this.db.transaction(
				        function (transaction) {
 
				        	transaction.executeSql('DELETE FROM tarefas WHERE id=?',[id], Db.onDeleteTarefaSucess, Db.errorHandler);
				        });	
 
 
		}catch(e){
			printMsg();
		}
	},
	onDeleteTarefaSucess : function (e,result){
			document.getElementById('myTarefasList').refresh();
			Db.getTarefas(Db.currentList);
			Db.toast('Tarefa Apagada');
 
	},
	addNewLista : function (name){
		try{
 
			this.db.transaction(
				        function (transaction) {
				        var demoData = new Date();	
				        	transaction.executeSql('INSERT INTO listas (lista_nome,cor_lista,data_criada) VALUES (?,"#aa0000","'+demoData+'")',[name],Db.onSucess, Db.errorHandler);
				        });	
 
			bb.pushScreen('Listas.html','ListaScreen');
		}catch(e){
			printMsg();
		}			
	},
	getListas : function (){
		try{
			this.db.transaction(
				        function (transaction) {
				        	transaction.executeSql('SELECT * from listas',[],Db.onListaSucess, Db.errorHandler);
				        });	
 
		}catch(e){
			printMsg();
		}
	},
	onListaSucess : function (e,result){
				var response = [];
				var len = result.rows.length, i;
				var render;
				  for (i = 0; i < len; i++) {
				   var item = result.rows.item(i);
 
				         render =  document.createElement('div');
					     render.setAttribute('data-bb-type','item');
					     render.setAttribute('data-bb-title',item.lista_nome);
					     render.setAttribute('data-itemid',item.id);
					     render.innerHTML = "Total de tarefas " +item.total;
					     render.setAttribute('data-bb-img','imgs/tag.png');
					     render.addEventListener('click',function(){
					     	bb.pushScreen('Tarefas.html','TarefasScreen',{listaid:$(this).data('itemid')});
					     });
					     response.push(render);
 
				  }document.getElementById('myLists').refresh(response);// voodoo!
	}
	,
	addNewTarefa : function (name){
		try{
 
			this.db.transaction(
				        function (transaction) {
				        var demoData = new Date();	
				        	transaction.executeSql('INSERT INTO tarefas (tarefa_nome,data_criada,lista_id) VALUES (?,"'+demoData+'",'+Db.currentList+')',[name],Db.onSucess, Db.errorHandler);
				        });	
 
			bb.pushScreen('Tarefas.html','TarefasScreen',{listaid:Db.currentList});
		}catch(e){
			printMsg();
		}			
	},
	getTarefas : function (id){
		try{
			Db.currentList = id;
			this.db.transaction(
				        function (transaction) {	
				        	transaction.executeSql('SELECT * FROM tarefas WHERE lista_id = ?',[id],Db.onTarefaSucess, Db.errorHandler);
				        });	
 
 
		}catch(e){
			printMsg();
		}
	},
	onTarefaSucess : function (e,result){
		var response = [];
				var len = result.rows.length, i;
				var render;
				  for (i = 0; i < len; i++) {
				   var item = result.rows.item(i);
				         render =  document.createElement('div');
					     render.setAttribute('data-bb-type','item');
					     render.setAttribute('data-bb-title',item.tarefa_nome);
					     render.setAttribute('data-itemId',item.id);
					     render.innerHTML = "Total de tarefas " +item.data_criada;
					     render.setAttribute('data-bb-img','imgs/bookmark.png');
					     $(render).swipeRight(function (){Db.deleteTarefa(item.id);});
					     response.push(render);
 
				  }document.getElementById('myTarefasList').refresh(response);// voodoo!
	},
	errorHandler: function (transaction, error){
		console.log(error);
		 	if (error.code==1){
		 	} else {
			    console.log('Oops.  Error was  '+error.message+' (Code '+error.code+')');
		 	}
		    return false;
	},
	onSucess: function(e,result){
	 return result;
	},
	printMsg : function (){
			var msg = "Can't open Database" + ' Error: ' + e.error;
			alert(msg);
			console.log(msg);
	},
	toast : function (msg, timeout){
 
		  if(typeof timeout == 'undefined') {
		    timeout = 1000;
		  }
 
		  // toast options
		  options = {
		    timeout: timeout
		  };
 
		  // display the toast message
		  toastId = blackberry.ui.toast.show(msg, options);
	}
}

Como cada função é bem específica para o exemplo,à medida que eu for implementando os métodos eu vou colocando na classe.

O banco de dados é hiper simples, veja a estrutura.

db-todolist

E aqui está o script para você executar.

CREATE TABLE IF NOT EXISTS "listas" (
"id"  INTEGER,
"lista_nome"  TEXT NOT NULL,
"data_criada"  TEXT NOT NULL,
PRIMARY KEY ("id")
);
CREATE TABLE IF NOT EXISTS "tarefas" (
"id"  INTEGER,
"tarefa_nome"  TEXT,
"cor_tarefa" TEXT,
"completa"  REAL,
"data_criada"  TEXT,
"lista_id"  INTEGER,
PRIMARY KEY ("id"),
CONSTRAINT "lista_id_fk" FOREIGN KEY ("lista_id") REFERENCES "listas" ("id") ON DELETE RESTRICT ON UPDATE RESTRICT
);

Em fim, até ai já fizemos a persistência, falta agora só navegar, e para simplificar, eu criei uma classezinha em literal para fazer o trabalho.

var navigatorController =  {
	openMain : function (){
		 var webworksreadyFired = false;
            document.addEventListener('webworksready', function(e) { 
               if (webworksreadyFired) return;
                webworksreadyFired = true;
                bb.init({actionBarDark: true,
                        controlsDark: false,
                        listsDark: false,
                        bb10ForPlayBook: false,
                        highlightColor: '#C82C29',
                        onscreenready : function(element,id,params){
                        	if(id == "ListaScreen")
                        	{
                        		Db.getListas();
                        	}
                        	if(id == "TarefasScreen")
                        	{
                        		 Db.getTarefas(params.listaid);
                        	}
                        	if(id == 'NewTarefaScreen')
                        	{
 
                        	}
                        },
                        ondomready : function(element,id,params){
 
                        }
 
                        });
                bb.pushScreen('Main.html', 'MainScreen');
				Db.open();
            }, false);
	},
	openSobre : function (){
		bb.pushScreen('Sobre.html','SobreScreen');
	},
	openConfig : function (){
		bb.pushScreen('Config.html','ConfigScreen');
	},
	openListas : function (){
		bb.pushScreen('Listas.html','ListaScreen');
	},
	newLista : function (){
 
			bb.pushScreen('NovaLista.html','NewListScreen');
	},
	saveNewLista : function (){
			Db.addNewLista($('#listaInputName').val());
 
	},
	newTarefa : function (){
			bb.pushScreen('NovaTarefa.html','NewTarefaScreen',{mode:true});
	},
	saveNewTarefa : function (){
			Db.addNewTarefa($('#tarefaInputName').val());
	}
}

Pontos negativos

Coloque em prática seus conhecimentos em Javascript e CSS3, por que muita coisa você vai precisar criar do zero, alguns componentes como o datePicker por exemplo ou um Numeric Stepper, algo como um Slider horizontal de imagens, Item renders para itens da lista, nada que não seja possível, vai dar só mais um pouco de trabalho e não fica com aquela coisa de out-of-the-box (tudo pronto).

O próprio toolkit tem muito código da própria comunidade como por exemplo o iScroll que foi utilizado para navegar entre as telas e nas listas. O legal é você poder hacker o código.

A performance não é 100%, eu testei a geração de efeitos com o context do Canvas e realmente leva-se de 2 a 3 segundos para aplicar um efeito a uma imagem, coisa que em um desktop no Chrome por exemplo é instantaneo, provavelmente seja a versão do webkit que esteja sendo utilziada, acredito que isso só o tempo pode dizer se eles vão melhorar esse item específico ou não.

Condeirações finais

É um pouco trabalhoso usar o BBUIJS, mais o acabamento final do projeto, é fantástico, por que você tem a exata idéia de estar criando um App com aparência nativa do sistema OS e isso meu amigo leitor, é fantástico. Porém nada lhe impede de usar outros toolkits de inicalização.

Código fonte disponível no Github

O código fonte dessa App você pode fazer o Fork no Github, a licença do projeto é via licença Apache.

https://github.com/igorcosta/bbuijs-todolist-demo