2008-08-23 03:55:02
tags: 

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 27)

Para ver os artigos anteriores desta série, clique aqui.

Olá meus caros amigos. Depois das decepções do salto com vara, do pentatlo moderno, do hóquei na grama, do salto triplo, do badminton, do baseball, do basquete, do bmx, do badminton, do taekwondo, do karatê, do salto em altura, do revezamento 4x100m, dos 800m, do tiro, do nado sincronizado, da ginástica rítmica e artística, da luta greco-romana, luta livre, esgrima, do arco e flecha, do boxe, da canoagem, do cliclismo, do softball, do levantamento de peso, do tênis, do triatlon, do tênis de mesa, do remo, do handball, do trampolim e finalmente, do futebol, voltamos aqui pra falar do que o brasileiro realmente entende: OOP, o famoso esporte da programação orientada a objetos.

Nas últimas semanas estivemos vendo alguns recursos que já vem na caixa do PHP quando você compra: as bibliotecas SPL são um bom exemplo. Mas o PHP é igual às olimpíadas: quanto mais popular é a necessidade, mais chance de entrar na instalação padrão do PHP. E uma das funcionalidades mais importantes em qualquer sistema é a conectividade com sistemas de banco de dados.

Pra isso, os nossos amigos discípulos do Boerger desenvolveram uma camada de abstração chamada PDO. Pra gente entender o que é PDO, portanto, precisamos entender primeiro o que é uma camada de abstração, correto?

Muito bem, caros leitores, nesse momento preciso contar uma verdade dura para vocês: o mundo lá fora é feio e sujo. Sim, acreditem! Não existem padrões, cada programador reinventa a roda a cada dia para provar que é o melhor! Bancos de dados, por exemplo: eu que sou um desconhecedor completo do assunto conheço pelo menos 10 sistemas gerenciadores de banco de dados, todos eles muito bons e com características próprias bastante singulares. A única coisa que tenho certeza que todos têm em comum é a capacidade de executar alguns comandos SQL de forma similar.

Uma camada de abstração para sistemas de banco de dados faz com que seu código use os mesmos métodos para acessar informações em qualquer um dos bancos de dados disponíveis. Assim, você poderia até mudar de sgbd sem ter que mudar seu código, e tudo funcionaria perfeitamente.

Claro que alguns bancos de dados não fazem as mesmas coisas que outros; alguns são mais poderosos, com mais possibilidades, outros mais simples. Portanto, uma boa camada de abstração tenta disponibilizar somente as funcionalidades que podem ser utilizadas de forma universal, ou, em alguns casos, emular as funcionalidades mais avançadas que não estão disponíveis nos bds mais simples.

Enfim, vocês sacaram?

A sigla PDO significa PHP Data Objects. Com os tais PDOs a gente tem uma forma padronizada e orientada a objetos para acessar os bancos de dados mais legais do planeta. E ganha de brinde um monte de funcionalidades interessantes. Veja um exemplo de conexão:

<?php
try {
    $meuObjetoPDO = new PDO('mysql:host=localhost;dbname=teste', $usuario, $senha);
    foreach($meuObjetoPDO->query('SELECT * from produtos') as $produto) {
        print_r($produto);
    }
    $meuObjetoPDO = null;
} catch (PDOException $e) {
    print "Deu zebra: " . $e->getMessage() . "
";
    die();
}
?>
    

 

O código acima abre uma conexão MySql e faz um select na tabela Produtos, que retorna um objeto PDO com os dados e um monte de outras propriedades. Qual a grande vantagem? Lembra como a gente fazia para acessar um bd em php? Tinhamos que usar os comandos Mysql:

<?php
$minhaconexao = mysql_connect('localhost', 'usuario', 'senha')
    or die('Erro de conexão: ' . mysql_error());
echo 'Beleza! Conectado!';
mysql_select_db('teste') or die('Não consegui selecionar o bd...');

// Performing SQL query
$query = 'SELECT * FROM produtos';
$resultado = mysql_query($query) or die('Erro na Query: ' . mysql_error());

// Printing results in HTML
while ($produto = mysql_fetch_array($resultado, MYSQL_ASSOC)) {
print_r($produto);
}

// Free resultset
mysql_free_result($resultado);

// Closing connection
mysql_close($minhaconexao );
?>
    

Até aí, já dá pra ver que usar PDO é mais fácil, mas ainda está faltando o nocaute: seu chefe chega pra você e diz, "ô fulano, vai mudar o bd, troca aí pra Oracle!".

Usando PDO, vc simplesmente troca a primeira linha e tá tudo pronto. No segundo caso... prepare-se para mudar um milhão de linhas.

Abraço grande e até semana que vem!

2008-08-16 04:18:50
tags: 

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 26)

Para ver os artigos anteriores desta série, clique aqui.

Ah, as olimpíadas. Atletas abnegados que se dedicaram durante anos para tentar a glória. Toda uma vida de treinos e sacrifícios é colocada à prova em frações de segundos. Um mísero erro, e toda a preparação vai por água abaixo.

A vida destes atletas, meus caros, guardadas as devidas proporções de massa muscular e gordura corporal, é parecida com a vida dos programadores. Um mísero erro, e todo o banco de dados vai pro saco. Um segundo a mais de performance perdida, e o servidor não aguenta o tranco. Portanto, meus atléticos leitores, ao celebrar as olimpíadas e virar a noite para ver aquele jogaço de hóquei na grama feminino não se esqueça: parte do solo sagrado do olimpo também é sua!

Ruminâncias sem nexo à parte, hoje quero mostrar mais uma jogada da biblioteca SPL pra vocês. Lembram-se do nosso carrinho de compras? Bem, respeitáveis atletas, preciso dizer que ele não aguentou a pressão na final e se enrolou todo. Explico: do jeito que construímos este carregador de objetos, ele falha miseravelmente se a gente simplesmente incluir o mesmo objeto mais de uma vez. Experimente. Duplique a linha abaixo e veja o carrinho se comportar de forma muito estranha, listando o mesmo item duas vezes. Cartão vermelho.

Pra dizer a verdade, já existe um cara na SPL que faz essa função de carrinho de compras (e, claro, de qualquer outro conteiner de objetos que você quiser) de forma muito mais esperta. É a classe SplObjectStorage.

Uma coisa que essa classe faz é exatamente controlar os objetos que nela são guardados para evitar que o mesmo objeto seja incluído duas vezes. Mais do que isso, ela controla também updates; se você alterar o objeto e adicionar novamente à coleção a classe automaticamente atualiza os dados pra você. Ippon!

