Seaside: um framework web de verdade
Não é nenhuma novidade - pelo menos, não para aqueles que acompanham este blog há algum tempo - que eu estou me aventurando pelo incrível mundo do Smalltalk e suas vertentes. Pois então… dando prosseguimento à esta saga, tenho o orgulho de anunciar a minha próxima vítima: o framework Seaside.
Grossamente falando, o Seaside é um framework web escrito em Smalltalk que, provavelmente, será - a médio/longo prazo - tão (ou até mais) copiado do que o Rails é hoje. Pois bem… mesmo conhecendo muito pouco a respeito do seu funcionamento (até então, nada além de um mísero hello world), resolvi tentar criar uma aplicação sozinho… sem ajuda da escassa documentação. Qual foi o resultado?
O resultado não poderia ter sido melhor. O código do framework é muito bem documentado e é possível aprender bastante, mesmo tendo apenas o Class Browser do Squeak como recurso de pesquisa.
Enfim, não vou matar o assunto aqui pois temos um longo texto pela frente… mas vou logo avisando: ler a este post só é recomendável àqueles que possuem estômago forte.
Só prossiga se quiser mesmo conhecer um framework web de verdade.
Entendendo o exemplo
Não mostrarei nada de muito complexo aqui, até porque eu também estou aprendendo. Mas o exemplo é interessante para conhecermos algumas das características principais do Seaside, que serão explicadas no decorrer deste post.
Você conhece aquele jogo de “adivinhar o número”? Pois então… criaremos aqui algo parecido. Ao lado, você pode conferir uma imagem que ilustra como a aplicação deverá funcionar.
Ao abrir a aplicação, o usuário irá ver uma mensagem, que deverá confirmar para que o jogo comece. Quando o usuário confirma, o sistema gera um número aleatório entre 1 e 10 e mostra um formulário, que o usuário deve preencher com seu palpite. Em seguida, o sistema checa se o palpite do usuário corresponde ao número gerado aleatoriamente: se sim, o sistema mostra uma mensagem parabenizando o usuário; caso contrário, o sistema re-exibe o formulário, mostrando a contagem de “chutes” até o momento, além de uma mensagem com uma dica, para ajudar o usuário a adivinhar o número.
Enfim, esse ciclo continua até o usuário acertar. Por fim, o sistema pergunta se o usuário deseja jogar novamente: em caso afirmativo, um novo número é sorteado e o formulário re-exibido; senão, a mensagem de boas vindas é mostrada.
Ainda em relação ao diagrama, perceba que há um quadrado vermelho em torno de algumas atividades. Basicamente, isso quer dizer que a aplicação não deve permitir que o usuário volte (com o botão back do browser) e re-submeta um palpite após já tê-lo acertado.
Caso você opte por baixar o código, eu disponibilizei para download um arquivo zip contendo o projeto Squeak. Instruções sobre como rodar a aplicação se encontram em um arquivo read-me.
Introdução
O Seaside trabalha com o conceito de componentes. Nste exemplo, criaremos dois componentes para implementar a aplicação proposta. Um será responsável por controlar o fluxo da aplicação e o outro que receberá o input do usuário através de um formulário. Mas, antes de prosseguirmos, vamos aprender para que servem duas das mais importantes classes do Seaside: WAComponent e WATask.
A classe WAComponent será sua melhor amiga em um projeto Seaside, pois é através da criação de sub-classes de WAComponent que construímos a aplicação, pedaço por pedaço. Toda sub-classe de WAComponent deve implementar o método #renderContentOn:, pois é através deste que definimos como o componente deve ser renderizado no browser do usuário.
A classe WATask, por sua vez, é parecida com a classe WAComponent (de fato, ela a estende), com a diferença que ela não é usada para nada além de definir uma tarefa, ou melhor, a ordem com que outras tarefas e componentes devem ser chamados de modo a cumprir um determinado objetivo. Em poucas palavras: estenda WATask para definir workflows!
Entendendo a interação entre os componentes
Como pôde ser visto no diagrama de atividades mostrado anteriormente, o fluxo começa quando o usuário confirma que deseja jogar, passando o controle para o fomulário. O formulário é então renderizado no browser do usuário. Em seguida, quando o usuário submete o formulário, o componente chamador (que é o próprio fluxo) recebe o palpite do usuário e o compara com o número gerado no início. Dependendo do resultado dessa comparação, o formulário é exibido novamente ou a mensagem de sucesso é mostrada.
Certo… talvez tenha ficado confusa a parte em que um componente “passa o controle” para outro. Esse é um conceito muito importante em um framework como o Seaside. Para entender essa coisa do “passa o controle”, imagine uma chamada de método comum:
-
-
foo(); // chama o método ("passa o controle")
-
}
-
-
private static void foo() {
-
// faz algo aqui
-
}
-
Quando chamamos um método, o fluxo de execução é modificado, fazendo com que o método chamado assuma o controle. Então, quando o método termina, o fluxo retorna ao método chamador.
Simples não? Agora imagine isso funcionando na web. Imagine que o método main do exemplo fosse um componente da nossa aplicação, e que esse componente pudesse passar o controle para outro componente (assim como a chamada de método no exemplo). Então, quando o componente chamado terminar seu trabalho, o controle volta ao componente chamador, exatamente como uma chamada a um método. Massa né?
Esse conceito é chamado de Continuation. A imagem a seguir tenta ilustrar o funcionamento disso no contexto do nosso exemplo:
Escrevendo o código!
Se você não está familiarizado com Continuations e não entendeu muito bem a explicação (confesso que não sei explicar direito isso)… sem problemas. Além do mais, tudo isso ficará mais claro ao olharmos os códigos.
Definindo o fluxo da aplicação
Vamos criar o componente responsável por controlar o fluxo de execução da aplicação. O componente a seguir estende WATask e possui as variávies de instância number e form que servem, respectivamente, para armazenar o número gerado e armazenar o formulário usado para receber o input do usuário:
-
-
WATask subclass: #GuessingAppComponent
-
instanceVariableNames: ‘number form’
-
classVariableNames: ”
-
poolDictionaries: ”
-
category: ‘GuessingApp’
-
-
GuessingAppComponent class>>canBeRoot
-
^true
-
-
"Accessors for instance variables"
-
-
GuessingAppComponent>>children
-
^ Array with: self form
-
-
GuessingAppComponent>>initialize
-
"Configure initial values"
-
-
self number: 10 atRandom;
-
form: (GuessNumberForm new)
-
-
GuessingAppComponent>>go
-
"Start application workflow"
-
-
self inform: ‘Start the game pushing the button below!!’.
-
[self initialize;
-
[self call: self form.
-
self form number < self number
-
ifTrue: [self form message: ‘Try a higher value’]
-
ifFalse: [self form message: ‘Try a lower value’]]
-
doWhileFalse: [self form number = self number]]
-
doWhileTrue: [self confirm: ‘You won the game using ‘ , self form guesses asString , ‘ guess(es)!! Play again?’]
-
Todo o código é bem simples de se entender, com exceção do método #go. Veja que o corpo do método não passa de uma transcrição dos diagramas mostrados anteriormente. Atenção especial para a chamada self call:, que é o equivalente ao exemplo de chamada de método, com a diferença que estamos transferindo o controle para que outro componente possa executar no lugar do componente corrente. Perceba também que este método ainda não está pronto, pois, do jeito que está, a aplicação não está preparada para caso o usuário queira re-submeter um palpite após já tê-lo acertado… faremos isso depois!
Recebendo o input do usuário
Abaixo segue o código do componente usado para receber o palpite do usuário. O componente estende WAComonent e tem as variáveis de instância number, message e guesses. Essas variáveis servem, respectivamente, para: guardar o palpite do usuário quando o formulário é submetido; guardar a dica a ser exibida para o usuário (algo do tipo “chute um valor mais alto”, ou “mais baixo”); e guardar o número de vezes que o formulário foi submetido:
-
-
WAComponent subclass: #GuessNumberForm
-
instanceVariableNames: ‘message number guesses’
-
classVariableNames: ”
-
poolDictionaries: ”
-
category: ‘GuessingApp’
-
-
"Accessors for instance variables"
-
-
GuessNumberForm>>increment
-
"Increments the number of guesses"
-
-
self guesses: self guesses + 1
-
-
GuessNumberForm>>initialize
-
"Configure initial values"
-
-
self number: 1;
-
message: ‘Try a number between 1 and 10′;
-
guesses: 0.
-
-
GuessNumberForm>>renderContentOn: html
-
"Render component"
-
-
html heading: self message level: 4.
-
html heading: ‘You did ‘ , self guesses asString , ‘ guess(es) until now’ level: 5.
-
html form
-
with: [html textInput value: self number;
-
callback: [:value | self increment; number: value asInteger; answer].
-
html submitButton value: ‘check my guess!!’]
-
Novamente, a simplicidade impera.
A única coisa deste código que vale explicar é o método #renderContentOn:, que serve para que possamos definir o que deve ser retornado ao usuário quando o componente precisar ser renderizado. O método recebe um objeto WAHtmlCanvas por parâmetro, responsável por gerar código XHTML automaticamente. Para quem tem conhecimentos em HTML, o código é bem trivial, com exceção do método #callback, onde colocamos algum código para ser executado quando o formulário for submetido. Perceba a chamada a self answer no final da linha, que é responsável por devolver o controle ao componente chamador (o que executou self call: form).
Demarcando uma transação
Se lembra do quadrado vermelho no diagrama de atividades? Então… precisamos dar um jeito de impedir que o usuário volte a dar palpites após ter descoberto o número. Ou seja: precisamos definir uma transação.
Isso é feito utilizando o método #isolate:, conforme pode ser visto abaixo:
-
-
GuessingAppComponent>>go
-
"Start application workflow"
-
-
self inform: ‘Start the game pushing the button below!!’.
-
[self initialize;
-
isolate: [[self call: self form.
-
self form number < self number
-
ifTrue: [self form message: ‘Try a higher value’]
-
ifFalse: [self form message: ‘Try a lower value’]]
-
doWhileFalse: [self form number = self number]]]
-
doWhileTrue: [self confirm: ‘You won the game using ‘ , self form guesses asString , ‘ guess(es)!! Play again?’]
-
Não tem mais código, não! É só envolver o trecho desejado em um bloco e passá-lo ao método #isolate:!
O resultado
Confira abaixo algumas screenshots da aplicação em funcionamento:
O que há de errado com a última screenshot? Nada… é isso que acontece quando o usuário, após já ter adivinhado o número secreto, volta algumas telas com o botão back do browser e tenta re-submeter um formulário. Não cheguei a pesquisar, mas acredito que esta página seja customizável.
Desafio
Mesmo em uma aplicação simples como a que foi descrita aqui, fica evidente o quanto o Seaside é produtivo e simples (embora eu ainda não saiba usá-lo direito).
Portanto, proponho aos leitores deste blog que pensem no que deve ser feito para se criar uma aplicação igual (com as mesmas funcionalidades) a mostrada aqui (com a sua linguagem e framework preferidos). Tirando os frameworks Wordpress-like, imagino que dê para contar nos dedos de uma só mão os frameworks capazes de fazer tanto com tão pouco. Enfim, sintam-se a vontade para expressar suas opiniões a respeito disso.
Conclusão
- Não precisei fazer gambiarras (ou apelar para frameworks externos) só para definir o workflow da aplicação;
- Não precisei fazer gambiarras para controlar o estado da aplicação (lembrando que a aplicação suporta diversas instâncias do browser (ou abas) acessando a mesma aplicação ao mesmo tempo);
- Não precisei fazer gambiarras para evitar que o usuário faça alguma coisa errada (no caso, voltar com o botão back do browser ao terminar o jogo);
- Não precisei fazer gambiarras para que o código XHTML gerado fosse válido;
- Não precisei ficar gambiarras durante o desenvolvimento da aplicação (como reiniciar o servidor ou fazer re-deploy);
- Não precisei fazer gambiarras (nem pagar) para conseguir um bom ambiente de desenvolvimento, pois o Squeak sozinho faz muito bem o serviço;
- Não precisei fazer gambiarras, pois o Seaside não é um pseudo-component-based-framework (que mais complica do que ajuda) ou um brainless-action-based-framework (onde as actions nada mais são que métodos em forma de classes, procedurais e não reusáveis);
- (esqueci de alguma coisa?)
Trocando em miúdos: Seaside ROCKS.
Imagem por: Google Images
Tags: aplicação, componentes, continuations, framework, seaside, smalltalk, squeak, tutorial, web, workflow