Veja só que beleza:


class CarrinhoDeCompras extends SplObjectStorage {

    // nosso carrinho de compras é, no fundo, um array de objetos da classe Item...
    private $carrinho = array();
    
    //coloca um Item no carrinho
    //usando type hinting, viu o Item antes da variavel? Isso diz que essa função só aceita objetos da classe Item...
    
    public function adicionaItem(Item $item){
    
     $this->attach($item);
    
    }
    
    //detona tudo no carrinho
    
    public function esvaziaCarrinho(){
    
     unset ($this->storage);
    
    }
    
    // ué cade o resto todo? Não precisa cara. A classe SplObjectStorage já traz tudo de brinde: ArrayAccess, Iterator e muito mais!
    
// uma inclusão boa pra nos aqui: que tal colocar a logica de impressao do carrinho logo aqui?
    public function __toString(){

     foreach($this as $item) { // <- $meucarrinho é um array? é um objeto? como???
        $string .= "
" . "Código: " . $item->getCodigo() . " Descrição: " . $item->getDescricao() ." Preço: " . $item->getPreco() . " Quantidade: ". $item->getQuantidade();
        $total += $item->getPreco() * $item->getQuantidade();
     }

     $string = $string . "
" . "O total da sua compra é: " . $total;
    
     return "
" . $string . "
";
    }


}


class Item {

    private $codigo;
    private $descricao;
    private $preco;
    private $quantidade;
    
    public function __construct($codigo,$descricao,$preco,$quantidade){
     $this->codigo = $codigo;
     $this->descricao = $descricao;
     $this->preco = $preco;
     $this->quantidade = $quantidade;
    }
    
    public function getCodigo(){
    
     return $this->codigo;
    
    }
    
    public function getDescricao(){
    
     return $this->descricao;
    
    }
    
    public function getPreco(){
    
     return $this->preco;
    
    }

    public function getQuantidade(){
    
     return $this->quantidade;
    
    }
    
    public function setQuantidade($quantidade){
     $this->quantidade = $quantidade;    
    }


}

//Muito bem, agora vamos la!


$meucarrinho = new CarrinhoDeCompras();

//aqui, pra provar nosso ponto, vamos ter que instanciar os objetos em variaveis pra poder tentar adicionar novamente o mesmo objeto

$item1 = new Item('1','Caneta Bic Azul','1.00','10');
$item2 = new Item('2','Caneta Bic Vermelha','1.50','5');
$item3 = new Item('3','Caneta Bic Quatro Cores','3.50','15');

$meucarrinho->adicionaItem($item1);
$meucarrinho->adicionaItem($item2);
$meucarrinho->adicionaItem($item3);


print $meucarrinho;

// agora a prova!

$item1->setQuantidade(30);
$meucarrinho->adicionaItem($item1);

print $meucarrinho;


?>

É isso aí pessoal! Até a próxima!

2008-08-09 03:58:25
tags: 

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 25)

Para ver os artigos anteriores desta série, clique aqui.

Respeitável público! Senhoras e Senhores! Rapazes e Raparigas de todo o Brasil! Com vocês, no palco do Circo Orientado a Objeto Ringling Bros. PHP, o fantástico mago transformador de objetos, o rei das interfaces spl, o grande Rasmussen! Vejam como ele transforma qualquer objeto em um array com apenas algumas linhas de código!


class CarrinhoDeCompras implements ArrayAccess, Countable {

    // nosso carrinho de compras é, no fundo, um array de objetos da classe Item...
    private $carrinho = array();
    
    //coloca um Item no carrinho
    //usando type hinting, veja o Item antes da variavel $item abaixo. Isso diz que essa função só aceita objetos da classe Item...
    
    public function adicionaItem(Item $item){
    
     $this->carrinho[] = $item;
    
    }
    
    //detona tudo no carrinho
    
    public function esvaziaCarrinho(){
    
     unset ($carrinho);
    
    }
    
    /*
    Aqui entram as funções obrigatórias da interface ArrayAccess e Countable.
    ArrayAccess: offsetExists, offsetGet, offsetSet, offsetUnset
    Countable: count
    Lembre-se: as interfaces exigem que voce use exatamente estes nomes para as funções!
    */


    //Existe o item numero $indice? offsetExists responde:

    public function offsetExists($indice) {

     return isset($this->carrinho[$indice]);

    }


    //Me dá ai o ítem $indice:

    public function offsetGet($indice){

     return $this->carrinho[$indice];

    }


    //Muda o item numero $indice para esse item aqui ó

    public function offsetSet($indice,$item) {

     if ($indice){

        $this->carrinho[$indice] = $item;

     }

     else {

        throw new Exception('Precisamos do indice para inserir o item no lugar certo...');

     }
    }
    
    
    // detona o item número $indice
    
    public function offsetUnset($indice) {
    
     unset($this->carrinho[$indice]);
    
    }
    
    
    // conta o numero de itens (obrigatório pela interface countable)
    
    public function count(){
    
     return count($this->carrinho);
    
    }
    
}


class Item {

    private $codigo;
    private $descricao;
    private $preco;
    private $quantidade;
    
    public function __construct($codigo,$descricao,$preco,$quantidade){
     $this->codigo = $codigo;
     $this->descricao = $descricao;
     $this->preco = $preco;
     $this->quantidade = $quantidade;
    }
    
    public function getCodigo(){
    
     return $this->codigo;
    
    }
    
    public function getDescricao(){
    
     return $this->descricao;
    
    }
    
    public function getPreco(){
    
     return $this->preco;
    
    }

    public function getQuantidade(){
    
     return $this->quantidade;
    
    }


}

//Muito bem, agora vamos la!


$meucarrinho = new CarrinhoDeCompras();
$meucarrinho->adicionaItem(new Item('1','Caneta Bic Azul','1.00','10'));
$meucarrinho->adicionaItem(new Item('2','Caneta Bic Vermelha','1.50','5'));
$meucarrinho->adicionaItem(new Item('3','Caneta Bic Quatro Cores','3.50','15'));

//Agora, caros amigos, a mágica:

$total = 0;
        for($i=0; $i<count($meucarrinho); $i++) { //<- $meucarrinho é um array? é um objeto? como???
         print "Código: " . $meucarrinho[$i]->getCodigo() . " Descrição: " . $meucarrinho[$i]->getDescricao() ." Preço: " . $meucarrinho[$i]->getPreco() . " Quantidade: ". $meucarrinho[$i]->getQuantidade();
         $total += $meucarrinho[$i]->getPreco() * $meucarrinho[$i]->getQuantidade();
}

print "O total da sua compra é: " . $total;

?>

Muito bom, não? Mas antes que vocês saiam por aí contando pra todo mundo que descobriram como se faz o truque, experimentem usar este objeto dentro de um foreach. Não rolou, certo? Você tem alguma idéia do porque? Muito bem, caro aprendiz! Você precisa implementar a interface Iterator, que falamos na semana passada, para que este objeto realmente se transforme em um Array completo!

Até semana que vem!


2008-08-02 02:52:05
tags: 

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 24)

Para ver os artigos anteriores desta série, clique aqui.

Caríssimos amigos, aqui estamos de volta. Na nossa última conversa, começamos a falar destes fantásticos seres mitológicos, os Iterators. Espero que nossos exemplos tenham ajudado a entender o conceito básico por trás deste Design Pattern tão importante.

Mas agora chegou a hora de aproveitar. Nosso caro Sr. Marcus Boerger, quando criou as extensões SPL, nos deu muita coisa de brinde. Não só temos agora interfaces padronizadas para um grande número de implementações de patterns, como ganhamos também uma série de classes prontas para usar. E quando falamos de iterators, temos muita coisa boa, meus amigos! Veja o exemplo abaixo:

 
	try{
$file = new SplFileObject( "/digitalminds/texto.txt" );
$numlines = 0;
foreach ($file as $line){
$numlines ++;
}
$linenum = rand(1,$numlines);
$file->seek($linenum);
//essa linha vai ser enviada por email
mail('danilo@digitalminds.com.br', 'Assunto aqui!', $file->current());
} catch (Exception $e) {
echo $e->getMessage();
}
?>

 

Veja a beleza do que está acontecendo: ao instanciar o objeto SplFileObject, tenho prontinho um iterator que posso jogar diretamente num foreach para contar as linhas, imprimí-las, ou, usando a função $file->seek() posso pegar uma linha específica do arquivo e, no caso particular acima, enviá-las por email pra mim mesmo.

Vocês repararam que eu nem sequer pedi pra abrir o arquivo? Tá tudo pronto. E tem muito mais: esta classe tem quase 100 outros métodos que servem pra escrever no arquivo, ver as propriedades, pegar o diretório no qual ele está... experimente fazer nesse exemplo aí de cima um

print_r(get_class_methods($file) );

pra ver todos os métodos disponíveis. Ou, clique aqui e desça o scroll até a lista de funções.

E não é só isso, meus caros: vocês conseguem imaginar o que as classes DirectoryIterator, RecursiveDirectoryIterator, SimpleXMLIterator e SplFileInfo podem fazer? E que todas elas, ao serem instanciadas, te dão um objeto que funciona direitinho em um foreach ou um while? Entenderam o lance?

Muito bem. Divirtam-se com seus novos amigos. Na semana que vem, vamos aprender como transformar qualquer objeto em um array. Abraços a todos.

 

2008-07-26 04:50:57
tags: 

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 23)

Para ver os artigos anteriores desta série, clique aqui.

Ah, os iterators. Iterators, meus amigos. Como vários outros conceitos desta enigmática arte da programação orientada a objeto, os iterators são quase incompreensíveis para gente como eu - cujo único treinamento foi nas ruas, entre códigos incompreensíveis, brigas de puxar cabelo e gambiarras que mais pareciam aquelas geringonças que os vilões de desenho animado inventam para eliminar seus inimigos (quase sempre sem sucesso, é claro).

Ainda não posso dizer que entendo os iterators em toda sua grandeza. Seus significados ocultos ainda me deixam perplexo de tempos em tempos. Mas que exercícios intelectuais fascinantes eles podem nos proporcionar! Que enigmas incríveis eles nos reservam, que tesouros escondidos!
Se você ainda está lendo, acho que consegui prender sua atenção com estas baboseiras. Vocês jovens precisam de um pouco de nonsense pra prestar atenção nas coisas. Enfim. Iterators.
Pra resumir: Iterators "pegam" uma coleção de objetos e permitem que você faça operações com cada um eles. Vou dar um exemplo, porque esse conceito sempre foi pra mim muito difícil de entender. Digamos que você seja um comerciante libanês. Na sua lojinha, que fica ali na S.a.a.r.a., você vende tudo, desde açafrão em pó até relógio digital. Depois de comer 15 esfihas, folha de uva, coalhada seca, arroz com lentilha e 3 caftas no Cedro do Líbano, você resolve fazer uma contagem do estoque. Sabe como é, nesses dias de hoje, não dá pra confiar. Aí você chama o seu gerente de eletrônicos, o Sr. Salim e fala o seguinte:

- Ô Salim! Pra cada relógio aí do estoque faz o seguinte: vê se o ponteiro tá mexendo, depois vê se tá arranhado e aí coloca na caixinha de novo. Depois você me diz quantos estão bons!

Muito bem. Qual é o grande problema deste approach, caro Amir? Qualquer comerciante libanês sabe - se eu tenho que dizer tudo o que o Salim tem que fazer, pra que diabos eu tenho um gerente? E se eu tiver 15 gerentes, imagina o tempo que eu vou perder pra explicar pra cada um como ver se o produto está bom?
O que você quer, meu caro amigo mercador, é perguntar quantos estão bons, e o seu gerente te responder, independentemente de qual linha de produtos ele gerencie. Sem que você precise explicar tudo toda hora.
Deu pra sacar a diferença? Agora, um exemplo muito prático que todo mundo usa pra explicar a utilidade dos iterators em php:
Imagine que você tem um site que pega dados de três tipos de fontes diferentes: arrays, banco de dados e diretórios no servidor. Assim:


/* array */
foreach ( $relogios as $relogio) {
     // faz alguma coisa com $relogio
}

/* MySQL */
while ( $tempero = mysql_fetch_array($result) ) {
     // faz alguma coisa com $tempero
}

/* Diretorio */
while ( false !== ($brinquedo = readdir($dir)) ) {
     // faz alguma coisa com $brinquedo
}

?>