27 de maio de 2007 às 10:33 pm
So faltou falar sobre continuations
Da uma olhada no blog do Avi (devel do seaside)
http://smallthought.com/avi/
A proposito, a discussao que ele faz sobre sessoes e continuations eh deveras interessante.
Thiago
28 de maio de 2007 às 5:37 am
É verdade… apenas coloquei uma referência ao Wikipedia sobre o assunto.
Inclusive, neste exemplo, o componente
WATask(e, claro, as Continuations) nos poupam de ter que nos preocupar com características inerentes ao protocolo HTTP (e que não são nada amigáveis ao se criar aplicações para web), como persistir dados através de requisições… no caso do exemplo, o número gerado aleatoriamente no início e a quantidade de vezes em que o formulário foi submetido.É… enquanto outros frameworks fazem gambiarras para contornar essa característica stateless do protocolo, o Seaside faz isso de um jeito que eu não pensava ser possível.
Bom, acho que deu pra perceber que eu gostei mesmo do Seaside, não?
28 de maio de 2007 às 4:57 pm
Legal meu, parabéns pelo post.
Sugestão para próximo post: Seaside + Magma
Tem um blog sobre smalltalk (squeak + seaside, na maioria dos posts) que acompanho e você pode utilizar como referência também: http://www.onsmalltalk.com
[]s
28 de maio de 2007 às 5:03 pm
Já está nos meus favoritos!
[]s!
28 de maio de 2007 às 5:46 pm
Ahh sim, quanto à sua suguestão, está anotada!
Ontem mesmo eu baixei o Magma através pelo SqueakMap… quando tiver algo mais concreto, com certeza escreverei um post a respeito.
Aliás, o Magritte também está na lista das minhas próximas vítimas
[]s
28 de maio de 2007 às 10:05 pm
Você está gostando mesmo de Smalltalk! Algum uso prático em vista, ou ainda mera curiosidade acadêmica?
E Smalltalk para Desktop? Componentes de negócios no lado do servidor?
Smalltalk não morreu? Ressucitou? Ainda é bebê? Usam isso no Brasil? No mundo? E por que “será - a médio/longo prazo - tão (ou até mais) copiado do que o Rails é hoje” ? Quais as bases para essa afirmação?
São apenas sugestões. Se você colocar Smalltalk dentro de um contexto, suas publicações sobre o assunto vão ficar ainda melhor.
Eu acho legal essa série Smalltalk no seu blog. Se cada programador Java blogueiro tivesse uma parecida - Lisp, Ruby, Erlang - seria muito interessante.
Quando comecei a viajar pelo mundo do Python, tinha a intenção de usar Jython para permitir mudanças de regras de negócios pelos usuários dos softwares. No próximo semestre devo continuar com Python e Jython.
T+!
28 de maio de 2007 às 10:49 pm
Por enquanto, isso é apenas uma “mera curiosidade acadêmica”.
A programação desktop em Smalltalk… no próprio Squeak dá pra brincar com isso. Dá pra ir criando e brincando com os objetos, na hora, através de comandos executados no Workspace… é bem legal. (embora o visual da aplicação seja meio porco, pelo menos com o Squeak.. talvez com uma versão mais “pró” do Smalltalk isso seja diferente).
Já em relação ao lado do servidor, não cheguei a pesquisar, mas tem o GemStone, que pode ser utilizado em conjunto com outras ferramentas (como o Seaside). Infelizmente, não sei dizer até onde o GemStone aguenta a pressão, em termos de carga de usuários e performance…
Se usam no mundo? Sim, embora não seja mainstream como o Java. Se usam no Brasil? Não sei bem onde li, mas parece que tem uma empresa que trabalha com Smalltalk há um bom tempo. Se existem outras, não sei.
Smalltalk morrer? Nah. Se ela tivesse mesmo morrido, teria levado todas as outras linguagens/plataformas junto!
Quanto à afirmação sobre o quanto o Seaside será copiado… bom, veja alguns exemplos:
Como eu disse, já começou! Sei lá, é que o troço é muito fudido mesmo… não culpo ninguém por copiar o Seaside. Mas seria mais legal ver o Smalltalk sair do quase-anonimato para ser utilizado em projetos que hoje são feitos em Java, por exemplo. Quer um exemplo? Dá só uma olhada nesta aplicação. É Seaside. Depois de ver isso, e sabendo que chamam o GMail de web 2.0, então isso aí tá mais pra web 2.5.
E muito obrigado pelas dicas! É que eu não consigo evitar… esse meu lado técnico faz com que eu me esqueça dos “porquês” e vá direto aos “comos”
[]s!
30 de maio de 2007 às 3:53 am
Esse “seu lado técnico” é fundamental, e é raro. Geralmente “se esquecem” dele.
Detalhe: depois desse artigo sobre Smalltalk e do monte de link que você disponibilizou, me lembrei de uma coisa que me disseram quando era criança e disse que queria crescer rápido pra parar de estudar, só trabalhar: “você nunca vai deixar de estudar; são os novos tempos”. Pois é, ainda bem que eu tive tempo pra aceitar e gostar disso.
Eu vi o Dabble DB, e é bonito mesmo. Do tipo “profisional”, que impressiona clientes.
Será que um componente Seaside consegue chamar um EJB? Se for o caso, dá pra pensar em usar.
30 de maio de 2007 às 11:33 am
Vixi rapaiz.. não sei se dá pra integrar uma aplicação Smalltalk em componentes Java EE… talvez dê, mas não encontrei nada a respeito disso.
6 de junho de 2007 às 8:34 pm
Oi pessoal,
tem muito mais do que curiosidade academica com smalltalk.
Como referencias rapidas deixo a voces olharem o sistema DabbleDB http://www.dabbledb.com que é um investimento milhonario de muito sucesso liderado pelo Avi Bryant o criador do Seaside. Roda sobre miles de imagems de Squeak com carga balanceada (oseja que está escalado horizontalmente).
Por outra parte Gemstone anunciou que libera uma versao do Gemstone/S com o port do Seaside incluido. Ele “in short” é o Oracle dos Smalltalks. Deve ser o smalltalk que mais e melhor escala verticalmente que tem no mercado. Desafortunadamente para o povo o preço escala igualmente bem o muito melhor
abraço,
Sebastian
6 de junho de 2007 às 8:43 pm
Olá, Sebastian! Bem-vindo novamente.
O DabbleDB realmente é uma aplicação impressionante. Até então, eu nunca havia visto uma aplicação tão “viva” quanto essa. E, o melhor de tudo, é saber que ela é feita com o Seaside!
Um abraço!