Olha para esses caras aí em cima. Não dá pra sentir que tem alguma coisa parecida entre eles? Então, imagine que você quer pegar o nome de cada um desses caras aí em cima e imprimir na tela. Isso mesmo, você quer pegar o nome tanto dos relógios que estão no array, quanto dos temperos que estão no banco de dados e ainda dos brinquedinhos que são arquivos em um diretório no servidor.
O que você faz, meu caro? Coloca dentro de cada loop um print()? De jeito nenhum! Imagina se amanha você quiser mudar o formato da impressão, vai ter que mudar 3 vezes! Ou pior, imagine se a sua loja tiver 469 tipos de produto, o que é bem comum nessas lojas - Vai colocar 469 prints? Ah, já sei - você pensou que pode simplesmente fazer uma função para imprimir e colocar dentro de cada loop! Cuidado! meu caro, você está flertando com o diabo! Você está se afastando do sagrado caminho da OOP! Comece a colocar código que não é realmente a função da classe dentro dela e em breve você estará deitado numa cama de pregos, tostando lentamente no fogo do inferno!
A solução, meus caros amigos, é muito bonita. Senhores, apresento-lhes os Iterators!

$itens['brinquedos'] = new MeuDirectoryIterator($briquedos_diretorio); // aqui a fonte de dados é um diretório
$itens['relogios'] = new MeuArrayIterator($relogios_array); // aqui a fonte de dados é um array
$itens['temperos'] = new MeuMysqlIterator($temperos_query); // aqui a fonte de dados... ah, você já entendeu.

foreach($itens as $iterator){
    
     foreach($iterator as $item){
         print $item->getName();
     }

}
?>

Não é fantástico? Que magia existe nessas classes que permite tanta padronização? Vocês entenderam o que está acontecendo? Eu não preciso me preocupar com a forma dos dados! Vale tudo! Diretórios, arrays, resultados do Mysql, e se a gente quisesse, poderíamos incluir aí XML, YAML, enfim, vale tudo! Como assim! Isso é genial! Nunca mais vou repetir código novamente! No próximo capítulo da nossa série, vamos construir as classes MeuDirectoryIterator, MeuArrayIterator e MeuMysqlIterator e conhecer em detalhes este sensacional truque. Até lá, caro Amir!
2008-01-09 11:15:42

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 22)

                

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 22)

Para ver os artigos anteriores desta série, clique aqui.

Olá amigos. Estamos de volta depois de muita farofa, bacalhau, doce de leite, maionese, pamonha, queijo cavalinha, uma nova geladeira que não chega, livros e muita alegria. Espero que todos tenham passado dias maravilhosos nesse fim-de-ano e que possam ter aproveitado esses dias pra curtir a família e os amigos!

Mas chegou a hora de voltar para o batente. Antes que vocês comecem a mandar os já tradicionais emails "Cadê o 22! Queremos o 22!" eu já estou escrevendo mais um capítulo desta série singela, que procura explicar os conceitos mais enrolados de orientação a objetos em uma linguagem que todos aqui possam facilmente entender. E, pra ser bem honesto, os emails cobrando o 22 na verdade já começaram a chegar, então a hora é essa. Não estou reclamando de vocês de forma alguma, caros leitores! A pressão ajuda! Obrigado a todos que cobram, que agradecem, que mandam correções e puxões de orelha, sua contribuição faz disso aqui um lugar melhor.

Ah, o Natal. Que loucura que é o Natal. Falar sobre oop aqui no Digitalminds sempre é divertido porque eu tenho que bolar metáforas interessantes pra que o assunto não fique chato no primeiro parágrafo.  Então, como não poderia deixar de ser, o artigo de hoje está em clima de Natal. Não, eu não estou maluco, eu sei que o Natal já passou. Mas a segunda semana de janeiro não tem muita coisa interessante pra nos oferecer, a não ser que você conte o Dia de Reis.

O DIA DE REIS! É isso! Meu Deus como pude esquecer! Muito bem, senhores, esqueçam o natal. É hora de falar de Belchior (ou Melchior), Gaspar e Baltazar.
Não, senhores, não estamos falando de Belchior, nem de Gaspar, ou mesmo de Baltazar. Estamos falando de três magos persas, sacerdotes de Zaratrustra, homens sábios que conheciam profundamente a astrologia e que vislumbraram um sinal - na noite do dia 25 de dezembro do ano 1, souberam pelo brilho da estrela de Belém que um Rei nascera. E para comprovar que a wikipedia é sempre uma diversão, veja este parágrafo:

A melhor descrição dos reis magos foi feita por São Beda, o Venerável (673-735), que no seu tratado “Excerpta et Colletanea” assim relata: “Melchior era velho de setenta anos, de cabelos e barbas brancas, tendo partido de Ur, terra dos Caldeus. Gaspar era moço, de vinte anos, robusto e partira de uma distante região montanhosa, perto do Mar Cáspio. E Baltazar era mouro, de barba cerrada e com quarenta anos, partira do Golfo Pérsico, na Arábia Feliz”.

Uau. Sábios, caldeus, Ur, Pérsia. Agora que consegui a atenção total e irrestrita de vocês, futuros reis magos da OOP, e cobri todos de Incenso e Mirra, é hora de falar de uma classe importantíssima que ficou faltando no nosso grande esquema das coisas: a venerável classe ModelCollection.

Lembram que optamos por simplificar as funcionalidades da classe Model, fazendo com que ela realizasse operações somente em registros únicos no banco de dados? Muito bem, senhores, a classe ModelCollection aparece para nos salvar nos casos em que precisamos trabalhar com conjuntos de registros. Aqui é bom repetir o aviso: esta implementação é diferente da utilizada nos frameworks que utilizam o pattern ActiveRecord, como o Cake. Nestes frameworks todas as operações com registros e conjuntos de registros são feitas pela classe Model e suas subclasses. Assim, um objeto da classe model típico destes frameworks tem funções tanto para buscar, editar, salvar e deletar um registro quanto para buscar conjuntos de registros. Isso faz com que o objeto da classe que estamos usando não tenha um mapeamento direto com os dados que estão no banco. Isso me incomoda muito, pois eu gosto de pensar no objeto como uma coisa, uma representação virtual dos dados que estão no banco. Pra mim é estranho usar o modelo para retornar uma lista de registros. A nossa implementação vai claramente mapear um registro no banco a um objeto da classe correspondente, e um conjunto de registros a uma coleção de objetos da classe correspondente. Exemplo: nossa classe Post. Teremos no banco a tabela Posts, com os campos title e text, por exemplo. Para acessar e alterar  o campo de título de um post em nosso framework vamos seguir os seguintes passos:

  1. Criar um novo objeto da classe Post fazendo $post = new Post();
  2. Carregar os dados do registro que queremos alterar com id número $id  usando $post->loadById($id);
  3. Mudar o título usando $post->setTitle("Novo Título!");
  4. Salvar o post com $post->save();
Para criar um novo post, é simples:
  1. Criar um novo objeto da classe Post fazendo $post = new Post();
  2. Mudar o título usando $post->setTitle("Título do Novo Post!");
  3. Mudar o texto usando $post->setText("Texto do Post!");
  4. Salvar o post com $post->save();
Nessa implementação vamos poder acessar as variáveis do objeto usando getters normalmente, por exemplo, $post->getTitle(); Podemos também criar vários atalhos: o construtor do Post pode receber  o Id diretamente e já carregar os dados automaticamente. Podemos implementar a interface ArrayAccess para permitr coisas como print $post['title'] sem nenhuma dificuldade. Enfim. A coisa é boa. Aguardem que vem coisa ótima por aí.
Mas até aí, nenhuma novidade. O que muda é a forma de trabalhar com conjuntos de dados. No Cake, esse trabalho seria feito por funções na classe Post que retornariam Arrays contendo vários ítens. E isso deixa tudo confuso, no final das contas. O que proponho é o seguinte:
  1. Criar um novo objeto da classe PostCollection fazendo $collection = new PostCollection();
  2. Carregar o conjunto de registros usando uma das várias funções disponíveis, por exemplo $collection->loadAll();
Muito simples. E agora temos um objeto da classe PostCollection carregado de registros, com métodos super úteis para manipular os dados, paginar, enfim, uma belezinha. E vamos implementar uma coisa sensacional: a interface iterator, disponível na fantástica biblioteca SPL, proveniente de UR, na Pérsia! Mas isso já é assunto para o próximo episódio. Até lá!


2007-12-04 02:20:34

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 21)

Para ver os artigos anteriores desta série, clique aqui.

Olá amigos. Em nosso último encontro finalizamos nossa primeira análise das classes principais do MVC: Model, View e Controller. Sei que muitos de vocês estão ansiosos para começar a codificar mas ainda temos bastante mufa pra queimar antes de começar a digitar código php!

Bom, pra começar, um pouco da história do cinema: quem não se lembra de Sean Connery como o policial irlandês em Os Intocáveis ? E de Kevin Spacey, como o bobalhão "Verbal" em Os Suspeitos ? E de Gene Hackman como o xerife mau, muito mal de Os Imperdoáveis ? Muito bem, o que esses três papéis interpretados por estes grandes atores têm em comum? As três interpretações foram premiadas com Oscars de melhor ator coadjuvante. E por quê todo este papo cinematográfico? É que hoje vamos falar de uma classe coadjuvante que é fundamental em nosso quase-framework: nosso amigo Dispatcher.

Muito bem, como já falamos algumas vezes, nosso framework é bastante esperto: ele vai permitir que acessemos métodos de nossos controllers simplesmente interpretando uma URL. Por exemplo, para acessar o método view() do controller BlogPost para ver o post com o id 19 nossa url seria

http://nossoblog.com.br/blogpost/view/19

Ok, mas quem transforma uma coisa na outra? Quem chama o que? Como isso funciona na prática?

Senhores, eu vos apresento a classe Dispatcher, encarregada de interpretar as urls de nossa aplicação e executar os métodos apropriados nos controllers corretos. Ela também vai checar se o controller e o método requisitados realmente existem, e vai apresentar mensagens de erro em caso negativo. Por fim, a classe vai separar corretamente os parametros do request, enviando um array já certinho para o método que foi chamado.


Uma outra função muito interessante de nossa classe Dispatcher vai ser interpretar extensões; sim, amigos, vamos fazer com que a extensão definida na url defina o layout e o template a ser utilizado, seguindo uma estrutura de arquivos pré-determinada. Exemplo: se ao invés da url acima tivessemos alguma coisa como

http://nossoblog.com.br/blogpost/view/19.xml

Nosso framework, ao invés de utilizar o layout em /layouts/default/blogpost/view.php e o template /templates/default/blogpost/view.php utilizaria o layout /layouts/xml/blogpost/view.php e o template /templates/xml/blogpost/view.php. Deu pra sacar o poder? Assim vc pode criar outputs diferentes pra toda a sua aplicação sem nenhum estresse. Bonito demais. Pra colocar o sistema disponível em wap, bastaria fazer um layout e um template, no caso /templates/wml/view.php e /layouts/wml/view.php e utilizar os códigos wml ao invés de html dentro deles. Eu adoro isso.

Você acaba de perceber a beleza de uma solução MVC. A lógica fica totalmente separada da apresentação, o que faz com que seja fácil fazer apresentações totalmente diferentes com o mesmo conteúdo. Sweet!

Vamos então à descrição da classe?

Class Dispatcher


Propriedades

$url

String. Propriedade que irá conter a url requisitada.


$controller

Objeto do tipo Controller. Esta propriedade irá conter uma nova instância do controller requisitado. No nosso exemplo, BlogPost.

$method

String. Método a ser executado no controller requisitado. O método será view(), no nosso exemplo.


$parameters

Array com os parâmetros enviados na requisição via get ou post. No exemplo acima, ele teria uma entrada $parameters["default"]=>"19" já que não nomeamos este parâmetro no request. Poderíamos, se nós fossemos chatos, ter montado a url assim:

http://nossoblog.com.br/blogpost/view/id/19


e aí nosso array seria montado na forma $parameters["id"]=>"19"

O framework vai ser esperto o suficiente pra perceber quando temos só o valor ou o par nome/valor e vai resolver tudo isso pra nós.

Métodos


parseURL()

O método parseURL() interpreta uma URL segundo os padrões combinados e popula as propriedades do nosso Dispatcher. Na prática, ele vai separar os pedacinhos da url, dividindo a string em partes. Em primeiro lugar, ele remove a string relativa ao protocolo (http:// ou https://). Depois, o domínio. Ficamos então com a estrutura

controller/método/parametros.extensao

Aí fica facil separar tudo, não?


parseParams()
O método parseParams() faz o mesmo que a função acima, só que para os parâmetros enviados por get e post. Tudo o que estiver entre um ? e o ponto da extensão será considerado parâmetro get. Também será considerado parâmetro get tudo o que vier depois da action requisitada, no caso de nossos exemplos acima "19" e "id"=>"19". Tudo o que vier no $_POST vai ser incluído também em nosso array $parameters.

Repare que pra simplificar as coisas não vamos considerar a diferença entre get e post em nosso array $parameters; será igual fazer um request por get e post. Juntaremos tudo no array, sem distinção.

2007-03-16 10:11:23

Mais uma jóia da Web 2.0: Gliffy

Gliffy é uma ferramenta web para desenho de diagramas e gráficos de forma colaborativa. Muito interessante. Confira no endereço http://www.gliffy.com.
2007-02-26 07:06:40

Produção Web: Subverta sua forma de trabalhar com arquivos

Vamos fazer um teste rápido: você montou uma página html muito legal, o css já está fechado, tudo está validando, acessível, etc. e tal. Nesse exato momento, o seu cliente liga e faz um pedido infame:    

— Olha, vamos precisar colocar esses 15 logos na página, mas precisamos que você coloque um em cada parágrafo, ok?

Bom profissional que você é, aceita resoluto a solicitação do cliente e insere uma a uma as imagens na página, transformando a sua linda criação em uma quase-árvore-de-natal feita de símbolos e logotipos que não têm absolutamente nada a ver uns com os outros. Pra que a coisa fique minimamente interessante, você perde mais 45 minutos ajustando o css, mexendo no tamanho das imagens, enfim, fazendo o rejunte e o acabamento.

Considerações estéticas à parte, a página eventualmente fica pronta e você pede a aprovação do cliente. Então, caro amigo, o inesperado acontece:

— Olha, ficou poluído demais. Pode voltar atrás.

O que?! Voltar atrás?

É aqui que entra nosso teste: nessa momento de tensão, caro leitor, o que você faria:

Opção 1: aceita, emburrado, e recomeça de imediato a corrigir o código, removendo uma a uma cada modificação feita. Depois, precisa revisar tudo novamente pra ver ser não ficou nenhum erro, se o layout está igual ao original (mas peraí, eu detonei o original!)

Tempo total: 30 minutos.

Opção 2: aceita, levemente aborrecido, e começa a procurar os arquivos de backup que você havia feito antes de alterar os arquivos. Depois, precisa revisar tudo novamente pra ver se realmente tudo está certo, se aquele “salvar como” foi feito com todas as últimas alterações, se aquela modificação de texto que a menina pediu por telefone estava incluída.

Tempo total: 7 minutos.

Opção 3: aceita, com um sorriso cativante no rosto, e clica com o botão direito do mouse no ítem “TortoiseSVN->Exibir histórico”, escolhe a revisão anterior, e clica com o botão direito novamente “Reverter para essa revisão”. Pronto.

Tempo total: 5 segundos.

Muito bem, meu amigo, minha amiga, o que este teste mostra para nós? Se você respondeu opção 1 ou opção 2 você precisa de um sistema de controle de versão. Fato.

Um sistema de controle de versão guarda diferentes versões de um mesmo documento em um repositório e é capaz de fazer operações com elas. Assim, é possível não só voltar a uma versão anterior de um arquivo instantaneamente, mas também comparar e ver exatamente as diferenças entre duas versões, linha a linha. Mais que isso, duas ou mais pessoas podem trabalhar ao mesmo tempo em um arquivo texto, combinando as partes alteradas quando terminarem. Não é feitiçaria, é tecnologia!

Existem várias soluções para controle de versão disponíveis no mercado. Uma das mais populares é a chamada Subversion, ou simplesmente svn.

O Subversion usa uma interface de linha de comando, mas graças à bondade da comunidade open-source já foi desenvolvida uma interface gráfica para Windows chamada TortoiseSVN que é bem mais fácil de usar. Faça o download da aplicação e da tradução para o português brasileiro no endereço

http://tortoisesvn.net/downloads

Observação: Para quem usa mac, existe o SvnX. É um pouco confuso, mas talvez ajude:

http://www.apple.com/downloads/macosx/development_tools/svnx.html

Depois de instalar o TortoiseSVN (será necessário reiniciar o Windows), você já pode criar seu primeiro repositório:

- crie uma pasta nova para conter seu repositório

- clique com o botão direito do mouse nessa pasta e escolha “TortoiseSVN->Criar repositório aqui”.

Pronto, já temos um repositório. Você não vai trabalhar nessa pasta: os repositórios svn guardam os arquivos num formato proprietário que não pode ser usado; Para trabalhar, é preciso fazer um “checkout” do repositório em outra pasta, ou, em português, “Obter” os arquivos do repositório:

- crie uma pasta para guardar seus arquivos de trabalho;

- clique com o botão direito e escolha “SVN Obter”

- Agora é necessário digitar no campo “URL do Repositório” a url do repositório que você criou. Como trata-se de um folder no sistema de arquivos, use o prefixo file:/// antes do caminho da pasta. Exemplo: file:///C:meurepositorio

- clique OK

Pronto. A partir de agora, os arquivos desta pasta poderão ser “Submetidos” clicando com o botão direito sobre ela e selecionando “TortoiseSVN->Submeter” . Uma janela se abrirá e você vai poder selecionar os arquivos que deseja submeter.

Dica importante: nunca apague ou renomeie um arquivo obtido de um repositório subversion pelo Windows; sempre use o menu do TortoiseSVN. Quando isso acontece, o banco de dados interno do subversion fica perdidinho e você pode ter algumas surpresas.

Para saber mais não deixe de ler o livro “Controle de Versão com Subversion”, que já está parcialmente traduzido para o português no endereço

http://svnbook.red-bean.com/

Nos próximos posts da série, vou mostrar como usar um repositório online do Dreamhost.

Divirtam-se!

2007-02-10 02:49:00

Produção Web: Ambientes de trabalho

Ao longo dos anos aprendi que uma boa estratégia é ter 3 ambientes distintos de trabalho: Produção, Validação e, finalmente, um ambiente de testes internos. O primeiro, intocável, é o ambiente onde os clientes acessam a versão do site atual. O segundo ambiente é o ambiente de validação, onde o cliente aprova as alterações feitas no site. O terceiro é o ambiente onde você testa as modificações antes de apresentar ao cliente.

Acredito que a coisa realmente importante desse approach é ter um ambiente separado de validação para o cliente. Assim você não corre o risco de "quebrar" o site enquanto ele revisa alguma outra coisa que você já fez.

Mas como fazer isso na prática? Um dos motivos da minha simpatia pelo Dreamhost é a possibilidade de criar subdomínios ilimitados. Assim, crio dois subdomínios, um de validação para o cliente e um de teste para mim. Exemplo:

  • http://cliente.digitalminds.com.br (ambiente de validação, url enviada para o cliente)
  • http://teste.cliente.digitalminds.com.br (ambiente de teste)

Para criar um subdomínio no Dreamhost é muito fácil: acesse o painel de controle, clique na opção "Domains", e em seguida no link "Add New Domain / Sub-Domain". O painel de controle vai configurar automaticamente o subdomínio pra você e já vai criar a pasta com o mesmo nome no seu diretório home. 

Trabalhe localmente e suba os arquivos para o ambiente de teste. Se tudo estiver bem lá, inicie o procedimento de atualização:

  1. Renomeie a pasta atual cliente.digitalminds.com.br para cliente.old (se alguma coisa inesperada acontecer é so voltar atrás)
  2. Duplique a pasta teste.cliente.digitalminds.com.br e renomeie para cliente.digitalminds.com.br

Tudo pronto, seu cliente já pode fazer a validação das mudanças. Assim você não interrompe o site de validação durante o upload e tem certeza de que tudo está funcionando no servidor.

Existem formas ainda mais bonitas de fazer este tipo de atualização, especialmente se você tem Subversion disponível. Mas aí é outra dica. Pra coisas simples isso funciona muito bem, e você ainda pode fazer um pequeno script bash pra automatizar tudo.

É isso, gente. Até a próxima. Para ver todos os artigos desta série, acesse http://www.digitalminds.com.br/tags/pw

2006-09-05 03:15:33
tags: 

Assine o Dreamhost sem pagar taxa de adesão

Pra quem ainda está procurando por um bom provedor recomendo enfaticamente o Dreamhost. Como já comentei anteriormente, tinha bastante gente que tinha gostado do Dreamhost mas que achava a taxa de setup muito alta. Aí lembrei que eu posso dar um desconto pra quem assinar usando o Digitalminds como referência. Então, como não estou aqui pra viver disso, resolvi descontar toda a taxa de setup da grana que eu receberia por cada referral. Ainda vou ganhar o suficiente pra pagar minha conta lá, então tá tudo certo.

Pra quem não conhece o Dreamhost, essas são algumas das características:

  • PHP 5
  • Bancos de Dados mysql ilimitados e acessíveis externamente
  • Criação de usuários FTP e SSH para clientes
  • 20 Gb de espaço em disco
  • 1 Tb de Transferência (Sim, 1 Terabyte)
  • dominios e subdomínios ilimitados
  • endereços de email ilimitados
  • acesso total via ssh (inclui crontab jobs para automatização)
  • .htaccess (mod rewrite permitido para o Apache, autenticação Digest)
  • suporte técnico (em inglês)
  • Instalação em 1 clique de Wordpress, PHP Gallery, PHPbb Fórum, MediaWiki, Joomla e Zencart
  • Servidor Jabber e Quicktime Streaming
  • e agora, servidores SVN ilimitados (Subversion)
  • Você ainda ganha um domínio (.com ou .net) totalmente grátis.    

Com a promoção, você paga US$ 10,95 no cartão de crédito por mês, sem taxa de adesão. Se pagar um ano de uma vez, eles não cobram a taxa, você paga US$9.95 e você ainda ganha o desconto, ficando com 5 meses grátis. Se quiser pagar 2 anos de uma vez o preço cai pra US$8.95 e você ainda ganha o desconto, ficando com 6 meses grátis. Vale a pena!

Mas cuidado: não tem suporte nem documentação em português. Se você está procurando um bom provedor com suporte diferenciado aqui no Brasil, recomendo o Vilago, do Cris Dias.

ATENÇÃO: Algumas pessoas tiveram dúvidas sobre como pegar o desconto, pois ele não aparece até a verificação final.

  1. Escolha o plano de hospedagem;
  2. Preencha os seus dados;
  3. na página entitulada Verify Total, coloque DIGITALMINDS no campo Promo Code;
  4. aperte o botão UPDATE para receber o desconto.

Clique aqui para escolher seu plano e aproveite.

2006-09-03 17:34:59

O Zen e a arte cavalheiresca da programação orientada a objeto (Parte 5)

(Para ver todas as partes desta série clique aqui)

Olá pessoal. No último artigo da série a gente construiu carros e falou do conceito mais importante da programação orientada a objetos: herança. Só pra relembrar, o conceito de herança em Object-oriented programming (OOP) é a capacidade de criar uma classe com características próprias que serão transmitidas a todos os objetos criados a partir dela e a possibilidade de estender ou evoluir esta classe criando outras classes filhas, com novas capacidades além das originais.

Falando assim, parece bastante complicado. Mas é pura genética. Você é filho da classe "seu pai" e da classe "sua mãe", portanto tem características similares a eles. No entanto você também é uma classe à parte, pois tem diferentes comportamentos e características em relação a eles. Na verdade muitas linguagens não permitem que um objeto (você, no caso) seja filho de mais de uma classe, pra não complicar as coisas. Assim os objetos em PHP, por exemplo, são sempre filhos de uma única classe (seu pai OU sua mãe, no caso). Estranho? Espere até a gente falar dos gnomos. É aqui que entramos na parte maneira dessa semana. Superpoderes.

Digamos que você seja um ser humano normal, da classe SerHumano:

Class SerHumano {

    public $nome;

    public $vivo;

    public $superpoder;

    public function __construct($nome) {

        $this->nome = $nome;

        $this->vivo = true;

    }

    public function taVivo() {

        if($this->vivo == true){

            print "Sim, eu estou vivo, e meu nome é $this->nome!";

        } else {            

            print "...";

        }

    }

}

Bom, como você pode notar, os seres humanos da classe SerHumano não têm lá uma vida muito complicada não. Eles só têm um nome, a propriedade e uma funcão, taVivo(), que serve pra gente saber se o carinha ainda está vivo ou se já partiu desta pra melhor. Que vidão. Mas você percebeu que, assim como na vida real, todos os objetos da classe SerHumano tem o potencial de ter um superpoder. Sim, amigos, a variável $superpoder está lá para mostrar que todos os objetos podem da classe SerHumano podem ter um superpoder, desde que saibamos bem o conceito de Composição em programação orientada a objetos.

Vamos ver como um superpoder pode ser descrito:

abstract class SuperPoder {

    abstract function ativar();

}

Vocês lembram da classe Automóvel, abstrata demais pra poder existir como um objeto? A classe superpoder também é assim. Além disso, também definimos a função ativar como sendo abstrata, pra que necessariamente o superpoder defina como ela funciona. Como não dá pra ativar() um superpoder genérico, a gente diz que essa função é abstrata, forçando a definição nas classes que estendem a classe SuperPoder.

Então, na verdade, o que a gente acaba de fazer é criar uma classe para que todos os superpoderes tenham obrigatoriamente a mesma função, ativar, definindo um jeito único de usar todos os superpoderes do mundo. Aí, fica fácil depois um SerHumano ativar() qualquer superpoder, independentemente do que ele faz.

Esse truque também vai nos permitir fazer coisas muito interessantes com diferentes superpoderes, já que todos eles são filhos da mesma classe.

Continuando, vamos para a classe SuperForça:

class SuperForca extends SuperPoder {

    function ativar(){

        print "Meu deus! Posso levantar um caminhão!";

    }

}

Muito bem. Tudo pronto pra gente começar a criar objetos como se não houvesse amanhã. Vamos começar por você, um SerHumano como outro qualquer:

$voce = new SerHumano("Fulano");

$voce->taVivo(); // Sim, eu estou vivo, e meu nome é Fulano!

A função taVivo() confirma que você tá vivo. Muito bem, agora vamos dar a você, meu caro amigo Fulano, um SuperPoder!

$voce->superpoder = new SuperForca();

E pronto! agora você já pode usar seu super poder, já que a variavel $superpoder, que fica dentro do objeto $voce já contém o novo objeto SuperForca:

$voce->superpoder->ativar(); // Meu deus! Posso levantar um caminhão!

Parabéns! Você acaba de conhecer um dos conceitos fundamentais de orientação a objeto: a composição. Algumas vezes vai ser melhor compor diferentes classes para ter maior flexibilidade, como nesse caso, por exemplo.

Explico: se você quisesse agora criar um SuperCao, que não é um SerHumano, claro, seria muito simples. Crie a classe SuperCao já com uma variavel para guardar o SuperPoder e pronto! Se você tivesse definido o superpoder dentro da classe SerHumano o código já começaria a ficar redundante, já que você precisaria definir novamente a função dentro da classe SuperCao.

Tá muito confuso? Não estão gostando? Ou acharam muito bom? Mande seus comentários clicando no link [Comente] aí embaixo. Até a próxima pessoal!

2006-02-27 15:12:44

Microsoft Origami

Sensacional vídeo do suposto tablet chamado Origami. Especula-se que esta belezinha custaria menos de 600 dólares nos EUA. Wow. Eu quero um.
2005-11-21 15:06:25
tags: 

XML Developer Center: Simple Sharing Extensions for RSS and OPML

Dica do Faoro, do Meiobit.
2005-11-14 11:46:39
tags: 

Blogger Brasil libera geral

Os termos de utilizaçao do blogger brasil ficaram... digamos... bem liberais. ;-)
2005-11-09 12:23:40

Subversion no MacOS X

O Cristóferson deu a dica desse tutorial sobre como instalar o subversion no MacOS X e a gente repassa pra galera.
2005-11-08 14:41:09

Halo 2 Hacks

Sensacionais vídeos de dicas pro Halo2, um jogo que me dá vontade de comprar o xbox.
2005-11-08 14:14:50
tags: 

Como vocês podem ver, é muito simples...

Um simples diagrama explica tudo do Microsoft Live...
2005-11-05 14:14:55

Jef Raskin about the history of the Macintosh

Outro artigo maravilhoso sobre o desenvolvimento da interface do Macintosh, escrito nada mais nada menos pelo homem que inventou o drag and drop, Jef Raskin.
2005-11-03 20:10:36

...E porque o Yahoo Maps! não vai dar certo

Robert Scoble, um funcionário da Microsoft, explica porque não dá mais pra competir com o Google. Uma dica: tem a ver com anúncios, conteúdo gerado por usuários e com direitos autorais.
2005-10-31 15:28:48

Argentina entra no projeto do Laptop de baixo custo do MIT

Nossos hermanos são os primeiros da américa latina a entrar no projeto e pretendem comprar entre 500 mil e 1 milhão de laptops. Por mais céticos que alguns sejam, não há como um projeto desse tipo não ter impactos profundos na educação de um país. Um bom exemplo a ser seguido.
P.S. putz e o pior é que o laptop é uma graça.
2005-10-24 06:26:19

Novo Office inclui BI

The Register afirma que a nova versão do Office da M$ vai incluir software de Business Inteligence e vai disponibilizar um Business Scorecard Manager para acompanhamento de KPIs (Key Performance Indicators).
Aparentemente eles desistiram dos consumidores comuns mesmo.
2005-10-24 06:19:27

E a grande novidade da Microsoft é... um novo shell!

A Microsoft refez o shell do windows inteiro; já estava mais do que hora. Parece bem interessante, ainda mais com gostinho de anos 80 estilo Digitalminds.
2005-10-19 18:00:31

Microsoft começa a lançar código (quase) open source

While many people see Microsoft as the "enemy" of open source, Microsoft has in fact been busy learning from open source, and has released source code for more than eighty Microsoft projects under a "shared source" license. In addition, there are about six hundred programs (notably dotNetNuke) released by independent developers under Microsoft shared source licenses.
2005-10-19 17:18:46

Macworld: Apple diz que Aperture não é competidor do Photoshop

Apple on Wednesday entered into a new market segment with the announcement of its first professional-level photography application: Aperture. Once the sole domain of Adobe Systems Photoshop, Aperture appeals to photographers by simplifying the workflow issues many professionals have encountered over the years.
Ah, ok. Não compete não. Beleza.
2005-10-19 17:09:19

Macworld: Apple lança novo Powermac Quad

Apple on Wednesday introduced a new Power Mac G5 line featuring dual-core PowerPC CPUs and a PCI Express expansion architecture. The new 2.0 and 2.3 GHz systems are shipping now, and the new 2.5GHz Quad system will be available in early November. Prices range from US$1,999 to $3,299.
Maravilha. Acho que é a despedida dos PowerPCs...
2005-10-18 14:10:17
tags: 

First look at Apple's new iPod video (photos)

Fotos do novo iPod, mostrando a grande diferença de espessura...
Update: Mais (e melhores) fotos no Engadget.
2005-10-16 06:06:36

Microsoft hopes to win women over with new Xbox

Note to hard-core video game players: Microsoft says it is aiming for your mothers and wives.
Hã? Eles vão fazer uma campanha pra vender o xbox360 pra mães e esposas? Socorro...
2005-10-13 15:47:33
tags: 

Microsoft wants WebDesigners to de-hack their CSS - Forever Geek

In a recent entry on the IE Blog Microsoft appeals to web designers asking them to remove their CSS fixes for IE.
2005-10-12 16:13:30

Think Secret - New iPod, iMac, iTunes 6, and more

At Apple's media event Wednesday the company rolled out a new iPod with a larger screen and video playback, introduced a new iMac line with media center-like features, and iTunes 6.
Acho que o mais impressionante é o Front Row. A Apple traz o conceito de media center para o resto de nós... isso vai vender iMacs igual água. E, de repente, tá a Apple concorrendo com tvs, dvd players e afins...
Vale muito a pena ver o vídeo do lançamento. Steve Jobs sempre dá um show.