Upload
profrogeriocardoso
View
282
Download
0
Embed Size (px)
Citation preview
UNIVERSIDADE VEIGA DE ALMEIDA
BACHARELADO EM SISTEMAS DE INFORMAÇÃO
INTRODUÇÃO AO FRAMEWORK GRAILS
Adriano Basto Antunes
Cabo Frio
2011
ADRIANO BASTO ANTUNES
INTRODUÇÃO AO FRAMEWORK GRAILS
Trabalho desenvolvido durante a disciplina Monografia e apresentada
ao Curso de Sistemas de Informação da Universidade Veiga de
Almeida, Campus Cabo Frio, como pré-requisito para a obtenção do
título de Bacharel em Sistemas de Informação.
Orientador: Prof. Jorge Ricardo Valardan Domingos, M.Sc
Cabo Frio
Junho/2011
Universidade Veiga de Almeida - UVA
Faculdade de Informática
Curso de Bacharelado em Sistemas de Informação
Reitor:
Prof. Dr. Mário Veiga de Almeida Jr.
Vice-Reitor:
Prof. Tarquínio Prisco Lemos da Silva
Coordenador do Curso de Informática:
Prof. Edgar Augusto Gonçalves Gurgel do Amaral
Banca Examinadora:
_____________________________
Prof. Jorge Ricardo Vallardan Domingos (Orientador)
M.Sc em Matemática – Puc-Rio
_____________________________
Prof. Matheus Bousquet Bandini
M.Sc em Sistemas e Computação – IME/RJ
_____________________________
Prof. Douglas Ericson Marcelino de Oliveira
M.Sc em Sistemas e Computação – IME/RJ
Universidade Veiga de Almeida – Cabo Frio
Estrada das Perynas, s/n
CEP: 28901-970 - Cabo Frio – RJ
Dedico este trabalho a todas as pessoas que sempre me apoiaram na conquista dos
meus objetivos.
AGRADECIMENTOS
A minha mãe que sempre me apoiou e incentivou fervorosamente meus estudos. Aos
meus irmãos que virão em mim à chance de realizar seus sonhos. Aos meus amigos, colegas e
professores com quem dividi experiências e aprendi muito nesta longa caminhada.
LISTA DE FIGURAS
Figura 2.1 – A evolução do ambiente tecnológico ................................................................... 17
Figura 2.2 – O padrão MVC ..................................................................................................... 22
Figura 3.1 – Código: Comparação Simples de Java e Groovy ................................................. 31
Figura 3.2 – Código: Exemplo do uso de Meta-Objeto ............................................................ 33
Figura 3.3 – Código: Exemplo de GString ............................................................................... 34
Figura 3.4 – Código: Os métodos findAll e each. .................................................................... 35
Figura 3.5 - Código: Exemplos de Closure .............................................................................. 36
Figura 3.6 – Entendendo o SiteMesh ....................................................................................... 41
Figura 3.7 – Árvore de diretórios de uma aplicação Grails ...................................................... 45
Figura 3.8 – Código do DataSource.groovy ............................................................................. 48
Figura 3.9 – Scaffold ................................................................................................................ 51
Figura 3.10 – Exemplo de GSP ................................................................................................ 52
Figura 3.11 – Código: Incluindo template parcial .................................................................... 53
Figura 4.1 – Diagrama de Classes ............................................................................................ 60
Figura 4.2 – Classe de domínio: Cliente.groovy ...................................................................... 61
Figura 4.3 - Classes de domínio: VendasaPrazo.groovy .......................................................... 62
Figura 4.4 – Diagrama Hierárquico das Classes de Testes ...................................................... 64
Figura 4.5 – Tela Unit Test Results .......................................................................................... 65
Figura 4.6 – Tela Principal ....................................................................................................... 67
Figura 4.7 – Tela de Login ....................................................................................................... 67
Figura 4.8 – Tela Listar Clientes .............................................................................................. 68
Figura 4.9 – Tela Mostra Usuário ............................................................................................. 68
Figura 4.10 – Tela Criar Venda a Prazo ................................................................................... 69
Figura 4.11 – Tela Mostrar Resultados da busca ..................................................................... 70
Figura 4.12 – Árvore de diretórios war .................................................................................... 72
LISTA DE TABELAS
Tabela 3.1 – Principais diretórios de uma aplicação Grails. .................................................... 46
Tabela 4.1 – Product Backlog .................................................................................................. 57
Tabela 4.2 – Sprint Backlog ..................................................................................................... 58
LISTA DE ABREVIATURAS E SIGLAS
COC Convention Over Configuration (Convenção sobre Configuração)
CPD Centro de Processamento de Dados
CRUD Create – Read – Update – Delete (Criar-Ler-Editar-Excluir)
CSS Cascading Style Sheets
DI Dependency Injection (Injeção de Dependências)
DRY Don‟t Repeat Your Self (Não se repita)
GORM Grails Object Relational Mapping (Mapeamento Objeto-Relacional do Grails)
GSP Groovy Server Pages
HTML Hyper Text Markup Language (Linguagem de Marcação de Hipertexto)
HTTP Hyper Text Transfer Protocol (Protocolo de Transferência de Hipertexto)
IDE Integrated Development Environment (Ambiente Integrado de
Desenvolvimento)
IoC Inversion of Controll (Inversão de Controle)
J2EE Java 2 Platform, Enterprise Edition (Plataforma Java 2, Edição Empresarial)
JDK Java Development Kit (Kit de Desenvolvimento Java)
JEE Java Enterprise Edition (Java Edição Empresarial)
JVM Java Virtual Machine (Máquina Virtual Java)
KISS Keep It Simple and Short (Mantenha-o Simples e Curto)
MOP Meta-Object Protocol (Protocolo de Meta-Objeto)
MVC Model – View - Controller (Modelo – Visão – Controlador)
OO Orientação a Objetos
PC Personal Computer (Computador Pessoal)
PHP Hypertext Preprocessor ou Personal Home Page
RoR Ruby on Rails
SQL Struct Query Language (Linguagem de Consultas Estruturada)
TDD Test Driven Development (Desenvolvimento Direcionado a Testes)
UML Unified Modeling Language (Linguagem Unificada de Modelagem)
URL Uniform Resource Location (Localização de Recurso Uniforme)
WAR Web Archive (Arquivo Web)
XML eXtensible Markup Language (Linguagem de Marcação Estendida)
XP eXtreme Programming (Programação Extrema)
RESUMO
Com o apoio de um conjunto de tecnologias de sucesso no mercado de
desenvolvimento e baseado em Ruby on Rails, o framework Grails caracteriza-se como a
melhor solução no modelo de desenvolvimento ágil para a plataforma Java na web. Este
trabalho apresenta uma introdução a ferramenta, apresentando suas funcionalidades,
principais tecnologias e o desenvolvimento prático de uma aplicação que demonstrará a
utilização desses poderosos recursos.
ABSTRACT
With the support of a set of technologies on the successful market of software
development and based on Ruby on Rails, Grails framework is characterized as the best
solution for an agile development model for the Java platform on the web. This paper aims to
provide an introduction to the tool, showing its features, key technologies and the
development of a practical application that will demonstrate the use of these powerful
resources.
SUMÁRIO
1 INTRODUÇÃO .................................................................................................. 13
1.1 MOTIVAÇÃO .................................................................................................. 13
1.2 OBJETIVO ...................................................................................................... 13
1.3 JUSTIFICATIVA ............................................................................................... 14
1.4 METODOLOGIA DE PESQUISA ......................................................................... 14
1.5 ORGANIZAÇÃO DO TEXTO .............................................................................. 15
2 O UNIVERSO DE GRAILS .............................................................................. 16
2.1 HISTÓRICO DA COMPUTAÇÃO ........................................................................ 16
2.2 DESENVOLVIMENTO ÁGIL .............................................................................. 18
2.3 FRAMEWORKS ............................................................................................... 20
2.4 O PADRÃO MVC. ........................................................................................... 21
2.5 JAVA VS. RUBY ON RAILS ............................................................................. 22
3 O FRAMEWORK GRAILS .............................................................................. 26
3.1 O QUE É GRAILS? .......................................................................................... 26
3.1.1 Origem do Grails ...................................................................................... 27
3.1.2 Groovy + Ruby on Rails = Grails ............................................................ 28
3.2 GROOVY: A LINGUAGEM DO GRAILS ............................................................. 29
3.2.1 O que é Groovy? ...................................................................................... 29
3.2.2 Groovy e Java: Comparação e Integração .............................................. 30
3.2.3 Por dentro do Groovy .............................................................................. 32
3.2.4 Closures ................................................................................................... 35
3.3 GRAILS: ALGUMAS TECNOLOGIAS................................................................. 37
3.3.1 Spring ....................................................................................................... 37
3.3.2 Hibernate .................................................................................................. 38
3.3.3 SiteMesh ................................................................................................... 39
3.3.4 Outras Tecnologias .................................................................................. 42
3.4 CONHECENDO O GRAILS................................................................................ 43
3.4.1 Começando com Grails ............................................................................ 43
3.4.2 Estrutura de Diretórios e o Padrão MVC ................................................ 44
3.4.3 Persistência de Dados no Grails .............................................................. 47
3.4.4 Scaffold: Problema ou Solução? .............................................................. 49
3.4.5 A camada de Visualização ........................................................................ 51
3.5 CASOS DE SUCESSO ....................................................................................... 53
4 ESTUDO DE CASO ........................................................................................... 55
4.1 DESCRIÇÃO DO PROBLEMA ........................................................................... 55
4.2 O SISTEMA ..................................................................................................... 55
4.2.1 Desenvolvendo em Scrum ........................................................................ 56
4.2.2 Preparando o ambiente para o sistema................................................... 59
4.2.3 Classes de domínio e Controladores ....................................................... 59
4.2.4 Provendo Segurança do Sistema ............................................................. 62
4.2.5 Desenvolvendo direcionado a Testes ...................................................... 63
4.2.6 Conectando ao Banco de Dados ............................................................. 65
4.2.7 Visualizações da aplicação ..................................................................... 66
4.2.8 Trabalhando com plugins ........................................................................ 69
4.2.9 Implantando a aplicação ......................................................................... 70
5 CONSIDERAÇÕES FINAIS ............................................................................. 73
5.1 CONCLUSÕES ................................................................................................ 73
5.2 TRABALHOS FUTUROS ................................................................................... 74
REFERENCIAS BIBLIOGRÁFICAS......................................................................75
ANEXOS A - CÓDIGO FONTE................................................................................77
13
1 INTRODUÇÃO
1.1 MOTIVAÇÃO
A área de tecnologia da informação está cada dia buscando novos caminhos e formas
de tornar cada vez mais simples e ágil os processos de desenvolvimento de produtos de
software. Esta é uma necessidade exigida por um mercado cada vez mais informado e com
maior necessidade de rápidos resultados que proporcionem rápidas, porém eficientes,
decisões. A internet cada vez mais ao alcance de todos e o advento de dispositivos móveis
com acesso à mesma traz aos programadores de softwares mais oportunidades e desafios de
desenvolver para a plataforma WEB. Dentro desta premissa surge à necessidade por
ferramentas de desenvolvimento que se adequem a realidade atual.
No estudo das ferramentas de desenvolvimento de software é difícil negar a
importância da plataforma Java no mercado. Mas como acompanhar o ritmo do mercado atual
e ultrapassar os problemas apresentados pela plataforma sem abandonar seu grande poder de
produtividade?
Nessa busca por soluções, com destaque para o desenvolvimento web, criam-se
frameworks e linguagens alternativas como ferramentas que possibilitem o aproveitamento do
que já fazia sucesso acrescentando características inovadoras que podem garantir a
continuidade desse mesmo sucesso.
1.2 OBJETIVO
Objetiva-se mostrar a importância do framework Grails e da linguagem de
programação Groovy para desenvolvimento de soluções de software no atual mercado
tecnológico, visando demonstrar e comparar as novas ferramentas com as já existentes, e
trazer a imensa gama de desenvolvedores da plataforma Java soluções que permitam uma
maior capacidade competitiva.
14
No estudo da linguagem Groovy pretende-se conhecer suas principais características e
entender de que forma a mesma pode trazer aos atuais profissionais da área melhorias nos
projetos de desenvolvimento de software. Na análise e conhecimento do framework Grails
pretende-se entender como funcionam seus processos, estruturas e propriedades e demonstrar
o que de novo o mesmo apresenta ao mercado de desenvolvimento web através de um
desenvolvimento prático.
1.3 JUSTIFICATIVA
O avanço da Internet trouxe ao mercado de tecnologia da informação uma migração
dos sistemas desktop para os sistemas web. Nesta atual realidade justifica-se a necessidade de
novos recursos e ferramentas que permita ao mercado de software estar apto a competir num
mundo globalizado, onde a informação está cada vez mais acessível e a perca da mesma pode
levar muitos negócios à falência.
Levando em conta o citado acima, é importante levar ao conhecimento de todos as
ferramentas criadas no intuito de agilizar os processos de desenvolvimento de software, sem
causar aos atuais sistemas existentes grandes mudanças. Permitir ao desenvolvedor o
desenvolvimento ágil de aplicações de forma com que esteja preparado para acompanhar o
atual mercado e seu futuro próximo.
Justifica-se ainda o intuito de deixar conteúdo e conhecimento de soluções de
desenvolvimento ágil para web numa transição simples de uma das plataformas de
desenvolvimento de software mais comuns nos dias de hoje para o que pode vir a ser o
dominante no mercado futuro.
1.4 METODOLOGIA DE PESQUISA
A metodologia a ser utilizada nesta pesquisa caracteriza-se principalmente como
bibliográfica e dentro deste contexto têm-se como fontes de pesquisas a consulta a livros,
artigos e estudo de caso.
Serão utilizados os métodos lógicos dedutivo, hipotético-dedutivo no contexto da
análise das ferramentas de desenvolvimento de software em estudo e, para demonstrar a
15
evolução e o atual cenário tecnológico do desenvolvimento de software, serão utilizados os
métodos auxiliares histórico, comparativo e estatístico.
No tipo de pesquisa utilizada a bibliográfica é privilegiada por tratar-se de um estudo
sobre uma ferramenta de software. No entanto para exemplificar e auxiliar no entendimento
do assunto pretende-se utilizar estudo de caso para o desenvolvimento de uma aplicação
modelo.
1.5 ORGANIZAÇÃO DO TEXTO
O texto está organizado em 5 capítulos com a intenção de apresentar uma introdução
as principais tecnologias do framework em estudo e um apanhado histórico do universo que o
originou, além de um exemplo prático através de um estudo de caso.
O Capitulo 2 traz uma apresentação do universo que envolve o framework Grails, o
histórico da computação e das ferramentas de software, a definição de framework, o ambiente
de desenvolvimento ágil, o padrão de projeto utilizado e as inspirações e influencias de Java e
Ruby on Rails.
O Capitulo 3 abrange o ponto principal da pesquisa apresentando o framework Grails,
seu histórico, características e suas principais tecnologias e mostra um apanhado básico da
linguagem de programação Groovy que é utilizada pelo framework, salientando sua
integração com Java e fazendo comparações com a mesma.
O Capitulo 4 traz um estudo de caso com o desenvolvimento de um sistema seguindo
as diretrizes do modelo ágil e encerrando o conteúdo da pesquisa, seguido das considerações
finais no Capitulo 5.
16
2 O UNIVERSO DE GRAILS
2.1 HISTÓRICO DA COMPUTAÇÃO
A história da computação como conhecemos hoje teve seu início há quase meio século
atrás quando os primeiros microcomputadores PC‟s (Personal Computers) foram introduzidos
no mercado, mas a idéia da construção de uma máquina capaz de executar operações e
cálculos é bem antiga tendo seu início nos esforços de Pascal, Leibnitz e Babbage [RODR
02].
A partir da introdução dos computadores pessoais a tecnologia da informação
começou a passar por um processo de transformação cada vez mais acelerado saindo do
antigo modelo centralizado pelas Centrais de Processamento de Dados (CPD‟s) até chegar ao
atual ambiente totalmente descentralizado e cada vez mais virtual. Foi com a introdução dos
PC’s na década de 1980 que a revolução começou, com a disseminação das redes locais
utilizando alta velocidade de comunicação e conexão com outras redes locais e de longa
distância.
Com a popularização dos ambientes de redes dentro das organizações os CPD‟s
iniciaram o processo de extinção e a década de 1990 iniciou com um ambiente de
processamento de dados mais dinâmico e distribuído. Foi também na década de 1990 que a
rede mundial de computadores, criada na década de 1960 pelos militares americanos, deixou
de ser apenas de uso das universidades e instituições militares e se tornou popular entre os
usuários comuns com o uso do correio eletrônico e das home-pages.
A disseminação da Internet trouxe uma nova era tecnológica, o ambiente
computacional, que nos seus primórdios se caracterizava pelos CPD‟s, passou pelo Time
Sharing (sistemas computacionais de tempo compartilhado), pelas redes Lan’s, pela
arquitetura Cliente/Servidor e hoje com a difusão da internet vive a denominada Computação
em nuvem, onde o processamento de informações é cada vez mais virtual.
A figura 2.1 mostra os estágios de evolução do ambiente tecnológico.
17
Figura 2.1 – A evolução da Tecnologia da Informação
Fonte: [RODR 02]
Desde sua criação o objetivo principal de um computador é realizar o processamento
de dados, para que tal objetivo fosse satisfeito é que surgiram os programas de computador.
No entanto passar ao computador os problemas a serem resolvidos, ou seja, criar os
programas necessitava de algo que traduzisse esses problemas na linguagem do computador, a
isso se deu o nome de Linguagens de Programação.
A primeira idéia de uma linguagem de programação que expressasse instruções para o
computador veio de Ada Lovelace para o projeto de computador de Charles Babbage, o
projeto não foi concretizado e a idéia da linguagem de Ada não chegou a ser testada. Tempos
depois várias outras linguagens de programação foram surgindo, mas sem grandes impactos,
até o surgimento da primeira grande linguagem de alto nível, o Fortran em 1954. Seguiu-se
ao Fortran o surgimento do COBOL e LISP, também linguagens de alto nível que são assim
conhecidas por apresentarem uma proximidade maior a língua humana.
Foi nas décadas de 1960 e 1970 que uma grande revolução iniciou-se em meio as
linguagens de programação, surgiam as linguagens Pascal, C e Smalltalk entre tantas outras e
18
também na mesma época surgia um novo paradigma de programação conhecido como
Orientação a Objetos. A programação orientada a objetos trouxe grandes mudanças no
desenvolvimento de software que antes era dominado pelo paradigma estruturado das
linguagens como Pascal e C, onde era priorizada a criação de estruturas simples, usando sub-
rotinas e funções. Na programação orientada a objetos as diversas unidades de software são
chamadas de objetos, esses objetos agregam o comportamento do que representam, e o fluxo
de execução neste tipo de programa é feito através de troca de mensagens entre os objetos.
A década de 1990 foi destaque pelo grande aumento da popularidade da internet e
nessa época começou a surgir a necessidade de linguagens que criassem aplicações para o
meio virtual fazendo a história das linguagens de programação subir mais um degrau na
escala de evolução. Foi nessa época que surgiram as linguagens como Java e PHP seguidas
pelas linguagens dinâmicas, ou linguagens de script, que viriam a dominar o mercado nos
anos 2000, como Ruby, Python e Groovy.
As linguagens dinâmicas são linguagens de alto nível, com tipagem dinâmica, ou seja,
cujo tipo da variável é definido em tempo de execução. Essas linguagens, como todos os
outros tipos de linguagens de programação, apresentam suas vantagens e desvantagens
dependendo da situação em que são empregadas. O destaque desse tipo de linguagem se dá a
necessidade do mercado que atualmente exige resultados cada vez mais rápidos sem perder
eficiência. O histórico das linguagens de programação mostra que no decorrer dos anos a
tecnologia se adapta as necessidades surgidas sem necessariamente definir uma regra, mas
não se deve negar a importância do desenvolvedor conhecer mais do que uma linguagem de
programação.
O seu conhecimento das linguagens de programação é a chave
para o seu portfólio pessoal como um desenvolvedor de software.
[KÖNI 07]
2.2 DESENVOLVIMENTO ÁGIL
Desde a década de 1970 o desenvolvimento de softwares vivia uma crise no que dizia
a respeito a gestão de seus projetos. Ferramentas tradicionais de gerência de projetos não
19
conseguiam suprir as necessidades que os projetos de software apresentavam como prazo,
orçamento e as constantes mudanças de escopo no decorrer do desenvolvimento.
Motivados pela busca de uma solução para esses problemas, um grupo de pensadores
da área de engenharia de software se juntou no final dos anos 90 e início dos anos 2000 para
criar uma metodologia correta para lidar com os projetos de software. Do encontro em
Fevereiro de 2001 em Utah nasceu o que chamamos de Manifesto Ágil, manifesto esse que
deu origem ao segmento de Desenvolvimento Ágil.
O manifesto ágil consiste de um conjunto de valores a serem seguidos pelos
envolvidos nos processos de desenvolvimento de software, criado por 17 fundadores num
grupo formado por importantes engenheiros de software e autores de importantes publicações
da área. O manifesto consistia, basicamente, de quatro grandes premissas:
1. Indivíduos e Interações mais que processos e ferramentas.
2. Software funcionando mais que documentação abrangente.
3. Colaboração com cliente mais que negociação de contratos
4. Resposta a mudança mais que seguir um plano.
A essas premissas segue-se um conjunto de 12 princípios que aqueles que desejam
empregar o desenvolvimento ágil devem seguir. Com o surgimento do manifesto ágil o
mercado de desenvolvimento de software começou a ganhar novo impulso e, movido pelo
crescimento cada vez maior do número de usuários da internet, ferramentas que se
adequavam mais a esse novo ambiente de desenvolvimento foram surgindo.
A maioria das linguagens de programação e framework surgidos nos anos 2000 vieram
preparadas para esse novo ambiente, prontos para facilitar cada vez mais o trabalho dos
desenvolvedores. Finalmente os processos de criação de software ganharam novos olhares e a
função do programador passou a ser vista como um trabalho criativo e não mais algo
regidamente padronizado.
Com o desenvolvimento ágil surgiram metodologias como Scrum e Extreme
Programming (XP) que permitiram as equipes de desenvolvimento se focar ao que realmente
interessava. Com o cliente do lado, programadores, testadores e a equipe como um todo
passaram a saber exatamente o que deveriam fazer. O prazo pode ser estimado e o escopo não
necessitou mais ser totalmente fechado.
20
Hoje se observa, no entanto, que muitas organizações que dizem trabalhar com
Desenvolvimento Ágil estão na verdade empregando de maneira errada o que prega o
manifesto. “... usar as ferramentas apenas como ferramentas, não funciona se os valores não
foram entendidos” [MORE 09]. O desenvolvimento ágil não significa garantia de sucesso,
mas representa um avanço na gestão de projetos de software como nunca visto antes.
2.3 FRAMEWORKS
Os frameworks tem papel importante no atual mercado de software e há uma
imensidão deles para as mais diversas plataformas, mas muita confusão ainda existe sobre o
que realmente são. Entende-se por framework de software o conjunto de classes
implementadas em uma linguagem de programação especifica provendo uma funcionalidade
genérica que auxilia no desenvolvimento de software [DEPA 10].
Existe a confusão por parte de alguns entre framework e bibliotecas de classes. Nos
frameworks, ao contrário das bibliotecas de classes, as classes são dependentes e possui um
modelo de colaboração entre si que livra o projetista da preocupação de saber quando chamar
cada método, por exemplo. Nas bibliotecas de classes cada classe é única e não é possível o
conhecimento do domínio, sendo este feito apenas pelas próprias aplicações que criam as
colaborações entre tais classes.
Isso representa apenas uma das vantagens do uso de frameworks, possibilitando aos
desenvolvedores componentes integrados e permitindo que os mesmos gastem seu tempo
ocupando-se da lógica de negócios e não dos detalhes técnicos. Além disso, os frameworks
caracterizam-se por serem partes mais concisas de software o que possibilita uma maior
facilidade na detecção de erros.
Acima de tudo o uso de framework poupa muito o trabalho do desenvolvedor
eliminando a perca de tempo com coisas que não o interessam, mesmo sendo importantes.
Muito da configuração do projeto de software é resolvido quase que automaticamente pela
maioria dos frameworks atuais, trazendo aos projetos maior agilidade, maior lucro,
antecipação de entrega e satisfação dos clientes. Atualmente nota-se que muitos frameworks
21
destacaram-se de tal forma que já chegam ao patamar de plataforma de programação devido
ao grande número de funcionalidades e facilidades que eles proporcionam.
2.4 O PADRÃO MVC.
Todo processo de desenvolvimento de software segue um conjunto de regras que
visam organizar, documentar e facilitar o trabalho do desenvolvedor, dentro desta premissa é
que surgem os padrões. Um desses padrões, conhecido por MVC (Model-View-Controller)
tem destaque principalmente no desenvolvimento para Web.
O padrão MVC consiste em dividir a estrutura da aplicação em 3 (três) camadas:
Modelo, Visualização e Controlador. Essa divisão visa separar a lógica de negócio da camada
de apresentação e do fluxo da aplicação e dessa forma garantir maior “independência” do
código. Cada uma dessas camadas possui funções, objetivos e escopos diferentes e cada uma
delas pode ser alterada separadamente sem muito interferir na outra [MACO 10].
A primeira camada correspondente ao Modelo da aplicação é a responsável por
guardar as informações dos dados, os atributos e métodos de uma classe e seus respectivos
tipos.
A camada de Controle é responsável pelo fluxo da aplicação, a interação entre usuário
e sistema controlando todas as operações da aplicação.
A terceira camada é a de Visualização, responsável pela interface da aplicação.
Também conhecida como camada de apresentação a visualização determina como os dados
do modelo são mostrados ao usuário.
A interação entre as três camadas do padrão MVC pode ser melhor entendida pela
análise da Figura 2.2.
22
Figura 2.2 – O padrão MVC
Como demonstra a figura acima, a camada de Modelo é acessada pelas duas demais
por conter em si o modelo de dados no qual a camada de Controle e de Visualização se
baseiam para criar suas funcionalidades. O Controlador da aplicação, responsável pelas
principais funcionalidades da aplicação é a camada que controla a interação entre as demais.
O padrão MVC tem sido exigência em muitas plataformas de desenvolvimento sendo,
em algumas, imprescindível o seu conhecimento. Muitos frameworks tem sua estrutura
construída no formato do padrão separando em três camadas distintas as classes e demais
arquivos pertinentes a Modelo, Visualização e Controle.
2.5 JAVA VS. RUBY ON RAILS
Em 1991 a Sun Microsystems dava inicio a um projeto que objetivava antecipar o que
previa ser tendência no mercado tecnológico futuro. A idéia era preparar a convergência dos
computadores com os eletrodomésticos utilizados no dia-a-dia com a criação de um
dispositivo que funcionasse como um controle remoto desses equipamentos. Mais tarde
observou-se que tal projeto era muito avançado para época e a recusa do mercado fez a
empresa buscar outra utilidade para o projeto. James Gosling, um dos integrantes da equipe de
13 pessoas que trabalharam arduamente no projeto, criou uma linguagem de programação
orientada a objetos que pudesse ser executada no aparelho, essa linguagem era independente
23
de plataforma e fortemente inspirada na linguagem C, na época a linguagem recebera o nome
de Oak, mas logo teve que ser mudado quanto se descobriu já existir linguagem de
programação com tal nome.
Em meados da década de 1990, a internet encontrava-se em seu momento de
popularidade, estabelecendo assim uma grande rede interativa e a oportunidade ideal para Sun
Microsystems empregar todo o potencial da linguagem por traz do seu ambicioso projeto. Em
1995 nascia a nova versão do Oak, rebatizada de Java e trazia para o WWW (Word Wide
Web) uma interatividade nunca antes vista, permitindo execução de conteúdo multimídia
direto do navegador.
As vantagens do Java eram muitas, mas o destaque maior era a possibilidade de
escrever uma vez e executar em qualquer local, e por proporcionar uma maior interatividade
na web. A portabilidade que a linguagem proporcionava vinha de sua máquina virtual, a JVM
(Java Virtual Machine). Com a JVM, os programas escritos em Java podiam funcionar em
qualquer plataforma de hardware ou software que possuía uma versão da mesma instalada
tornando as aplicações independentes da plataforma onde funcionam. O sucesso do Java foi
absoluto e rapidamente cresceu o número de usuários da plataforma fazendo grandes
empresas passar a oferecer suporte ao Java [SIER 05].
Em 1999 foi criada a plataforma Java Enterprise Edition (JEE ou J2EE) visando
principalmente a internet e os softwares corporativos. O JEE, mas recentemente chamado de
J2EE consiste em um conjunto de padrões de desenvolvimento que oferecem recursos
(bibliotecas) e funcionalidades para programar software distribuído. O surgimento do J2EE
representou uma grande evolução para o Java trazendo grande produtividade a linguagem. A
introdução do desenvolvimento em camadas, que trouxe maior segurança e organização ao
código.
O sucesso da linguagem fez crescer o número de desenvolvedores Java que no início
dos anos 2000 já dominava o mercado de software. Todavia existiam na plataforma algumas
desvantagens que começavam a por em risco tal sucesso. Essas desvantagens destacavam-se
principalmente por:
Alto nível de complexidade;
Grande consumo de tempo;
24
Tarefas repetitivas;
E a falta de uma diretiva nítida a respeito de quais convenções seguir.
Para ajudar a resolver esses problemas vários frameworks eram criados, sendo o Struts
a melhor referência de sucesso e responsável por influenciar muitos dos frameworks que
surgiram após seu lançamento. O objetivo básico desses frameworks era facilitar o
desenvolvimento de aplicações web na plataforma Java, diminuindo sua complexidade e
aumentando significativamente sua produtividade, mas alguns problemas ainda se repetiam.
Até que o início de um movimento que revolucionaria novamente o ramo se iniciou e uma
nova plataforma de desenvolvimento para web se mostrava promissora a ser grande
concorrente da plataforma J2EE.
Na mesma época do surgimento do Java, outra linguagem era concebida no outro lado
do globo. Em 1993, no Japão, Yukihiro Matsumoto criara uma linguagem que combinava
programação funcional com programação imperativa, cujo objetivo era “ser mais poderosa
que o Perl e mais orientada a objetos que o Python”. Apresentada ao público em 1995, o Ruby
era uma linguagem dinâmica, totalmente orientada a objetos e com grande expressividade e
foi justamente sua expressividade que mais tarde levou o dinamarquês David Heinemeier
Hansson, a utilizá-la para criar um framework objetivando a agilidade e praticidade do
desenvolvimento web.
Hansson criou o Rails inicialmente para um projeto de sua empresa, a 37signals, e
passou a incentivar o uso do mesmo em comunidades da web. Em 2006, o RoR começou a se
popularizar entre os desenvolvedores, disputando com PHP, Python e Java o mercado de
desenvolvimento para web [BARR 09].
Adotando princípios ágeis como Don’t Repeat Yourself (DRY) e Convention over
Configuration (CoC) que tratam, respectivamente, sobre reaproveitamento de código e foco
na convenção com o mínimo de configuração e, utilizando a estrutura em camadas do padrão
MVC, o RoR representou uma ameaça maior ao Java principalmente pelas seguintes
características:
Stack completo: componentes pré-integrados, eliminando a necessidade de
integrar cada componente necessário a uma aplicação;
25
Scaffold: eliminando a repetição de tarefas, criando a base para construção da
aplicação;
Expansibilidade: permitindo criar plugins, de maneira intuitiva, para recursos que
a linguagem não oferece.
O Ruby on Rails trouxe ao mercado de desenvolvimento web a solução de uma
exigência atual, incentivada principalmente pelo Manifesto Ágil de Fevereiro de 2001: A
necessidade de criar ferramentas que permitissem o desenvolvimento de aplicações eficientes
de maneira ágil. Uma aplicação simples em Rails pode ser criada em tempo mínimo e a
imensa comunidade de desenvolvedores que a plataforma apresenta possibilita a troca de
informações que torna o RoR cada vez mais poderoso.
Dentro deste universo ágil os desenvolvedores Java começaram a procurar soluções
para manter a plataforma dentro do que o mercado exigia e assim continuar usufruindo da
enorme quantidade de bibliotecas que o Java apresentava. Surgiu então o projeto JRuby que
seria uma implementação de Ruby dentro da plataforma Java além do projeto Jython, mesma
idéia do anterior só que em Python. Os projetos não tiveram grande aceitação devido a grande
diferença no código de ambas para o que se estava acostumado no Java.
A solução seguinte veio na forma de um novo framework baseado no RoR e utilizando
uma linguagem que não era Java apenas por algumas diferenças, o Groovy, que será mostrada
mais adiante. Groovy com Rails deu origem ao Grails que veio para enfim deixar a
plataforma Java em pé de guerra com os novos modelos de desenvolvimento web.
26
3 O FRAMEWORK GRAILS
3.1 O QUE É GRAILS?
Grails é um framework open source para desenvolvimento de aplicações web na
plataforma Java criado com o intuito de fornecer a mesma um maior nível de abstração, com
enfoque em seguir convenções no lugar de configurar sempre e numa sintaxe mais expressiva
e limpa proporcionada pela linguagem na qual se baseia, a linguagem dinâmica Groovy.
Grails se baseia em ferramentas como Ruby on Rails, Django e TurboGears
assumindo princípios Dry (Don’t Repeat Yourself), CoC (Convention over Configuration) e
KISS (Keep It Simple and Short) que regem as principais características das ferramentas
ágeis. Características essas como:
Evitar tarefas repetitivas;
Necessidade mínima de configuração através do enfoque em seguir uma
convenção;
Proporcionar código simples, pequeno e limpo;
Desenvolvimento em camadas utilizando padrões de projeto como o MVC
(Model-View-Controller).
Grails foi desenvolvido no topo da plataforma Java o que lhe permite uma grande
integração com frameworks, bibliotecas e até uso de código Java. Apresenta um ambiente
completo de desenvolvimento sem a necessidade de preocupar-se com a configuração de
outras ferramentas como servidores de aplicação, por exemplo. Além disso, Grails tem a seu
lado os poderosos e maduros frameworks Hibernate, para mapeamento objeto-relacional
(ORM – Object Relational Mapping), e Spring, para inversão de controle (IoC – Inversion of
Controll), com os quais apresenta um enorme nível de integração. A alta produtividade e
enorme quantidade de funções e ferramentas que apresenta dão ao Grails características de
plataforma em lugar de apenas um framework. Além da compatibilidade com as duas
ferramentas já mencionadas Grails tem ainda em seu quadro outras populares tecnologias de
código aberto como:
27
SiteMesh para processamento de layout;
Jetty e Tomcat como servidores web;
HSQLDB para banco de dados de testes e desenvolvimento;
JUnit para testes, entre outros.
Grails é a solução ágil para plataforma Java na web mais bem sucedida trazendo a
imensa gama de desenvolvedores menos preocupação com detalhes desnecessários que
consumiam tempo do que realmente era importante preocupar-se e tornando novamente
divertido o desenvolvimento na plataforma robusta e poderosa que é o Java.
3.1.1 Origem do Grails
A busca por soluções em ferramentas para o desenvolvimento de aplicações web na
plataforma Java que proporcionasse pouca escrita de código, zero de retrabalho e rápidos
resultados levou um grupo de desenvolvedores americanos a, em 2005, iniciar o projeto de
criação de um framework baseado em Ruby on Rails e utilizando a linguagem dinâmica
Groovy. Surgia assim o Grails, cuja versão 1.0 viria a ser disponibilizada em 2006.
Graeme Rocher trabalhava com Java no desenvolvimento de sistemas de gestão de
aprendizagem quando cansado da enorme quantidade de trabalho que poderia ser evitado lhe
fez questionar se não havia soluções mais simples e rápidas para fazê-los. Mais tarde, quando
começou a trabalhar com TV Digital, Graeme conheceu o Groovy e já era bastante amigável
as poucas regras das linguagens dinâmicas e ao pouco trabalho e tempo que proporcionavam.
No entanto, até o surgimento do Groovy em 2003, nenhuma ferramenta parecia fazer bom uso
de tudo o que já proporcionava a plataforma Java. Tempos depois surgia no mercado web o
framework Ruby on Rails mostrando-se promissor a ser um grande concorrente da plataforma
Java e baseado em uma linguagem até então pouco conhecida entre a maioria dos
desenvolvedores, a linguagem Ruby.
Graeme juntou-se a Steven Devijver e Guillaume Laforge fundando o projeto que se
inspirava nos princípios de RoR e utilizava a linguagem Groovy para assim criar o framework
web Grails.
28
O objetivo era trazer para plataforma Java na web todas as vantagens que as
ferramentas ágeis como RoR, Django entre outras apresentavam e juntar isso ao poder do Java
com sua enorme biblioteca de classes, a enorme quantidade de tecnologias que já possuía e
toda a portabilidade proporcionada pela sua conhecida máquina virtual. Grails surgiu para se
unir ao poder do Java, trazendo soluções que outras ferramentas não conseguiram ajudando a
manter ainda por muito tempo o sucesso que a plataforma já apresentava com uma enorme
gama de desenvolvedores e empresas que não parecem dispostos a abandonar seu poderio.
Grails foi desenvolvido em código aberto, característica comum das ferramentas ágeis,
e após a disponibilização da versão 1.0, a comunidade do framework iniciou atividades e
vários desenvolvedores se juntaram ajudando na melhora do mesmo, criando plugins e
adicionando tecnologias que forneciam suporte ao desenvolvimento em Grails. Hoje vários
são os casos de sucesso que utilizaram Grails no desenvolvimento de aplicações.
Na criação do projeto em 2005, Graeme co-fundou a empresa G2One ao lado de
Guillaume Laforge e Alex Tkachman oferecendo consultoria, treinamento e suporte para as
tecnologias Groovy e Grails. Em 2008, a SpringSource adquiriu a G2One e Graeme junto dos
demais colegas juntou-se a empresa que já era responsável por outras tecnologias do mundo
Java. Grails hoje possui uma enorme quantidade de plugins e várias IDEs que facilitam o
desenvolvimento de aplicações em sua „plataforma‟ entre elas o Netbeans e o Eclipse.
3.1.2 Groovy + Ruby on Rails = Grails
Em 2005 quando iniciava o projeto de criação do Grails, Graeme e sua equipe
batizaram o projeto de Groovy on Rails, devido a forte inspiração nos conceitos e princípios
do RoR, no entanto, David Hansson, criador do Rails pediu para que o nome fosse alterado,
fazendo mudarem para o atual Grails.
Foi a capacidade do Ruby on Rails de gerar soluções rápidas e simples apenas
seguindo convenções sem a necessidade de geração de arquivos XML a todo o momento,
além de apresentar um ambiente completo de desenvolvimento, que fez Graeme se interessar
no que o framework tinha a oferecer. Além disso, RoR apresentava todo o poder e
organização do desenvolvimento em camadas através do padrão MVC, a capacidade de
29
transformar trabalhos repetitivos da geração de CRUD em uma única linha de comando e
possibilitar ao desenvolvedor criar suas próprias soluções quando o mesmo não as oferecia,
através da expansibilidade. Ruby on Rails parecia resolver todos os problemas que o
desenvolvimento em J2EE apresentava. Mas como fazer uso dessas soluções sem abandonar
tudo o que Java já proporcionava?
Antes do surgimento do Grails outras soluções ágeis para a plataforma Java foram
pensadas e algumas com grande sucesso. Uma dessas soluções foi implementar Ruby
(linguagem dinâmica do RoR) em Java criando o JRuby, porém a diferença da sintaxe de
Ruby para Java afastou muitos programadores devido a enorme curva de aprendizado
apresentada. O JRuby é ainda utilizado como solução para muitos casos mesmo não tendo
uma aceitação total no mundo Java.
Assim como Ruby, uma solução em linguagem Python veio através de Jython que
trazia soluções dinâmicas para plataforma Java, mas o problema continuava e os
desenvolvedores Java continuavam desanimados com o trabalho de aprender uma sintaxe
totalmente nova. Foi somente com a criação do Groovy que soluções dinâmicas começaram a
mostrar real sucesso na plataforma Java.
A enorme semelhança e integração de Groovy com Java diminuíam quase totalmente a
curva de aprendizado para os desenvolvedores da plataforma. Juntar Groovy com Rails foi a
idéia de Graeme para tirar proveito de duas das plataformas de desenvolvimento mais
poderosas da atualidade: Java e Ruby on Rails.
3.2 GROOVY: A LINGUAGEM DO GRAILS
3.2.1 O que é Groovy?
Groovy é uma linguagem de programação ágil e dinâmica para
a plataforma Java com muitas características inspiradas em linguagens
como Python, Ruby e Smalltalk, disponibilizando-as para os
desenvolvedores utilizando uma sintaxe similar a do Java [SMIT 09].
30
Groovy surgiu da idéia de um programador Java de trazer para a plataforma uma
sintaxe mais expressiva e comportamento dinâmico. Trabalhar dinamicamente na plataforma
Java foi o diferencial proposto por James Strachan quando criou a linguagem, desta forma
Groovy aproveitaria todo poder de uma plataforma robusta e bem aceita no mercado como o
Java e acrescentaria as vantagens das linguagens dinâmicas.
É comum muitos ainda chamarem Groovy de „apenas mais uma linguagem de script
para plataforma Java‟ e é verdade que a linguagem se sai muito bem executando scripts, mas
seu mérito maior não é esse. Ser a linguagem dinâmica mais amigável com a plataforma Java
é o que dá ao Groovy um grande potencial. Groovy é tão integrado com Java que alguns
ousam a dizer que „Groovy é Java‟. Groovy oferece uma gama de possibilidades que é um
erro afirmar que se trata de apenas uma linguagem de script.
A linguagem Groovy assim como o Java é orientada a objetos, mas também permite
que se trabalhe de forma procedural, pode ser compilada para bytecode Java, pode ser
integrada a aplicações Java sem grandes complicações e faz uso de toda a variedade de classes
que a biblioteca Java oferece, além dos frameworks. Cada tipo de Groovy é um subtipo de
java.lang.Object e cada objeto é uma instância deu um tipo na forma normal. Por exemplo,
um tipo data em Groovy é um java.util.Date [SMIT 09].
Falar da integração de Groovy com Java é pouco e pode levar a se entender que a
linguagem é apenas Java com poucas adições, já que boa parte do que Groovy faz pode ser
feito em Java. Porém a maioria do trabalho que o Java obriga ao desenvolvedor gastar tempo
se preocupando, Groovy faz nos bastidores conseguindo assim a agilidade e dinamismo que
caracterizam a linguagem.
3.2.2 Groovy e Java: Comparação e Integração
Um dos motivos do surgimento da linguagem dinâmica Groovy foi o de trazer para
plataforma Java maior agilidade com menos escrita de código. A capacidade do Groovy de se
integrar com Java, permitindo até misturar código Java na aplicação com um mínimo de
esforço, e a curta curva de aprendizado para o desenvolvedor Java faz da linguagem a solução
ágil mais próxima do sucesso dentre as opções existentes para plataforma. Uma comparação
31
entre as linguagens ressaltando os benefícios que o Groovy traz ao Java, e uma mostra de
como Groovy e Java trabalham lado a lado podem ser dados por simples exemplos.
Uma boa exemplificação da maneira como Groovy se assemelha ao Java é fazendo
uma pequena análise de sua sintaxe. A sintaxe de Groovy é bastante amigável ao que está
acostumado o desenvolvedor Java, com o ganho de permitir que se escreva menos código,
deixando a programação na plataforma mais limpa. O trecho de código a seguir compara a
sintaxe do Groovy com a já conhecida do Java mostrando a pouca diferença entre as duas.
Código Java Código Groovy
import Java.util.*; today = new Date()
Date today = new Date();
Figura 3.1 – Código: Comparação Simples de Java e Groovy
Fonte: [SMIT 09]
Como se observa o código de Groovy tem a mesma função que o correspondente em
Java e grande semelhança com o mesmo, porém sem a necessidade do uso de ponto-e-vírgula,
sem a importação de nenhum pacote e, com sua tipagem dinâmica, sem a necessidade de
declaração de variáveis. Essa pequena amostra serve apenas de introdução ao poder do código
de Groovy sendo eficiente e simples.
Como foi dito antes quase tudo em Groovy é Java, tipos, objetos e a maneira como
Groovy instancia seus objetos seguem a mesma linha de Java. Mas Groovy vai além e permite
que um código completo em Java rode nele sem apresentar erro.
Groovy é considerado por muitos um subconjunto de Java por apresentar grande
integração com a plataforma, porém acrescenta recursos que a plataforma não possuía. Assim
como Java, Groovy é uma linguagem que compila para forma binária (bytecode) e roda na
JVM. Compilar código Groovy para bytecode é uma das maneiras mais simples de integrar
Groovy com Java. Os arquivos .class gerados do Groovy são disponibilizados para Java que
pode fazer uso do mesmo. A Máquina Virtual Java roda Groovy como se estivesse rodando o
próprio Java, mesmo que ajam diferenças em código.
32
Estas aplicações podem se beneficiar da expressividade,
brevidade e dos poderosos recursos do Groovy. Em outras situações,
no entanto, Groovy pode não ser a melhor solução. Isto é
particularmente verdadeiro para aplicações que exigem alto
desempenho, dada o inevitável trade-off 1 entre agilidade e velocidade
em Groovy. [ABDU 09]
O uso do Groovy como solução colaborativa as aplicações Java, fornecendo recursos
que não estão presentes na mesma, é bem vindo, porém tiram todo o poder que a linguagem
pode oferecer a aplicação por si só.
Groovy não tem somente semelhanças e integrações com Java. Há uma gama enorme
de funcionalidades que a linguagem oferece como: blocos de códigos executáveis (closure),
fácil comunicação com base de dados, eficiência em orientação a objetos, entre outros
recursos que serão apresentados a seguir.
3.2.3 Por dentro do Groovy
Já foi descrito que Groovy é uma linguagem dinâmica, ou de script, orientada a
objetos e que também pode ser compilada para bytecode Java através da JVM. A forma como
a linguagem faz uso dessa capacidade híbrida e o poder de tal característica está em sua
sintaxe. Groovy salva a plataforma Java, mas não exclui as vantagens que a mesma já
apresenta, muito pelo contrário adiciona a ela ainda mais produtividade tornando o código
mais legível, limpo, simples e, é claro, ágil.
Em sua declaração de tipos Groovy permite trabalhar de forma dinâmica (script) ou de
forma estática, diferenciando-se de outras linguagens como Ruby e Python que só aceitam a
tipagem dinâmica, isto diminui as críticas que esse tipo de linguagem costuma receber em
relação à detecção de erros em tempo de execução.
Groovy possui a vantagem das linguagens dinâmicas de utilizar o Protocolo de Meta-
Objeto (Meta-Object Protocol ou MOP) que cria para cada objeto um meta-objeto que
1 Trade-off ou tradeoff é uma expressão que define uma situação em que há conflito de escolha. Ele se caracteriza em uma
ação econômica que visa à resolução de problema mas acarreta outro, obrigando uma escolha. Ocorre quando se abre mão de
algum bem ou serviço distinto para se obter outro bem ou serviço distinto. (Fonte: Wikipédia)
33
armazena os métodos separando-os, de certa forma, dos objetos em si. Isso dá ao programador
a vantagem de poder alterar o meta-objeto em tempo de execução, adicionando novas funções
ao objeto.
A função de meta-objeto também pode ser vista como o Duck Typing que é um
conceito utilizado pelas linguagens dinâmicas que traz grande flexibilidade a aplicação. Tal
nome vem da expressão que diz: “Se anda como um pato. Corre como um pato e fala como
um pato é porque é um pato”. Nesse sentido diz-se que a classe deve assumir o
comportamento que lhe for dado, adquirindo este dinamicamente.
class Pessoa {
def nome, sexo
}
Pessoa.metaClass.toString = {
return “Nome: ${nome} sexo: ${sexo}.executou
}
println new Pessoa (nome: “Adriano”, sexo: “M”).toString()
Figura 3.2 – Código: Exemplo do uso de Meta-Objeto
O código acima mostra o exemplo do uso de meta-objeto em Groovy. Em tempo de
execução, o método toString é adicionado a classe dando a aplicação um novo
comportamento. Em linguagens de tipagem forte, como o Java, por exemplo, isso não
ocorreria, pois os métodos são definidos pela classe superior do objeto e isto é fixo. Com duck
type (meta-objeto) isto depende apenas do estado corrente do objeto, em determinado
momento este pode ter 10 métodos e em outro, 15.
Muito do que Java apresenta em sua sintaxe deixa o código pesado e trabalhoso,
Groovy reduz a necessidade de escrita de código deixando opcional o uso desses recursos.
Além da declaração de tipos facilitada pela tipagem dinâmica, quando trabalha-se com
scripting, onde o tipo é opcional, em Groovy é opcional também o uso de modificadores de
acesso, ponto e vírgula e return. Algumas características básicas do Groovy são:
34
Por padrão tudo o que não é especificado com modificador de acesso é visto como
público;
O uso de ponto e vírgula só será necessário em caso de mais de um comando em
uma mesma linha;
A instrução return é opcional, pois se adota o retorno do último comando como
valor de retorno do método ou função;
O conceito de verdade estende o conceito do Java e diz que qualquer valor
diferente de null é verdadeiro, independente de ser do tipo boolean ou não;
Assim como em C++, podem-se subscrever os operadores como: ++, --, >, < etc.
Outro destaque importante da sintaxe do Groovy é a maneira como simplifica o uso de
beans e eleva o uso do princípio DRY (Don’t Repeat Yourself) implementado pelo modelo de
desenvolvimento ágil. Ao contrário do Java, em Groovy não é necessário a declaração dos
métodos gets e sets, isso é feito dinamicamente pela linguagem. O Groovy Beans entende que
se o escopo de uma variável é indefinido os métodos básicos da mesma devem ser criados
dinamicamente, tornando a programação mais limpa e com menos escrita de códigos.
String em Groovy é outro atrativo da linguagem que torna o código ainda mais legível.
As GString, como são denominadas, tem características da linguagem PHP e permitem o
acoplamento de String misturando o conteúdo de uma variável, uma classe Java ou o
resultado de uma expressão numa mesma saída sem a necessidade do uso do operador
mais(+). A seguinte String funciona perfeitamente no Groovy:
def x=10
println “Hoje é: ${new Java.util.Date().toString()} - ${x}”
Figura 3.3 – Código: Exemplo de GString.
Groovy possui ainda muitas funcionalidades e métodos que simplificam e muito o
trabalho do desenvolvedor. Algumas funções importantes e frequentemente utilizadas,
principalmente no framework Grails, são os métodos each e find. O uso de listas e mapas em
Groovy juntamente com esses métodos é um recurso poderoso da linguagem.
35
def lista = [”fornecedor”, “advogado”, “dentista”, “mecânico”]
lista.findAllbyPalavraChave {
it == “advogado”
}.each {
println it
}
Figura 3.4 – Código: Os métodos findAll e each.
O código acima exemplifica o uso dos métodos find e each em uma lista de Strings. O
método findAll procura na lista todas as ocorrências do valor informado em it, que é uma
variável usada no Groovy para implicitamente representar a variável corrente, ou no caso o
valor iterado. O método each retorna então cada (each) ocorrência e imprime na tela o valor
de it.
Groovy apresenta ainda muitas outras ferramentas e funções que melhoram a
programação na plataforma Java, entre eles os GSP (Groovy Server Pages) que serão
discutidos nos tópicos seguintes do Grails, antes, porém é preciso falar de outra grande proeza
do Groovy, o uso de Closure.
3.2.4 Closures
Closure são blocos de código executáveis. Podem ser vistas como variáveis diferentes
das demais que retornam um conjunto de instruções que é executado retornando ou iterando
dentro de variáveis locais no contexto em que está inserida [ABDU 09].
O uso de closure não é novo, mas pouco conhecido. É muito utilizado em linguagens
funcionais seu uso teve início na década de 1960 em linguagens como LISP, por exemplo. No
entanto a maneira como Groovy explora esse recurso é o que o faz surgir como um recurso
aparentemente novo.
A declaração de uma closure é feita da mesma maneira com a qual declara-se as
variáveis comuns, com a diferença de, em lugar de incluir apenas um valor, inclui-se um
36
bloco de código. Devido a essas e outras características é que defini-se as closure como
variáveis.
Em Groovy as closure são uma instancia da classe groovy.lang.Closure e sua
referencia, assim como a declaração é feita da mesma maneira com que referencia-se qualquer
variável convencional.
O uso de closure em Groovy é constante e sua execução dentro de uma aplicação
acontece como a execução de um método onde a única obrigatoriedade para sua execução é o
uso de parênteses. O código a seguir demonstra o uso de closure em Groovy, da declaração à
execução dentro de um contexto.
Closure
Declaração
def save = {
def usuarioInstance = new Usuario(params)
if (usuarioInstance.save(flush: true)) {
redirect(action: "show", id: usuarioInstance.id)
}
else {
render(view:"create", model:[usuarioInstance: usuarioInstance])
}
}
Referência/Execução
def aba = new Usuario(nome:"Adriano Basto", login:"aba", senha:"123456")
aba.save()
Figura 3.5 - Código: Exemplos de Closure
No código acima os itens em destaque demonstram a closure save() que recebe um
bloco de código e logo abaixo sua execução é feita com o objeto chamando-a como chama-se
um método comum.
37
3.3 GRAILS: ALGUMAS TECNOLOGIAS
O poder do desenvolvimento em Grails, além de possuir ao seu lado total integração
com a plataforma Java e todo o poderio da linguagem Groovy, ainda conta com o suporte das
tecnologias open-source reconhecidas e mais elogiadas do mundo Java. Tecnologias essas
como Spring, Hibernate, Sitemesh, Tomcat, entre outras que complementam o Grails e fazem
do framework a poderosa ferramenta que é.
3.3.1 Spring
Uma das tecnologias por trás do Grails é o Spring. Este é um poderoso container de
injeção de dependência que sustenta o framework livrando os programadores de uma
imensidão de trabalho. Porém, mais do que uma ferramenta integrante do Grails, Spring é um
poderoso recurso hoje utilizado por grandes empresas até mesmo pelas que não adotam
código-aberto como exigência em suas aplicações.
Podemos dizer que boa parte do que o Grails executa está ligado ao Spring e todo o
poder de injeção de dependência que o mesmo oferece. O fato de ser conhecido também como
um container de inversão de controle está no núcleo do Spring que é assim chamado. Todavia
o uso do termo pode limitar o framework a apenas essa função quando na verdade o seu foco
principal é a injeção de dependências.
Para entender o real funcionamento do Spring é preciso compreender os conceitos de
Inversão de Controle (Invertion of Control - IoC) e Injeção de dependências (Dependency
Injection – DI) mas podemos dizer que o que o Spring faz basicamente é livrar o
desenvolvedor do trabalho que se tinha, por exemplo, ao empregar o padrão de projeto
factory. O container do Spring encarrega-se de instanciar as classes e definir as dependências
entre elas, permitindo o baixo acoplamento entre classes, um problema enfrentado muitas
vezes pelo uso do factory.
O conceito de Inversão de Controle é visto por muitos como algo genérico uma vez
que consiste de uma característica comum dos frameworks onde, a sequência de chamada de
métodos é tirada das mãos dos programadores para uma infra-estrutura de software que toma
controle sobre a execução. É basicamente esta característica que também diferencia um
38
framework das bibliotecas de classes. Já a idéia de Injeção de Dependência, criada por Martin
Fowler, leva o IoC um pouco mais além. Ao invés de apenas tomar controle sobre os
métodos, o container ‘injeta‟ em cada classe, por exemplo, suas dependências declaradas. Um
módulo montador separado é responsável por injetar a implementação no objeto, assegurando
que qualquer usuário siga esta convenção [CARV 08].
Mas as funcionalidades do Spring vão além, com o objetivo principal de simplificar o
desenvolvimento de aplicações, o framework apresenta um conjunto de módulos que podem
ser utilizados de acordo com a necessidade do projeto. Voltados para persistência de dados,
desenvolvimento web, acesso remoto, entre outros, esses módulos dão ao framework a
possibilidade de ser um ótimo substituto para a plataforma J2EE.
O trabalho do Spring no Grails é fundamentado principalmente na injeção de
dependências, mas o framework está presente também no suporte à persistência de dados,
segurança, gerenciamento de transações do GORM, configuração em tempo de execução,
delegação de controladores, entre outras. Uma aplicação Grails é praticamente desenvolvida
em cima do Spring que viabiliza toda a gama de convenções que o framework segue e dessa
forma faz do Grails a poderosa ferramenta que é. Por isso, um conhecimento avançado do
Grails exige antes um conhecimento também do Spring.
3.3.2 Hibernate
Surgiu com o objetivo de simplificar os processos de persistência de dados para
programação orientada a objetos no modelo relacional de banco de dados. O Hibernate é um
framework feito principalmente para plataforma que integra o pacote de desenvolvimento do
Grails.
Sendo uma ferramenta de mapeamento objeto/relacional, Hibernate atua no Grails
através do GORM (Grails Object Relational Mapping), possibilitando ao desenvolvedor a
pouca escrita de código de acesso a banco de dados e de comandos SQL, acelerando a
velocidade do desenvolvimento de uma forma fantástica.
As características básicas do Hibernate são:
39
Permitir o desenvolvimento de persistência de dados orientada a objetos
naturalmente, incluindo herança, polimorfismo, e encapsulamento;
Não necessidade de tabelas especiais ou campos e gerando a maior parte do
SQL em tempo de inicialização do sistema, em vez de tempo de execução;
Não necessidade de interfaces ou classes base para classes persistentes,
permitindo a persistência de qualquer estrutura de classes ou de dados.
A integração de Hibernate e Grails é bem vinda principalmente porque Grails trabalha
com a maior parte da lógica de negócios na própria aplicação, dependendo pouco de funções
específicas do banco de dados. Dessa forma o framework aproveita vantagens de ambas as
partes para o mapeamento objeto/relacional.
O Hibernate possui uma poderosa linguagem de consulta que é nada mais do que um
dialeto SQL no Hibernate. O poder dessa linguagem está principalmente no fato de ser
totalmente orientada a objetos o que permite a inclusão de características próprias do
paradigma como herança, polimorfismo e encapsulamento.
Os arquivos de configuração do Hibernate são gerados na criação da aplicação em
Grails e localizam-se no diretório grails-app/conf, mas o único trabalho que um
desenvolvedor iniciante tem que ter com o mesmo fica localizado no arquivo
DataSource.groovy que será abordado em tópicos posteriores.
3.3.3 SiteMesh
Para suporte da camada de visualização do Grails a tecnologia presente é o SiteMesh,
uma ferramenta para geração e manipulação de layout de páginas web baseado no padrão
decorador (decorator). O SiteMesh foi originalmente desenvolvido como ferramenta de
código fechado para a plataforma Java passando a código aberto por volta do ano 2000 sob a
licença da OpenSymphony.
O SiteMesh funciona através de um filtro Servlet 2 que intercepta o HTML a ser
retornado para o navegador, extrai dele o conteúdo que lhe é relevante e funde o mesmo
utilizando o modelo decorador. Esse funcionamento fica mais claro quando entende-se a
2 Servlet é um componente do lado servidor que gera dados HTML e XML para a camada de apresentação de um aplicativo Web.
40
diferença básica do modelo que o SiteMesh adota de outro modelo comumente utilizado por
outras ferramentas.
O padrão do modelo decorador (decorator) cria um layout central com espaços vazios
onde apenas os elementos ausentes (não presentes nas visualizações) são preenchidos
deixando os demais espaços para manipulação direta das páginas de visualização. O outro
padrão, conhecido por composição (compositive), é utilizado por ferramentas como o Tiles da
Apache, por exemplo, e tem seu layout central espaços vazios que são preenchidos como um
todo por templates gerados pelas visualizações. Resumindo, pode-se dizer que o padrão do
SiteMesh faz uma fusão dos componentes do layout com os da visualização, enquanto que o
padrão composição faz a inclusão dos componentes da visualização no layout [RUDO 06].
O SiteMesh é parte integrante das ferramentas Grails e por padrão vem configurada
como o provedor de layouts do framework. Os arquivos de layout do SiteMesh são alocados
no diretório grails-app/views/layouts no ato de criação da aplicação grails.
41
Figura 3.6 – Entendendo o SiteMesh
A figura acima mostra de que forma o SiteMesh trabalha no Grails. O arquivo
main.gsp é um layout SiteMesh que possui em sua estrutura 3 (três) tags peculiares:
g:layoutTitle, g:layoutHead e g:layoutBody. No ato de criação da visualização no Grails,
caso um dos elementos referentes a title, head ou body não sejam preenchidos e o uso do
layout criado pelo SiteMesh estiver inserido no cabeçalho da visualização, como mostrado na
figura, os valores padrões serão mesclados aos contidos na visualização.
42
O uso do SiteMesh não é exclusividade do Grails e nem da plataforma Java, a
ferramenta hoje pode ser integrada também a PHP, Python, Perl, entre tantas outras
ferramentas de desenvolvimento web.
3.3.4 Outras Tecnologias
Grails possui integração ainda com muitas outras tecnologias de sucesso no mundo de
software para web, sendo muitas delas padrão do framework e integrantes de seu pacote de
instalação. Além das tecnologias já apresentadas o framework permite integração com:
Tomcat
Um servidor web sob licença livre da Apache Software Fundation que a partir da
versão 1.2 do Grails passou a vir por padrão junto ao framework. Uma ferramenta elogiada no
desenvolvimento web que provê um servidor HTTP puramente em Java. No Grails a grande
contribuição do Tomcat está na facilidade de se fazer o deploy da aplicação. Basta configurar
o arquivo config.groovy da aplicação, localizado em grails-app/conf, com as informações
pertinentes e executar o comando grails tomcat deploy que o deploy da aplicação será
realizado sem grandes problemas.
Jetty
Solução alternativa ao Tomcat como servidor web, um servidor HTTP totalmente
escrito em Java que pode ser integrado ao Grails através de plugin. As vantagens do Jetty são
a sua fácil configuração e o fato de suportar uma carga maior de usuários simultâneos sem
depender do auxilio de outras estratégias.
JUnit
O framework de testes automatizados do Java que vem por padrão entre os
componentes do framework Grails e assim como em Java, permite padronizar os testes da
aplicação. Grails segue o princípio de TDD (Test Driven Development), que em português
refere-se a desenvolvimento direcionado a testes, um princípio comum no meio de
43
desenvolvimento ágil. É através do JUnit que o framework realiza os testes unitários,
funcionais e de integração de toda a aplicação.
HSQLDB
É o banco de dados padrão do Grails. Vem junto ao pacote de instalação do framework
e pré-configurado no DataSource.groovy, porém tem a característica de não ser um banco de
armazenamento constantes. O HSQLD armazena os dados da aplicação Grails em memória
enquanto a mesma está sendo executada e os apaga quando a aplicação é finalizada. Possui as
características de ser bastante leve e simples de ser manuseado e por isso bastante
recomendado para execução de testes.
Quartz
É o agendador de tarefas do Grails. Implementa agendamentos para execução de
códigos em intervalos de tempo regulares. Uma ferramenta open-source que originalmente
integrava o pacote do Grails e hoje pode ser obtida através da instalação de plugin.
3.4 CONHECENDO O GRAILS
Adquirir um conhecimento avançado sobre uma ferramenta nova e com tantas
funcionalidades como o Grails exige uma grande dedicação, não apenas no aprofundamento
do framework em questão, como também das tecnologias que o sustentam, tais como: Spring,
Hibernate, Groovy, e até mesmo Java. Mas, para uma introdução do que o framework já
oferece, pouco tempo é suficiente para conhecê-lo.
3.4.1 Começando com Grails
Trabalhar com o framework Grails, apesar de exigir um conhecimento básico da
sintaxe do Groovy, mostra-se bastante simples, até mesmo nos primeiros momentos, e
principalmente aos já habituados com a plataforma Java. Desde a instalação até o
desenvolvimento através de IDE’s não há grandes dificuldades apresentadas.
44
Ao instalar o Grails o desenvolvedor não precisa se preocupar com nada, exceto ao
fato de ter instalado um pacote JDK 1.4, ou posterior, em sua máquina e configurar as
variáveis de ambiente necessárias. No download e instalação do Grails todas as tecnologias
integrantes como Spring e Hibernate, e até mesmo Groovy, já estão embutidas no pacote de
instalação do framework que aloca tudo em seus devidos lugares para o funcionamento da
“plataforma” de desenvolvimento. Esse procedimento não impede que outras tecnologias de
escolha do desenvolvedor sejam anexadas a plataforma, desde que haja compatibilidade com
a mesma.
Com o Grails devidamente instalado e configuradas as variáveis de ambientes
necessárias, o desenvolvedor deve apenas escolher de que forma pretende escrever seus
códigos e executar os comandos do Grails. O framework é habilitado para executar em prompt
de comando, da mesma forma que funciona perfeitamente em IDEs como NetBeans e Eclipse,
por exemplo. Essas IDEs apresentam compatibilidade com o framework e pouco trabalho de
configuração.
3.4.2 Estrutura de Diretórios e o Padrão MVC
Um conhecimento introdutório do Grails exige antes conhecer a estrutura em camadas
que o framework implementa. Já no ato de criação de uma aplicação através do comando
grails create-app, seja via linha de comando ou via IDE, Grails cria no diretório definido para
aplicação uma árvore de diretórios fundamentada no padrão em camadas MVC.
Como já visto, o padrão MVC separa os recursos principais da aplicação em 3 níveis
(camadas) Modelo – Visualização – Controle, tornando a aplicação organizada, legível e
facilitando a manipulação desses recursos separadamente sem interferir diretamente entre si.
A árvore de diretórios gerada pelo grails create-app demonstrada na Figura 3.6,
apresenta os diretórios: grais-app/domain, grails-app/view e grails-app/controllers. Esses
diretórios são responsáveis por armazenar os arquivos referentes às camadas de modelo de
dados, visualização e controle respectivamente.
45
Figura 3.7 – Árvore de diretórios de uma aplicação Grails
É no diretório grails-app que os recursos mais importantes da aplicação se encontram.
Além dos diretórios já apresentados, outros diretórios e subdiretórios da árvore apresentam
funções específicas dentro da aplicação. A Tabela 3.1 explica as funções básicas de alguns
deles.
46
Tabela 3.1 – Principais diretórios de uma aplicação Grails. Fonte: [DAVI 10]
Diretório Função
grails-app Responsável por manter em seus subdiretórios as classes principais da
aplicação.
grails-app/conf Armazena os arquivos de configuração da aplicação tais como fontes
de dados, mapeamento de URL e arquivos de configuração do Spring e
Hibernate.
grails-app/controllers Armazena as classes de controle da aplicação, o “C” do padrão MVC.
grails-app/domain Armazena as classes de modelo (domínio) da aplicação. Cada arquivo
deste diretório tem uma tabela correspondente no banco de dados.
grails-app/i18n Armazena os recursos de internacionalização da aplicação como
mensagens de erro, por exemplo.
grails-app/services Armazena os serviços da aplicação, que seriam classes que prestam
suporte a lógica de negócio, encapsulando determinados aspectos das
mesmas. Essas classes são gerenciadas pelo Spring.
grails-app/taglib Armazena tags customizadas criadas pelo Grails.
grails-app/utils Armazena arquivos utilitários do Grails tais como codecs e classes
utilizadas por plugins.
grails-app/views Armazena arquivos de visualização da aplicação, os GSP (Groovy
Server Pages). É o “V” do padrão MVC.
lib Responsável por armazenar os arquivos de bibliotecas terceirizadas
como drivers JDBC, por exemplo.
scripts Responsável por armazenar os scripts criados para facilitar o
desenvolvimento da aplicação
src Responsável por armazenar código fonte legado a ser reaproveitado
pela aplicação. Possui subdiretórios que separam código Groovy e
código Java, além de templates. Normalmente guarda código fonte que
não se encaixa facilmente na estrutura de diretórios do Grails.
test Responsável por armazenar as classes de testes unitários, funcionais e
de integração da aplicação.
web-app Responsável por armazenar todo conteúdo estático da aplicação, tais
como: CSS, imagens, ícones, arquivos html.
Grails implementa o MVC muitas vezes dinamicamente, através do comando scaffold
que, com base nos valores e atributos das classes de domínio, gera os controladores e
respectivas visualizações das classes presentes no diretório grails-app/domain. Porém o uso
do scaffold pode se apresentar arriscado dependendo do tipo de problema, como veremos
mais adiante.
47
3.4.3 Persistência de Dados no Grails
Banco de dados em Grails é um assunto interessante e, assim como todas as suas
demais funções, tem um procedimento de configuração mínimo. Além de possuir uma base de
dados pronta para fornecer suporte durante o desenvolvimento (HSQLDB), o framework
permite ainda o acesso a base de dados legadas e a geração automática de comandos SQL
para banco de dados externo como o MySql, por exemplo. O desenvolvedor tem contato
apenas com um arquivo, além das classes de domínio que implementa o modelo de dados e
suas respectivas validações. É neste arquivo que os dados referentes à conexão da aplicação
ao banco de dados ficam armazenados, é nele que ocorre o gerenciamento de todo o trabalho
de configuração executados por ferramentas como Hibernate através do recurso conhecido
como GORM no Grails.
A figura 3.8, apresenta o código do arquivo DataSource.groovy, localizado no
diretório grails-app/conf e responsável por guardar as informações referentes aos bancos de
dados da aplicação. Conforme se observa na figura, é comum o DataSource apresentar 3
sessões de blocos de códigos que possuem cada um uma função específica dentro deste
arquivo, são eles:
Datasource: guarda as configurações da base de dados comum aos 3 ambientes
de execução que o Grails implementa (desenvolvimento, testes e produção),
tais como driver do banco a ser utilizado, dados para acesso ao banco
(username e senha), entre outras;
Hibernate: guarda informações especificas do hibernate que normalmente
ficariam em um arquivo próprio do mesmo, como uso ou não de cachê (true ou
false), exposição ou não de comandos SQL;
48
Figura 3.8 – Código do DataSource.groovy
Environments: composta basicamente de três ambientes de execução guardando
as informações especificas de acesso a banco de dados para cada um deles.
Cada ambiente possui uma extensão da sessão dataSource com as
configurações especificas de: endereço do banco usado e forma de manipulação
do mesmo pela propriedade dbcreate.
Ambiente de execução é um conceito que o Grails implementa para tornar a aplicação
mais eficiente, a idéia é dividir a aplicação em ambientes voltados a cada tipo de execução,
seja ela para testes, desenvolvimento ou produção. No caso do DataSource.groovy,
especificamente, os ambientes referem-se apenas a base de dados onde se pode definir um
banco de dados para testes, um para desenvolvimento e outro para produção, principal
ambiente da aplicação.
Se um mesmo banco de dados for utilizado pelos três ambientes, ou seja, se ambos
compartilharem as mesmas informações, a solução melhor é configurar tais informações na
sessão datasource, que abrange os 3 ambientes da sessão environment.
49
Uma propriedade que merece destaque na sessão environment do exemplo é o recurso
dbcreate. É no dbcreate que o desenvolvedor define se a aplicação deve ou não criar
automaticamente as tabelas nos bancos correspondentes. Essa propriedade aceita quatro
valores e cada qual realiza uma função especifica na execução da aplicação, são eles:
Create: cria a estrutura SQL que gera as tabelas no banco de dados, apagando
dados quando as mesmas já existem no banco;
Creat-drop: cria as tabelas, mas apaga as mesmas no momento em que a
aplicação é finalizada;
Update: atualiza as tabelas existentes e cria novas quando essas não existem no
banco;
Validate: verifica se as classes de domínio e as tabelas nos bancos de dados
estão de acordo umas com as outras.
O uso do dbcreate pode se mostrar arriscado dependendo de qual valor é empregado a
propriedade em cada ambiente. Por exemplo, o uso do create-drop no ambiente de
desenvolvimento pode ser bem vindo, o mesmo não se recomenda ao ambiente de produção.
Na persistência de dados o trabalho do Hibernate realmente começa. O GORM
(Grails Object Relational Mapping), já no ato de criação das classes de domínio, inicia o
mapeamento das informações de cada classe, desde atributos a validações (constraints). É
através do GORM que o Hibernate cria, na execução de métodos como o dbcreate, por
exemplo, as tabelas dos bancos de dados gerando nome, tipo e tamanho dos campos de acordo
com o que está definido nas classes de domínio.
O trabalho do GORM com Hibernate na persistência de dados envolve ainda os
relacionamentos entre classes e suas correspondentes tabelas, a herança das classes entre
tantos outros recursos.
3.4.4 Scaffold: Problema ou Solução?
Um recurso poderoso implementado pelas ferramentas de desenvolvimento ágil para
web, como o Ruby on Rails, por exemplo, é o chamado scaffold. Um recurso que elimina
quase totalmente o trabalho do desenvolvedor criando dinamicamente os componentes da
50
camada de controle e de visualização da aplicação. Grails também possui essa função entre
suas principais funcionalidades e dessa forma permite a criação da base de uma aplicação
simples em pouco tempo. No entanto há casos em que o uso do scaffold pode ser arriscado e
tornar-se mais um problema do que uma solução [ABDU 09].
O uso do scaffold por vezes esconde do desenvolvedor os recursos referentes aos
controladores e as visualizações da aplicação, isso pode ser um problema quando há a
necessidade de se criar códigos específicos, como no caso de criar uma closure para
autenticação, por exemplo.
A utilização do scaffold no Grails é opcional, o framework possui outras
funcionalidades e comandos que geram todo o código básico de controladores e suas
respectivas visualizações. Através do comando grails generate-all, por exemplo, é gerado
para cada classe de domínio o seu correspondente na camada de controle e na camada de
visualização, além das classes de testes, porém, ao contrário do scaffold, este comando não
esconde os arquivos e códigos gerados, apenas facilita sua criação.
A utilização do scaffold é simples e basicamente o que tal função faz dentro do
controlador é gerar o CRUD (Create – Read – Update – Delete) da aplicação. O CRUD de
uma aplicação Grails é composto das propriedades básicas da aplicação, propriedades essas
chamadas em Grails por action (ação). Cada action implementa uma closure com códigos pra
realizar uma função específica de acordo com o que o usuário solicita e, em certos casos, cada
action gera um arquivo .gsp que representa uma visualização daquela ação na camada de
apresentação.
51
Figura 3.9 – Scaffold
A figura acima mostra o código que implementa a funcionalidade scaffold no Grails, o
único conteúdo do controlador que usa scaffold é o demonstrado, escondendo do
desenvolvedor as action que são geradas dinamicamente em tempo de execução.
3.4.5 A camada de Visualização
A segunda camada do modelo MVC é a responsável por tratar as visualizações, ou
seja, tudo o que envolve a aparência da aplicação e a interface de usuário. Grails apresenta em
suas tecnologias poderosos recursos que facilitam a geração de layout e templates, além da
geração das paginas de visualização.
Como foi dito anteriormente, é comum algumas action dos controladores gerarem uma
visualização, isso acontece dinamicamente quando a função scaffold é usada, nesse caso, para
cada classe de domínio/controle é criado um subdiretório em grails-app/views onde os
arquivos de visualização são armazenados. Em Grails os arquivos de visualização tem a
extensão .gsp de Groovy Server Pages. Os GSP contém todo o código que implementam as
interfaces da aplicação e só diferenciam dos arquivos html pela presença de tags especificas
iniciadas por <g:> que fazem valer os recursos exclusivos do Grails.
A Figura 3.10 destaca a presença das tags do grails, além das tags html convencionais,
como por exemplo a tag <g:each></g:each> que, através da função each do Groovy,
percorre uma determinada lista retornando um valor a cada iteração.
52
Figura 3.10 – Exemplo de GSP
A manipulação de layout no Grails é facilitada por tecnologias como o SiteMesh, por
exemplo. O framework possibilita ainda a customização de templates de maneira rápida e
intuitiva e que torna-se ainda mais poderosa se usada em conjunto com a funcionalidade
scaffold.
Grails possibilita a criação de templates parciais em que o desenvolvedor pode definir
em um arquivo .gsp a aparência e conteúdo que terão suas visualizações. Para isso basta
inserir ao início do nome do arquivo gsp o símbolo underline (_) e incluir o mesmo no
diretório grails-app/views/layouts. Após isso é preciso configurar o layout do main.gsp do
SiteMesh, conforme o exemplo do código abaixo.
53
<body><!-- snip -->
<g:render template="/layouts/meutemplateparcial" />
<g:layoutBody />
</body>
Figura 3.11 – Código: Incluindo template parcial
O código mostra que o grails irá renderizar o template através da tag <g:render> e
exibir seu conteúdo nas visualizações que forem criadas. É possível ainda customizar a forma
como o Grails gera as visualizações quando utilizada a função scaffold bastando configurar os
arquivos de templates que tornam-se visíveis ao desenvolvedor ao chamar o comando grails
install templates.
A camada de visualização do Grails ainda é responsável pela manipulação dos
arquivos CSS e possibilita a customização de bibliotecas de marcas (TagLibs).
3.5 CASOS DE SUCESSO
Grails tem colhido frutos dentro da comunidade de desenvolvimento ágil para web e
muitos são os casos de sucesso de aplicações desenvolvidas com o framework. A própria
história do Grails demonstra isso pelo fato de hoje ter o núcleo de gerenciamento de suas
operações de suporte e desenvolvimento de soluções junto de uma das tecnologias mais
elogiadas do mundo Java, o Spring Framework. Parte integrante da SpringSource desde 2008,
o núcleo de gerenciamento do Grails e Groovy liderados por Graeme Rocher e Guillaume
Laforge respectivamente, é dos mais ativos da empresa estando o Grails hoje a caminho da
versão 1.4 e o Groovy na versão 1.8.
Elogiado pela maneira como gerencia memória de suas aplicações, o Grails segue
desmentindo casos que sugerem o não suporte a múltiplos usuários. Segue abaixo alguns
casos de sucesso com grails no Brasil e no mundo:
54
Mins.ms: encurtador URL utilizando Grails no App Engine;
NoiteUniversitária.com.br: site de entretenimento brasileiro feito inicialmente
em Grails 0.4;
AndroidGateway.com: voltado para desenvolvedores de aplicativos do
Android;
ExperienceOz.com: site ecommerce para passeios turísticos na Austrália;
ImoveisnoMorumbi.com.br: site da agência imobiliária brasileira focada em
clientes de alto nível. Possui mais de 100 mil requerimentos por dia e funciona
com dois servidores Tomcat.
55
4 ESTUDO DE CASO
Este estudo de caso tem por objetivo apresentar uma introdução prática do uso do
framework Grails como ferramenta de desenvolvimento para aplicações Web. Para
exemplificar o funcionamento do framework uma aplicação simples foi elaborada explorando
um problema real e que demonstra parte das principais tecnologias que compõem o Grails.
Por tratar-se de uma ferramenta ágil, o processo de desenvolvimento se baseou na
metodologia Scrum, utilizando seus recursos para gerenciamento do projeto.
4.1 DESCRIÇÃO DO PROBLEMA
Para conquistar a confiança e manter o bom relacionamento com seus clientes, uma
das preocupações que as empresas devem ter diz respeito às formas de venda com as quais
trabalha. É pensando nisso que a empresa Gesso Arte de Araruama oferece a seus clientes
mais antigos e fiéis uma modalidade de venda exclusiva, as vendas a prazo.
A Gesso Arte de Araruama é uma empresa do ramo de construção civil que oferece
soluções em serviços e venda de produtos de gesso e derivados. A empresa mantém o
gerenciamento de suas informações em planilhas e fichas de papel, porém, o problema de se
controlar tais informações fez surgir a necessidade de implantar um sistema informatizado
que, de maneira rápida, resolvesse ao menos parte dos seus problemas.
4.2 O SISTEMA
O sistema proposto para resolução do problema apresentado visa gerenciar as
informações da Gesso Arte através de uma aplicação web, dividida em módulos que
corresponda a cada departamento ou operação que a empresa realiza. Por possuir sede e uma
filial em endereços distintos e pelas vantagens que o modelo web e a plataforma JVM
proporcionam é que foi sugerido o desenvolvimento desta aplicação no framework Grails.
Usando as vantagens do framework e da plataforma Java, o projeto será desenvolvido
tendo a metodologia Scrum como base, seguindo os passos do modelo ágil de
desenvolvimento sem o foco em documentação ao extremo. Os módulos nos quais o sistema
56
se divide, serão representados no Scrum como sprints, e para demonstração nesse estudo de
caso, apenas um sprint será desenvolvido, gerando ao seu final uma parte funcional do
sistema.
4.2.1 Desenvolvendo em Scrum
O modelo de desenvolvimento ágil oferece algumas ferramentas que visam orientar os
projetos de software de forma rápida e sem grandes complicações. Dentre estas ferramentas o
Scrum é uma das mais conhecidas e utilizadas que trabalha basicamente com 3 premissas: a
definição de papéis (ScrumMaster, dono do produto, equipe), reuniões periódicas
(planejamento, revisão, retrospectiva, reunião diária) e criação de artefatos (product backlog,
sprint backlog, burndown charts).
Um desenvolvimento baseado em Scrum não necessariamente precisa seguir
rigidamente essas regras. Pode-se adotar a inclusão de diagramas do modelo UML ou a
exclusão de alguns artefatos do Scrum de acordo com cada projeto e com o que for melhor
para a equipe desenvolvedora. No caso específico deste estudo de caso o uso de alguns dos
artefatos do Scrum é priorizado e os papéis servem apenas para identificar os envolvidos no
processo. Não há a necessidade de descrição detalhada das reuniões ou criação de muitos
diagramas para o sprint.
Começando com o planejamento do projeto o problema apresentado pela Gesso Arte
deu origem ao Product Backlog mostrado na Tabela 4.1. O Product Backlog da tabela lista
apenas algumas das estórias, ou seja, funcionalidades, que o sistema deve possuir.
O dono do produto pontua por importância (IMP) o que, a seu ver, deve ser executado
primeiro. É com base nessas informações que o primeiro sprint do projeto é criado.
Ao analisar o quadro do Product Backlog a equipe e ScrumMaster tem de pensar em
estórias que levem em consideração a pontuação do dono do produto (IMP), o tempo
estimado para cada estória (EST) e decidir quais estórias podem entrar no sprint para
proporcionar um resultado funcional ao final do mesmo, num período de pouco tempo. É
nesse momento também que se define um objetivo para o sprint. Todo sprint deve possuir um
objetivo que deve ser descrito durante a reunião de sprint, no caso deste problema o objetivo
do primeiro sprint foi o de retornar uma primeira versão funcional do sistema que realize
algumas das funcionalidades listadas no product backlog.
57
Tabela 4.1 – Product Backlog
ID NOME DA ESTÓRIA IMP EST COMO DEMONSTRAR
1 CADASTRO DE CLIENTES 10 1
Usuário faz login, clica em clientes, na
página clientes seleciona "criar novo",
insere os dados e clica em salvar.
2 CADASTRO DE
FUNCIONÁRIOS 5 3
Usuário faz login, clica em
funcionários, e se for autorizado de
acordo com seu papel na aplicação vai
a pagina funcionário seleciona "criar
novo", insere os dados e clica em
salvar.
7 CONTROLE DE VENDAS 10 8 Possuirá dependência direta do
controle de estoque e de caixa.
8 CONTROLE DE VENDAS
A PRAZO 10 2
Se a forma de venda for a prazo, clica-
se em Venda a prazo, clica em "Criar
nova", seleciona cliente por código e
nome, insere dados da venda e salva.
9 CONTROLE DE CAIXA 8 10 Depende do controle de vendas,
serviços, compras, despesas
15 REALIZAR VENDA 10 10
Clica em realizar venda na sessão
venda, insere os dados da venda e
imprime cupom fiscal
16 CONSULTAR VENDAS A
PRAZO 10 2
Digita um dado sobre a venda no
campo buscar da sessão vendas a
prazo e clica em buscar
17 CONSULTAR CLIENTES 10 3
Digita um dado sobre o cliente no
campo buscar da sessão clientes e
clica em buscar
18 LISTAR CLIENTES 10 1 Clica em cliente e uma lista de clientes
cadastrados é exibida
21 LISTAR VENDAS A
PRAZO 10 2
Clica em vendas a prazo e uma lista de
vendas é exibida
22 CADASTRO DE
USUÁRIOS 10 1
Usuário com permissão "cria novo
usuário" fornecendo dados de nome,
login e senha.
23 CONTROLE DE ACESSO 10 1
Verificar autenticidade do usuário, se
o mesmo se confirmar permite o
acesso, caso contrario retorna a tela
anterior.
ID = Identificador da estória - IMP = Importância - EST = Estimativa em Dias
58
Da análise do Product Backlog criou-se o primeiro sprint backlog, que após aprovação
do dono do produto seguiu para o desenvolvimento. A tabela 4.2 demonstra o sprint backlog.
Tabela 4.2 – Sprint Backlog
ID NOME DA ESTÓRIA IMP EST COMO DEMONSTRAR
1 CADASTRO DE CLIENTES 10 1
Usuário loga-se, clica em clientes, na
página clientes seleciona "criar novo",
insere os dados e clica em salvar.
2 CONTROLE DE VENDAS A
PRAZO 10 2
Se a forma de venda for a prazo, clica-se
em Venda a prazo, clica em "Criar
nova", seleciona cliente por código e
nome, insere dados da venda e salva.
3 CONSULTAR VENDAS A
PRAZO 10 2
Digita um dado sobre a venda no campo
buscar da sessão vendas a prazo e clica
em buscar
4 CONSULTAR CLIENTES 10 3
Digita um dado sobre o cliente no
campo buscar da sessão clientes e clica
em buscar
5 LISTAR CLIENTES 10 1 Clica em cliente e uma lista de clientes
cadastrados é exibida
6 LISTAR VENDAS A PRAZO 10 2 Clica em vendas a prazo e uma lista de
vendas é exibida
7 CADASTRO DE USUÁRIOS 10 1
Usuário com permissão "cria novo
usuário" fornecendo dados de nome,
login e senha
8 CONTROLE DE ACESSO 10 1
Verificar autenticidade do usuário, se o
mesmo se confirmar permite o acesso,
caso contrario retorna a tela anterior.
9 ...
10 ...
11 ...
Levando em conta o sprint gerado, a aplicação pode seguir o desenvolvimento. Com o
Grails como framework e as várias tecnologias que o acompanham, o primeiro módulo da
59
aplicação teve seu desenvolvimento inciado objetivando a implementação da parte do sistema
que faz o controle das vendas a prazo da empresa.
4.2.2 Preparando o ambiente para o sistema.
Para o desenvolvimento da aplicação em Grails algumas tecnologias e ferramentas
serão utilizadas, tais como:
o servidor HTTP Tomcat;
a IDE NetBeans;
o plugin Searchable;
o framework de testes JUnit;
o banco de dados MySql;
o gerenciador de layout SiteMesh;
o framework para modelo objeto-relacional Hibernate;
o framework para injeção de dependências Spring.
A versão do Grails utilizada nesta aplicação é a 1.2.2, estando o framework hoje em
sua versão 1.4. As demais ferramentas, com exceção do MySql, estão integradas na instalação
do framework Grails. A versão do MySql usada foi a 5.1.
Com a criação da base da aplicação e da estrutura de diretórios, o primeiro passo foi
customizar a aplicação para que ela agisse da maneira como se esperava, personalizando o
Grails para que fosse possível a implementação futura de estruturas de autenticação, por
exemplo, sem a necessidade de retrabalho. Este procedimento torna-se possível através da
instalação de templates que exibem uma lista de arquivos a serem configurados. Nesses
arquivos informa-se de que forma agirão os controladores, classes de domínio, visualizações,
testes, entre outros componentes quando usada a funcionalidade scaffold.
4.2.3 Classes de domínio e Controladores
O projeto do primeiro sprint partiu então para a criação das classes de domínio da
camada de modelo da aplicação. Nota-se pela análise das estórias do sprint a presença de
estórias com características semelhantes a outras do product backlog e que, com o recurso de
60
herança do modelo OO (Orientado a objetos), poupará o trabalho nos sprints posteriores. Uma
das estórias dessa questão é o “Cadastro de Clientes”, com a criação da classe Pessoa será
possível o reaproveitamento dos atributos da mesma através da herança dessa classe para a
classe de domínio Cliente e posteriormente para as classes Funcionário e Fornecedor.
Como já dito anteriormente, o modelo de desenvolvimento ágil e metodologias como
Scrum não possuem a necessidade de criação de todos os diagramas implementados pelo
modelo UML, no entanto não proíbem o seu uso. Para um melhor entendimento das classes
da aplicação solicitadas pelo dono do produto para esse primeiro sprint, torna-se importante a
criação do Diagrama de Classes apresentado na figura 4.1.
Figura 4.1 – Diagrama de Classes
Além das classes de domínio Pessoa.groovy e Cliente.groovy, tornou-se necessária a
criação da classe de domínio Usuario.groovy para possibilitar a criação do mecanismo de
acesso ao sistema. Para concluir as implementações da camada de modelo, a classe de
domínio VendasaPrazo.groovy foi criada para guardar as informações referentes a modalidade
de venda a prazo da empresa e realizar assim o objetivo do sprint de entregar algo funcional.
61
As figuras a seguir apresentam o código de implementação de algumas dessas classes criadas
em Grails, com seus respectivos atributos, validações e relacionamentos.
Figura 4.2 – Classe de domínio: Cliente.groovy
As figuras mostram o código com os atributos das classes conforme informado pelo
dono do produto no Diagrama de Classes, estendendo das classes pai alguns atributos e
acrescentando novos. Como se observa, mesmo na herança, a diferença para a sintaxe Java é
pouca e por isso a grande aceitação do Grails entre os desenvolvedores da plataforma.
As classes de domínio implementam ainda as validações (constraints) e o
relacionamento que as mesmas terão umas com as outras. No caso do problema deste sprint
deseja-se criar o sistema para controle de vendas a prazo, a classe Cliente exibida acima terá
relacionamento um-para-muitos com a classe VendasaPrazo, exibida na figura 4.3.
62
Figura 4.3 - Classes de domínio: VendasaPrazo.groovy
Algumas classes de domínio da aplicação terão seus respectivos controladores gerados
através do método scaffold do Grails, porém classes específicas com métodos próprios
evitarão este recurso por apresentar a necessidade de incluir informações além do que seria
gerado. A classe Pessoa, por exemplo, por ser usada para herança de outras classes, não
apresentou problemas no uso do scaffold.
4.2.4 Provendo Segurança do Sistema
A criação dos mecanismos de segurança do sistema começa pela classe de domínio
Usuario.groovy e seu respectivo controller. Chamam-se mecanismos de segurança nesta
aplicação as rotinas de autenticação de usuários e a codificação de senhas, além da limitação
de privilégios de acordo com o papel de cada usuário no sistema.
Grails oferece soluções prontas para autenticação de usuários, através do uso de
plugins, mas no caso deste problema a mesma se deu através de código programado
diretamente nos controladores e classes utilitárias.
63
Para o controle do acesso, a classe de controle Usuario recebeu a implementação das
closure login, authenticate e logout, cujos códigos possibilitam respectivamente o acesso,
autenticação e saída de sessão no sistema.
A closure login não possui código pois serve apenas para as chamadas à visualização
login.gsp que recebe os dados do usuário e realiza a autenticação conforme código da closure
authenticate. O código da closure logout realiza o encerramento da sessão do usuário.
Ainda no que diz respeito a controle de acesso, a implementação de uma classe filtro
para interceptar as tentativas de acesso e direcioná-las de acordo com a limitação do papel de
cada usuário é feita pela classe AdminFilters.groovy. Este filtro verifica se a sessão do usuário
encontra-se ativa e se o papel do usuário no sistema permite o acesso a determinada área
direcionando-o ou não a área seguinte.
Para a proteção de senhas de acesso é utilizado uma classe utilitária que codifica as
senhas geradas através do método encodeAsBase64. As senhas ficam assim protegidas sendo
gravadas nas bases de dados de modo mais seguro. Grails possibilita esses entre outros
recursos para segurança, porém por tratar-se de simples acesso a aplicação reservou-se a
implementação de mecanismos mais poderosos de segurança para os sprints posteriores do
projeto.
4.2.5 Desenvolvendo direcionado a Testes
Seguindo a „tradição‟ do modelo ágil de desenvolvimento a execução de testes
automatizados se faz necessária durante todo o processo de desenvolvimento, assim antes de
seguir para persistência de dados, foram criadas classes de testes para garantir a estabilidade
da aplicação evitando erros.
No ato de criação de classes de domínio, controle e de biblioteca de marcas, Grails
automaticamente cria uma classe teste para cada uma. Os testes implementados por essas
classes são conhecidos por testes unitários que testa partes da aplicação separadamente antes
de integrar-se a banco de dados ou servidores HTTP, por exemplo. Para realizar estes testes o
Grails utiliza o pacote de testes, que se baseia no framework de testes JUnit e, por sua vez,
cada classe de teste unitário em Grails é uma extensão de GrailsUnitTestCase, para classes de
domínio; ControllerUnitTestCase, para controladores; e TagLibUnitTestCase, para
64
bibliotecas de marca. A figura 4.4 ilustra a hierarquia das classes de testes do Grails incluindo
as classes de teste desta aplicação.
Figura 4.4 – Diagrama Hierárquico das Classes de Testes
Os testes necessários para essa primeira parte da aplicação objetivam-se a testar
validação das classes de domínio e o funcionamento de algumas rotinas dos controladores,
desta maneira evitando antecipadamente a presença de erros.
No decorrer dos testes unitários, por exemplo, foram notados erros nas classes de
domínio e de controle Usuário, o que permitiu a correção dos mesmos antes de prosseguir
com o restante do desenvolvimento. Isso mostra importância do uso de testes durante o
desenvolvimento e não somente após a aplicação pronta, com a realização dos chamados
testes de caixa branca. O desenvolvimento direcionado a testes é algo natural nas tecnologias
ágeis e exigência do modelo ágil de desenvolvimento, isso garante maior precisão no código e
consequentemente a diminuição do retrabalho.
Em Grails, por tratar-se de uma ferramenta em que os objetos e métodos só são
instanciados e chamados em tempo de execução, para execução de testes existe o recurso
chamado mock que simula uma aplicação em execução no próprio código, garantindo o
sucesso do teste.
Para visualizar os resultados dos testes, Grails guarda no diretório da aplicação um
arquivo HTML que mostra se o teste passou ou não, ou se apresentou algum erro que impedia
65
o seu sucesso. A figura 4.5 apresenta a tela do resultado dos testes da aplicação, com os
respectivos erros ou falhas.
Figura 4.5 – Tela Unit Test Results
Antes da persistência de dados, porém testes de execução da aplicação também são
necessários e um recurso que auxilia este tipo de teste populando os campos das classes para
comprovar o sucesso das rotinas de código é o BootStrap do Grails.
No BootStrap as classes podem assumir valores criando instâncias das mesmas que em
tempo de execução fazem o preenchimento dos campos permitindo-se analisar, por exemplo,
o estado das visualizações como list, show e edit; e os testes de acesso e autenticação de
usuário.
4.2.6 Conectando ao Banco de Dados
Para persistência de dados da aplicação foi escolhido o banco de dados MySql pelas
característica de ser de simples configuração e apresentar bastante compatibilidade com o
Grails. Usando os recursos da linguagem, a conexão do banco ao sistema não apresentou
grandes problemas e o trabalho de criação de estrutura SQL ficou a cargo do GORM do
Grails junto ao framework Hibernate, através do método dbcreate configurado no
DataSource.groovy.
66
Durante o desenvolvimento, porém o uso do BootStrap do Grails juntamente com o
banco de dados padrão do framework, o HSQLDB, foi de grande ajuda para testes das funções
implementadas até que por fim os bancos de dados foram criados no MySql.
Utilizando a divisão de ambientes do DataSource, foram criados 3 bancos de dados
para: desenvolvimento, testes e produção. O recurso foi utilizado pensando na continuidade
do sistema nos sprints posteriores.
4.2.7 Visualizações da aplicação
Para a camada de visualização da aplicação não houve exigência específica a respeito
de design. Para cada classe de domínio foi criado um diretório que continham as visualizações
básicas geradas através do comando grails generate all. Cada visualização criada no diretório
está diretamente ligada a determinadas closures da classe de controle, assim as views: list.gsp,
create.gsp, show.gsp e edit.gsp, não apresentaram grandes alterações do que foi gerado pelo
Grails, mas a inclusão de views extras foi necessária, como o caso do login.gsp.
Através do SiteMesh, que o Grails apresenta para o suporte a layout, foi possível
personalizar a aplicação criando bibliotecas de marcas e templates parciais que ofereceram
recursos para simplificar o acesso as partes do sistema. Através do SiteMesh também foi feita
a inclusão do logotipo da empresa no topo da aplicação e a página principal gerada pelo
Grails foi alterada para apresentar os recursos de forma mais organizada.
As figuras a seguir apresentam as principais telas da aplicação:
67
Figura 4.6 – Tela Principal
Figura 4.7 – Tela de Login
68
Figura 4.8 – Tela Listar Clientes
Figura 4.9 – Tela Mostra Usuário
69
Figura 4.10 – Tela Criar Venda a Prazo
4.2.8 Trabalhando com plugins
Durante o desenvolvimento da aplicação uma mudança no „escopo‟ do sprint foi
pedida pelo dono do produto. Ao acompanhar o desenvolvimento e observar o modelo de
consulta criado através de listagem o dono do produto pediu que fosse implementado um
mecanismo de busca rápida que retornasse as principais informações dos clientes cadastrados.
O desafio de desenvolver algo para esse pedido foi facilmente solucionado por um dos
recursos oferecidos pelo Grails. Através da instalação do plugin Searchable, foi possível criar
o mecanismo de busca solicitado.
O uso de plugins é uma característica que garante a expansibilidade e o
aproveitamento de código do Grails, duas das características básicas das ferramentas ágeis. O
plugin em questão foi então inserido ao projeto através do comando grails install plugin e
devidamente configurado para possibilitar a busca de informações de clientes. Apesar de estar
integrado ao projeto, os plugins instalados através deste comando são alocados em um
diretório diferente do diretório fonte da aplicação. Os códigos compilados pelo Grails são
70
alocados no diretório .grails, e neste mesmo diretório são alocados os arquivos dos plugins
instalados sendo os mesmos chamados em tempo de execução.
O trabalho por traz do plugin Searchable está na implementação de um serviço que
caracteriza-se por possibilitar lógica de negócio e comportamento a mais de uma classe de
domínio. O trabalho por traz dessa proeza fica por conta do Spring framework através da
injeção de dependência entre essas classes.
Na inserção de mais essa funcionalidade ao sistema, foi necessária ainda a criação de
uma view que apresentasse o resultado das buscas feitas pelo Searchable, bem como da
inclusão do respectivo formulário de busca. A tela da figura abaixo mostra o resultado de uma
busca simples no sistema.
Figura 4.11 – Tela Mostrar Resultados da busca
4.2.9 Implantando a aplicação
O último passo para que o resultado do primeiro sprint possa ser apresentado diz
respeito a preparar a aplicação para ser implantada. Até o momento toda a execução da
aplicação ocorreu no ambiente de desenvolvimento, através do comando grails run-app. Para
71
que a aplicação funcione no modo de produção em Grails, necessita ser gerado um arquivo
WAR (Web Application Archive) que prepara a aplicação com todos os componentes que a
integram deixando a pronta para a realização de deploy no Tomcat ou outro servidor utilizado.
Um detalhe importante da configuração dessa etapa é a atenção exigida com as
informações inseridas nos arquivos de configuração da aplicação, localizados no diretório
grails-app/conf. Em DataSource.groovy é preciso certificar-se de que as configurações de
conexão e acesso a bases de dados estejam corretamente implementadas.
No arquivo Config.groovy é feita então a configuração do Tomcat, ou outro servidor,
para que o mesmo realize o deploy da aplicação corretamente. Neste caso, é preciso observar
na configuração do servidor o tipo de usuário que possui permissão para realizar o deploy e
caso o mesmo não exista providenciar sua criação. No Config.groovy informa-se nome de
usuário, senha e a url da aplicação, incluindo o endereço da porta do servidor.
Com tudo devidamente conferido, o comando grails war cria o arquivo .war com o
nome e versão da aplicação contido no arquivo application.properties e que, no caso da
aplicação em estudo, gerou o arquivo: SisGesso-0.1.war. Um diretório de nome /war dentro
do diretório .grails/1.2.2/projects/SisGesso armazena todos os componentes da aplicação
desde as classes de domínio e controladores até as imagens e templates da camada de
visualização, conforme demonstra a estrutura da árvore de diretórios da figura 4.12.
72
Figura 4.12 – Árvore de diretórios war.
Com o arquivo .war gerado é preciso apenas instalar o mesmo no servidor de
aplicações escolhido, tendo o Grails compatibilidade com vários dos servidores da plataforma
java.
Finalizado o primeiro sprint da aplicação o sistema pode ser entregue ao cliente para
uso e retorno de informações para o prosseguimento nos sprints posteriores. O Grails permite
fácil manipulação das estruturas de códigos permitindo recuperar-se até mesmo depois de um
deploy sem sucesso através do comando undeploy no servidor de aplicações escolhido.
73
5 CONSIDERAÇÕES FINAIS
5.1 CONCLUSÕES
O atual do mercado de desenvolvimento de software tem exigido ferramentas que
facilitem o trabalho dos desenvolvedores proporcionando o maior aproveitamento do tempo
que dispõem e voltando o foco para a criatividade no processo de desenvolvimento. Desta
busca por soluções foi que se expandiu o uso de frameworks na intenção de livrar o
desenvolvedor de uma gama de trabalho repetitivo e detalhes técnicos.
Este trabalho fez uma introdução ao framework Grails surgindo como solução para a
plataforma Java nas novas exigências de mercado. Foram citadas as principais tecnologias e
funcionalidades do Grails e de que forma o mesmo está integrado ao Java.
Através de comparações ressaltou-se as vantagens e desvantagens da plataforma Java
perante o Ruby on Rails, plataforma de desenvolvimento web de grande crescimento nos
últimos anos, e que serviu de inspiração para o surgimento do Grails. Apresentou-se ainda a
linguagem de programação Groovy, numa rápida introdução a suas características e
funcionalidades.
O trabalho apresentou ainda um estudo de caso que demonstrou todo processo de
desenvolvimento de uma aplicação através do framework Grails. Procurou-se demonstrar de
forma prática a utilização da metodologia ágil Scrum para exemplificar todo processo de
gerenciamento de software do modelo ágil de desenvolvimento. Através do Scrum foram
listadas as funcionalidades de um problema real e criou-se um módulo de uma aplicação para
solução do mesmo.
O uso do framework Grails no desenvolvimento de aplicações web mostra-se
promissor principalmente aos desenvolvedores da plataforma Java, já acostumados as
tecnologias que o Grails incorpora. Porém, a curta curva de aprendizado faz do framework
uma ótima solução para desenvolvimento de aplicações simples e de rápido desenvolvimento.
Foi mostrado que o objetivo de ferramentas como o Grails é fazer o trabalho do
desenvolvedor ter enfoque ao que realmente se espera do mesmo: a criação de novas soluções
em softwares sem grandes preocupações com configurações o tempo todo como ocorre com
muitas plataformas. Seguindo princípios como „Convention over Configuration’ e „Don’t
74
Repeat Yourself’, Grails faz jus ao que prega o Manifesto Ágil de 2001 para desenvolvimento
de soluções de software.
Espera-se com isso ter conseguido produzir conhecimento introdutório que motivem
aos desenvolvedores na busca constante por atualizações e o conhecimento de novas
ferramentas para o processo de desenvolvimento de software.
5.2 TRABALHOS FUTUROS
O sistema proposto no estudo de caso apresentado mostrou a criação de apenas um
módulo do mesmo, representado através do sprint do Scrum. Para trabalhos futuros pretende-
se dar continuidade ao desenvolvimento do mesmo implementando outros módulos em
sprints posteriores.
Desta forma pretende-se ainda buscar um maior aprofundamento das funcionalidades e
recursos proporcionados pelo Grails para desenvolvimento de software para web.
75
REFERÊNCIAS BIBLIOGRÁFICAS
[ABDU 09] ABDUL-JAWAD, Bashar. Groovy and Grails Recipes. United States of
America: Apress, 2009.
[BARR 09] BARRETO, Juliano. Histórias do pai do Ruby on Rails. Disponível em:
<http://info.abril.com.br/professional/desenvolvimento/o-rails-e-lindo.shtml?3>. Acessado em
14/04/2011 às 18 horas e 20 minutos.
[BROW 09] BROWN, Jeff & ROCHER, Graeme. The Definitive Guide to Grails (2nd
ed.).
United States of America: Apress, 2009.
[CARV 08] CARVALHO, Marlon. Spring Framework: Introdução. Disponível em:
<http://imasters.com.br/artigo/4497/spring_framework_introducao>.
Acessado em 25/05/2011 às 16 horas e 20 minutos.
[DAVI 10] DAVIS, Scott & RUDOLPH, Jason. Getting Started with Grails – Second
Edition. United States of America: InfoQ, 2010.
[DEPA 10] Departamento de Sistemas e Computação da UFCG. O que são frameworks?
Disponível em <http://www.dsc.ufcg.edu.br/~jacques/cursos/map/html/frame/oque.htm>.
Acessado em 31/05/2010 às 15 horas.
[KÖNI 07] KÖNIG, Dierk. Groovy em Ação (Ed. Traduzida). Rio de Janeiro: Alta Books,
2007.
[MACO 10] MACORATTI, José Carlos. Padrões de Projeto: O modelo MVC – Model
View Controller. Disponível em < http://www.macoratti.net/vbn_mvc>. Acessado em
31/05/2010 às 15 horas.
[MORE 09] MOREIRA, Daniela. Desenvolvimento ágil funciona? Disponível em
<http://info.abril.com.br/noticias/ti/desenvolvimento-agil-funciona-30062009-3.shl>.
Acessado em 16/05/2010 às 23 horas e 50 minutos.
[RODR 02] RODRIGUEZ, Martius Vicente Rodriguez y. Gestão do Conhecimento nas
Empresas. Rio de Janeiro: E-papers Serviços Editoriais, 2002.
[SIER 05] SIERRA, Kathe & BATES, Bert. Use a cabeça! Java (2ª ed). Rio de Janeiro: Alta
Books, 2005.
76
[SMIT 09] SMITH, Glen & LEDBROOK, Peter. Grails in Action (1st ed). United States of
America: Manning Publications, 2009.
77
ANEXO A – CÓDIGO FONTE
// Classe de Domínio Cliente.groovy
package sisgesso
class Cliente extends Pessoa{
String email
String telCel
String outrasInf
static hasMany = [vendasaprazos: VendasaPrazo] //o método hasMany define o relacionamento
// um-para-muitos (um cliente tem muitas vendas a prazo)
static searchable = true
String toString(){"${this.nome}" - "${this.tel}"}
static constraints = {
email(email:true)
telCel()
outrasInf()
}
}
Cliente.groovy
//Classe de domínio Pessoa.groovy
package sisgesso
class Pessoa {
String nome //Tambem receberá razão social sem diferenciação especifica
String cpf //ou cnpj, caso pessoa jurídica, no mesmo campo
String rua
String numero
String bairro
String complemento
String cep
String cidade
String uf
String tel
static opcional = ["cpf","complemento", "tel"] //Campos não obrigatórios
static constraints = {
nome(maxSize:130,blank:false)
cpf(maxSize:18)
rua()
78
numero()
bairro()
complemento()
cidade ()
uf(inList:['AC','AL','AM','AP','BA','CE','DF','ES','GO','MA','MG','MS','MT','PA',
'PB','PE','PI','PR','RJ','RN','RO','RR','RS','SC','SE','SP','TO'],blank:false)
cep()
tel()
}
static mapping = {
sort "nome" // listar por ordem alfabética do nome
}
}
Pessoa.groovy
// Classe de domínio Usuario.groovy
package sisgesso
class Usuario {
String nome
String login
String senha
String papel = "usuario"
static transients = ['admin']
boolean isAdmin(){
return papel == "admin"
}
static constraints = {
nome()
login(blank:false, nullable:false, unique:true)
senha(blank:false, password:true)
papel(inList:["usuario","admin"])
}
String toString(){
login
}
def beforeInsert = {
senha = senha.encodeAsSHA()
}
}
Usuario.groovy
//Classe de domínio VendasaPrazo.groovy
package sisgesso
class VendasaPrazo {
Cliente cliente
int numVenda
Date dataVenda
BigDecimal valorTotal
BigDecimal valorPago = 0
BigDecimal valorReceber
79
Date dataPagamento = new Date()
String toString (){
"${this.numVenda}" - "${this.valorTotal}"
}
static belongsTo = [Cliente] //o método indica o relacionamento que a classe tem com Cliente
//(pertence a Cliente)
static optionals = ["valorPago", "valorReceber"]
def beforeInsert = {
valorReceber = valorTotal - valorPago
}
static constraints = {
cliente()
numVenda(blank:false,unique:true)
dataVenda(format:"dd/mm/aaaa")
valorTotal (min:0.0,scale:2)
valorPago (nullable:true)
valorReceber (nullable:true, display:false)
dataPagamento (format:"dd/mm/aaaa")
}
}
VendasaPrazo.groovy
//Classe de Controle ClienteController.groovy
package sisgesso
class ClienteController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def index = {
redirect(action: "list", params: params)
}
def search = {
flash.message = "Resultados da procura por: ${params.q}"
def resultsMap = Cliente.search(params.q, params)
render(view:'list_resultsSearch',
model:[clienteInstanceList:resultsMap.results,
clienteInstanceTotal:Cliente.countHits(params.q)]
)
}
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[clienteInstanceList: Cliente.list(params), clienteInstanceTotal: Cliente.count()]
}
def create = {
def clienteInstance = new Cliente()
clienteInstance.properties = params
return [clienteInstance: clienteInstance]
}
def save = {
def clienteInstance = new Cliente(params)
if (clienteInstance.save(flush: true)) {
80
flash.message = "${message(code: 'default.created.message',
args: [message(code: 'cliente.label', default: 'Cliente'), clienteInstance.id])}"
redirect(action: "show", id: clienteInstance.id)
}
else {
render(view: "create", model: [clienteInstance: clienteInstance])
}
}
def show = {
def clienteInstance = Cliente.get(params.id)
if (!clienteInstance) {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'cliente.label', default: 'Cliente'), params.id])}"
redirect(action: "list")
}
else {
[clienteInstance: clienteInstance]
}
}
def edit = {
def clienteInstance = Cliente.get(params.id)
if (!clienteInstance) {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'cliente.label', default: 'Cliente'), params.id])}"
redirect(action: "list")
}
else {
return [clienteInstance: clienteInstance]
}
}
def update = {
def clienteInstance = Cliente.get(params.id)
if (clienteInstance) {
if (params.version) {
def version = params.version.toLong()
if (clienteInstance.version > version) {
clienteInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'cliente.label', default: 'Cliente')] as Object[],
"Another user has updated this Cliente while you were editing")
render(view: "edit", model: [clienteInstance: clienteInstance])
return
}
}
clienteInstance.properties = params
if (!clienteInstance.hasErrors() && clienteInstance.save(flush: true)) {
flash.message = "${message(code: 'default.updated.message',
args: [message(code: 'cliente.label', default: 'Cliente'), clienteInstance.id])}"
redirect(action: "show", id: clienteInstance.id)
}
else {
render(view: "edit", model: [clienteInstance: clienteInstance])
}
}
81
else {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'cliente.label', default: 'Cliente'), params.id])}"
redirect(action: "list")
}
}
def delete = {
def clienteInstance = Cliente.get(params.id)
if (clienteInstance) {
try {
clienteInstance.delete(flush: true)
flash.message = "${message(code: 'default.deleted.message',
args: [message(code: 'cliente.label', default: 'Cliente'), params.id])}"
redirect(action: "list")
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
flash.message = "${message(code: 'default.not.deleted.message',
args: [message(code: 'cliente.label', default: 'Cliente'), params.id])}"
redirect(action: "show", id: params.id)
}
}
else {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'cliente.label', default: 'Cliente'), params.id])}"
redirect(action: "list")
}
}
}
ClienteController.groovy
//Classe de Controle PessoaController.groovy
package sisgesso
class PessoaController {
def scaffold = true
}
PessoaController.groovy
//Classe de Controle UsuarioController.groovy
package sisgesso
class UsuarioController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def login = { }
def logout = {
flash.message = "Até breve ${session.usuario.login}"
session.usuario = null
redirect(action:"login")
}
def authenticate = {
82
def usuario = Usuario.findByLoginAndSenha(params.login, params.senha.encodeAsSHA())
if(usuario){
session.usuario = usuario
flash.message = "Olá ${usuario.login}!"
redirect(uri:"/")
}else{
flash.message = "Desculpe, ${params.login}. Por favor tente novamente."
redirect(action:"login")
}
}
def index = {
redirect(action: "list", params: params)
}
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[usuarioInstanceList: Usuario.list(params), usuarioInstanceTotal: Usuario.count()]
}
def create = {
def usuarioInstance = new Usuario()
usuarioInstance.properties = params
return [usuarioInstance: usuarioInstance]
}
def save = {
def usuarioInstance = new Usuario(params)
if (usuarioInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message',
args: [message(code: 'usuario.label', default: 'Usuario'), usuarioInstance.id])}"
redirect(action: "show", id: usuarioInstance.id)
}
else {
render(view: "create", model: [usuarioInstance: usuarioInstance])
}
}
def show = {
def usuarioInstance = Usuario.get(params.id)
if (!usuarioInstance) {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'usuario.label', default: 'Usuario'), params.id])}"
redirect(action: "list")
}
else {
[usuarioInstance: usuarioInstance]
}
}
def edit = {
def usuarioInstance = Usuario.get(params.id)
if (!usuarioInstance) {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'usuario.label', default: 'Usuario'), params.id])}"
redirect(action: "list")
}
else {
return [usuarioInstance: usuarioInstance]
83
}
}
def update = {
def usuarioInstance = Usuario.get(params.id)
if (usuarioInstance) {
if (params.version) {
def version = params.version.toLong()
if (usuarioInstance.version > version) {
usuarioInstance.errors.rejectValue("version",
"default.optimistic.locking.failure", [message(code: 'usuario.label',
default: 'Usuario')] as Object[],
"Another user has updated this Usuario while you were editing")
render(view: "edit", model: [usuarioInstance: usuarioInstance])
return
}
}
usuarioInstance.properties = params
if (!usuarioInstance.hasErrors() && usuarioInstance.save(flush: true)) {
flash.message = "${message(code: 'default.updated.message',
args: [message(code: 'usuario.label', default: 'Usuario'), usuarioInstance.id])}"
redirect(action: "show", id: usuarioInstance.id)
}
else {
render(view: "edit", model: [usuarioInstance: usuarioInstance])
}
}
else {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'usuario.label', default: 'Usuario'), params.id])}"
redirect(action: "list")
}
}
def delete = {
def usuarioInstance = Usuario.get(params.id)
if (usuarioInstance) {
try {
usuarioInstance.delete(flush: true)
flash.message = "${message(code: 'default.deleted.message',
args: [message(code: 'usuario.label', default: 'Usuario'), params.id])}"
redirect(action: "list")
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
flash.message = "${message(code: 'default.not.deleted.message',
args: [message(code: 'usuario.label', default: 'Usuario'), params.id])}"
redirect(action: "show", id: params.id)
}
}
else {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'usuario.label', default: 'Usuario'), params.id])}"
redirect(action: "list")
}
}
}
84
UsuarioController.groovy
//Classe de controle VendasaPrazoController.groovy
package sisgesso
class VendasaPrazoController {
static allowedMethods = [save: "POST", update: "POST", delete: "POST"]
def index = {
redirect(action: "list", params: params)
}
def search = {
render VendasaPrazo.search(params.q, params)
}
def list = {
params.max = Math.min(params.max ? params.int('max') : 10, 100)
[vendasaPrazoInstanceList: VendasaPrazo.list(params),
vendasaPrazoInstanceTotal: VendasaPrazo.count()]
}
def create = {
def vendasaPrazoInstance = new VendasaPrazo()
vendasaPrazoInstance.properties = params
return [vendasaPrazoInstance: vendasaPrazoInstance]
}
def save = {
def vendasaPrazoInstance = new VendasaPrazo(params)
if (vendasaPrazoInstance.save(flush: true)) {
flash.message = "${message(code: 'default.created.message',
args: [message(code: 'vendasaPrazo.label',
default: 'VendasaPrazo'), vendasaPrazoInstance.id])}"
redirect(action: "show", id: vendasaPrazoInstance.id)
}
else {
render(view: "create", model: [vendasaPrazoInstance: vendasaPrazoInstance])
}
}
def show = {
def vendasaPrazoInstance = VendasaPrazo.get(params.id)
if (!vendasaPrazoInstance) {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'vendasaPrazo.label', default: 'VendasaPrazo'), params.id])}"
redirect(action: "list")
}
else {
[vendasaPrazoInstance: vendasaPrazoInstance]
}
}
def edit = {
def vendasaPrazoInstance = VendasaPrazo.get(params.id)
if (!vendasaPrazoInstance) {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'vendasaPrazo.label', default: 'VendasaPrazo'), params.id])}"
redirect(action: "list")
}
85
else {
return [vendasaPrazoInstance: vendasaPrazoInstance]
}
}
def update = {
def vendasaPrazoInstance = VendasaPrazo.get(params.id)
if (vendasaPrazoInstance) {
if (params.version) {
def version = params.version.toLong()
if (vendasaPrazoInstance.version > version) {
vendasaPrazoInstance.errors.rejectValue("version", "default.optimistic.locking.failure",
[message(code: 'vendasaPrazo.label', default: 'VendasaPrazo')] as Object[],
"Another user has updated this VendasaPrazo while you were editing")
render(view: "edit", model: [vendasaPrazoInstance: vendasaPrazoInstance])
return
}
}
vendasaPrazoInstance.properties = params
if (!vendasaPrazoInstance.hasErrors() && vendasaPrazoInstance.save(flush: true)) {
flash.message = "${message(code: 'default.updated.message',
args: [message(code: 'vendasaPrazo.label', default: 'VendasaPrazo'),
vendasaPrazoInstance.id])}"
redirect(action: "show", id: vendasaPrazoInstance.id)
}
else {
render(view: "edit", model: [vendasaPrazoInstance: vendasaPrazoInstance])
}
}
else {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'vendasaPrazo.label', default: 'VendasaPrazo'), params.id])}"
redirect(action: "list")
}
}
def delete = {
def vendasaPrazoInstance = VendasaPrazo.get(params.id)
if (vendasaPrazoInstance) {
try {
vendasaPrazoInstance.delete(flush: true)
flash.message = "${message(code: 'default.deleted.message',
args: [message(code: 'vendasaPrazo.label', default: 'VendasaPrazo'), params.id])}"
redirect(action: "list")
}
catch (org.springframework.dao.DataIntegrityViolationException e) {
flash.message = "${message(code: 'default.not.deleted.message',
args: [message(code: 'vendasaPrazo.label', default: 'VendasaPrazo'), params.id])}"
redirect(action: "show", id: params.id)
}
}
else {
flash.message = "${message(code: 'default.not.found.message',
args: [message(code: 'vendasaPrazo.label', default: 'VendasaPrazo'), params.id])}"
redirect(action: "list")
}
}
}
86
VendasaPrazoController.groovy
//Classe para codificação de Senhas SHACodec.groovy
import java.security.MessageDigest
class SHACodec{
static encode = {target->
MessageDigest md = MessageDigest.getInstance('SHA')
md.update(target.getBytes('UTF-8'))
return new String(md.digest()).encodeAsBase64()
}
}
SHACodec.groovy
//Classe Filtro para administração de acesso de usuários AdminFilters.groovy
package sisgesso
class AdminFilters {
def filters = {
usuariosLogados(controller:"*", action:"(list|show|create|edit|update|delete|save)")
{
before = {if(!session?.usuario) {
redirect(controller:"usuario", action:"login")
return false
}
}
}
adminOnly(controller:'usuario',
action:"(create|edit|update|delete|save)") {
before = {
if(!session.usuario.admin){
flash.message = "Somentes Usuários Administradores"
redirect(uri:"/")
return false
}
}
}
}
}
AdminFilters.groovy
//Biblioteca de Marcas para template parcial LogarTagLib.groovy
package sisgesso
class LogarTagLib {
def loginControl = {
if(request.getSession(false) && session.usuario){
out << "Olá ${session.usuario.login} "
87
out << """[${link(action:"logout",
controller:"usuario"){"Sair"}}]"""
} else {
out << """[${link(action:"login",
controller:"usuario"){"Entrar"}}]"""
}
}
def isAdmin = {
}
def loggedIn = {
}
}
LogarTagLib.groovy
//Biblioteca de Marcas para template parcial RodapeTagLib.groovy
package sisgesso
class RodapeTagLib {
def thisYear = {
out << new Date().format("yyyy")
}
def copyright = {attrs, body->
out << "© " + attrs.startYear + " - "
out << thisYear() + " " + body()
}
}
RodapeTagLib.groovy
//Classe de Testes ClienteControllerTests.groovy
package sisgesso
import grails.test.*
class ClienteControllerTests extends ControllerUnitTestCase {
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testIndex() {
controller.index()
assertEquals "list",
controller.redirectArgs["action"]
}
void testShow(){
def Cli = new Cliente(nome:"Cliente")
def Cli2 = new Cliente(nome:"Cliente2")
88
mockDomain(Cliente, [Cli, Cli2])
controller.params.id = 2
def map = controller.show()
assertEquals "Cliente2", map.clienteInstance.nome
}
}
ClienteControllerTests.groovy
//Classe de Testes ClienteTests.groovy
package sisgesso
import grails.test.*
class ClienteTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testConstraints() {
mockDomain Cliente
//testes para validar atributos
def cliente = new Cliente()
assertFalse cliente.validate()
def cliente_ok = new Cliente(nome:"Fulano de tal", cpf:"123.456.700-89",
rua:"A", numero:"12", bairro:"Qualquer",
complemento:"casa", cep:"28970-000",
cidade:"Rio de Janeiro", uf:"RJ", tel:"0000000",
email:"[email protected]", telCel:"9999-9999",
outrasInf:"isso é um teste")
assertTrue cliente_ok.validate()
}
void testValidar_nome() {
mockDomain Cliente
def cliente_Nome = new Cliente(cpf:"123.456.700-89",
rua:"A", numero:"12", bairro:"Qualquer",
complemento:"casa", cep:"28970-000", cidade:"Rio de Janeiro",
uf:"RJ", tel:"0000000", email:"[email protected]",
telCel:"9999-9999", outrasInf:"isso é um teste")
assertFalse cliente_Nome.validate() //Campo nome é obrigatório
}
void testValidar_cpf() {
mockDomain Cliente
def cliente_validarCpf = new Cliente(nome:"Fulano de tal",
cpf:"123.456.700-8900000000000000000",
rua:"A", numero:"12", bairro:"Qualquer",
complemento:"casa", cep:"28970-000", cidade:"Rio de Janeiro",
uf:"RJ", tel:"0000000",
89
email:"[email protected]", telCel:"9999-9999",
outrasInf:"isso é um teste")
assertFalse cliente_validarCpf.validate() //Cpf não deve ser > 18
}
void testValidar_email(){
mockDomain Cliente
def cliente_validarEmail = new Cliente(nome:"Fulano de tal",cpf:"123.456.700-89",
rua:"A", numero:"12", bairro:"Qualquer",
complemento:"casa", cep:"28970-000", cidade:"Rio de Janeiro",
uf:"RJ", tel:"0000000",
email:"fulano", telCel:"9999-9999", outrasInf:"isso é um teste")
assertFalse cliente_validarEmail.validate() //formato de email inválido o correto é
}
void testValidar_uf(){
mockDomain Cliente
def cliente_validarUf = new Cliente(nome:"Fulano de tal",cpf:"123.456.700-89",
rua:"A", numero:"12", bairro:"Qualquer",
complemento:"casa", cep:"28970-000", cidade:"Rio de Janeiro",
uf:"KK", tel:"0000000",
email:"fulano", telCel:"9999-9999", outrasInf:"isso é um teste")
assertFalse cliente_validarUf.validate()
assertEquals "inList", cliente_validarUf.errors["uf"]// O valor de uf não consta na lista
}
}
ClienteTests.groovy
//Classe de Testes UsuarioTests.groovy
package sisgesso
import grails.test.*
class UsuarioTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testContraints() {
mockDomain Usuario
def usuario = new Usuario()
assertFalse usuario.validate()
def usuario_ok = new Usuario(nome:"Adriano", login:"aba",
senha:"123456", papel:"Superadmin")
assertFalse usuario_ok.validate()
assertEquals "inList", usuario_ok.errors["papel"]
}
void testLoginUnico(){
90
def usuario = new Usuario(login:"aba")
def admin = new Usuario(login:"admin")
mockDomain (Usuario, [usuario,admin])
def usuario_repetido = new Usuario(login:"aba")
usuario_repetido.save()
assertEquals 2, Usuario.count()
assertEquals "unique", usuario_repetido.errors["login"] // O login 'aba' já está sendo usado
def usuario_unique = new Usuario(nome:"Adriano", login:"abaok",
senha:"123456", papel:"admin")
usuario_unique.save()
assertEquals 3, usuario_unique.count()
}
}
UsuarioTests.groovy
//Classe de Testes UsuarioControllerTests.groovy
package sisgesso
import grails.test.*
import org.codehaus.groovy.grails.plugins.codecs.*
class UsuarioControllerTests extends ControllerUnitTestCase {
protected void setUp() {
super.setUp()
String.metaClass.encodeAsBase64 = {->
Base64Codec.encode(delegate)
}
String.metaClass.encodeAsSHA = {->
SHACodec.encode(delegate)
}
}
protected void tearDown() {
super.tearDown()
}
void testIndex() {
controller.index()
assertEquals "list",
controller.redirectArgs["action"]
}
void testShow(){
def aba = new Usuario(login:"aba")
def usuario = new Usuario(login:"usuario")
mockDomain(Usuario, [aba, usuario])
controller.params.id = 2
def map = controller.show()
assertEquals "usuario", map.usuarioInstance.login
91
}
void testAutenticacao() {
def aba = new Usuario(login:"aba", senha:"123456".encodeAsSHA())
mockDomain(Usuario, [aba])
controller.params.login = "aba"
controller.params.senha = "123456"
controller.authenticate()
assertNotNull controller.session.usuario
assertEquals "aba", controller.session.usuario.login
controller.params.senha = "123"
controller.authenticate()
assertTrue controller.flash.message.startsWith("Desculpe, aba")
}
}
UsuarioControllerTests.groovy
//Classe de Teste VendasaPrazoTests.groovy
package sisgesso
import grails.test.*
class VendasaPrazoTests extends GrailsUnitTestCase {
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testContraints() {
mockDomain VendasaPrazo
def venda = new VendasaPrazo()
assertFalse venda.validate()
def venda_ok = new VendasaPrazo(cliente:"Adriano", numVenda: 3,
dataVenda:new Date(), valorTotal:-5, valorPago:45,
dataPagamento:new Date())
assertFalse venda_ok.validate()
assertEquals "min", venda_ok.errors["valorTotal"]
}
void testNumerovendaUnico(){
def venda = new VendasaPrazo(numVenda: 5)
def venda2 = new VendasaPrazo(numVenda: 4)
mockDomain (VendasaPrazo, [venda,venda2])
def venda_repetida = new VendasaPrazo(numVenda: 5)
venda_repetida.save()
assertEquals 2, VendasaPrazo.count()
assertEquals "unique", venda_repetida.errors["numVenda"] // O numero de venda '5' já existe
}
}
92
VendasaPrazoTests.groovy
//Classe de Teste VendasaPrazoControllerTests.groovy
package sisgesso
import grails.test.*
class VendasaPrazoControllerTests extends ControllerUnitTestCase {
protected void setUp() {
super.setUp()
}
protected void tearDown() {
super.tearDown()
}
void testIndex() {
controller.index()
assertEquals "list",
controller.redirectArgs["action"]
}
}
VendasaPrazoControllerTests.groovy
//Arquivo de configuração DATASOURCE.groovy
dataSource {
pooled = true
driverClassName = "com.mysql.jdbc.Driver"
username = "aba"
password = "desenv"
}
hibernate {
cache.use_second_level_cache=true
cache.use_query_cache=true
cache.provider_class='net.sf.ehcache.hibernate.EhCacheProvider'
}
// environment specific settings
environments {
development {
dataSource {
dbCreate = "update" // one of 'create', 'create-drop','update'
url = "jdbc:mysql://localhost:3306/sisgessodb_dev?"
}
}
test {
dataSource {
dbCreate = "update"
url = "jdbc:mysql://localhost:3306/sisgessodb_test?"
}
}
production {
dataSource {
93
dbCreate = "update"
url = "jdbc:mysql://localhost:3306/sisgessodb_prod?"
}
}
}
DataSource.groovy
//Arquivo de Configuração UrlMappings.groovy
class UrlMappings {
static mappings = {
"/$controller/$action?/$id?"{
constraints {
// apply constraints here
}
}
"/"(view:"/index")
"500"(view:'/error')
}
}
UrlMappings.groovy
<html>
<head>
<title>Sis. Gesso - Gesso Arte de Araruama</title>
<meta name="layout" content="main" />
<style type="text/css" media="screen">
#nav {
margin-top:20px;
margin-left:30px;
width:228px;
float:left;
}
.homePagePanel * {
margin:0px;
}
.homePagePanel .panelBody ul {
list-style-type:none;
margin-bottom:10px;
}
.homePagePanel .panelBody h1 {
text-transform:uppercase;
font-size:1.1em;
margin-bottom:10px;
}
.homePagePanel .panelBody {
background: url(images/leftnav_midstretch.png) repeat-y top;
margin:0px;
padding:15px;
}
.homePagePanel .panelBtm {
background: url(images/leftnav_btm.png) no-repeat top;
height:20px;
94
margin:0px;
}
.homePagePanel .panelTop {
background: url(images/leftnav_top.png) no-repeat top;
height:11px;
margin:0px;
}
h2 {
margin-top:15px;
margin-bottom:15px;
font-size:20px;
}
#pageBody {
margin-left:280px;
margin-right:20px;
}
</style>
</head>
<body>
<table>
<td>
<div id="nav">
<div class="homePagePanel">
<div class="panelTop">
</div>
<div class="panelBody">
<h2 align="center"><a class="cliente" href="${createLink(uri: '/cliente')}">Cliente</a></h2>
</div>
<div class="panelBtm">
</div>
</div>
</div>
<div id="nav">
<div class="homePagePanel">
<div class="panelTop">
</div>
<div class="panelBody">
<h2 align="center"><a class="promissoria" href="${createLink(uri: '/vendasaPrazo')}">
Venda a Prazo</a>
</h2>
</div>
<div class="panelBtm">
</div>
</div>
</div>
<div id="nav">
95
<div class="homePagePanel">
<div class="panelTop">
</div>
<div class="panelBody">
<h2 align="center"><a class="usuario" href="${createLink(uri: '/usuario')}">Usuário</a></h2>
</div>
<div class="panelBtm">
</div>
</div>
</div>
</td>
</table>
</body>
</html>
Índex.gsp (Página Principal)
<%@ page import="sisgesso.Cliente" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'cliente.label', default: 'Cliente')}" />
<title><g:message code="default.create.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Principal</a></span>
<span class="menuButton"><g:link class="list" action="list"><g:message code="default.list.label"
args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.create.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:hasErrors bean="${clienteInstance}">
<div class="errors">
<g:renderErrors bean="${clienteInstance}" as="list" />
</div>
</g:hasErrors>
<g:form action="save" method="post" >
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="nome"><g:message code="cliente.nome.label" default="Nome" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'nome', 'errors')}">
<g:textField name="nome" maxlength="130" value="${clienteInstance?.nome}" />
</td>
96
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="cpf"><g:message code="cliente.cpf.label" default="CPF" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'cpf', 'errors')}">
<g:textField name="cpf" maxlength="18" value="${clienteInstance?.cpf}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="rua"><g:message code="cliente.rua.label" default="Rua" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'rua', 'errors')}">
<g:textField name="rua" value="${clienteInstance?.rua}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="numero"><g:message code="cliente.numero.label" default="Número"/>
</label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'numero',
'errors')}">
<g:textField name="numero" value="${fieldValue(bean: clienteInstance, field:
'numero')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="bairro"><g:message code="cliente.bairro.label" default="Bairro" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'bairro', 'errors')}">
<g:textField name="bairro" value="${clienteInstance?.bairro}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="complemento"><g:message code="cliente.complemento.label"
default="Complemento" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'complemento',
'errors')}">
<g:textField name="complemento" value="${clienteInstance?.complemento}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="cidade"><g:message code="cliente.cidade.label" default="Cidade" /></label>
</td>
97
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'cidade', 'errors')}">
<g:textField name="cidade" value="${clienteInstance?.cidade}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="uf"><g:message code="cliente.uf.label" default="UF" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'uf', 'errors')}">
<g:select name="uf" from="${clienteInstance.constraints.uf.inList}"
value="${clienteInstance?.uf}" valueMessagePrefix="cliente.uf" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="cep"><g:message code="cliente.cep.label" default="CEP" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'cep', 'errors')}">
<g:textField name="cep" value="${clienteInstance?.cep}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="tel"><g:message code="cliente.tel.label" default="Telefone" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'tel', 'errors')}">
<g:textField name="tel" value="${clienteInstance?.tel}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="email"><g:message code="cliente.email.label" default="E-mail" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'email', 'errors')}">
<g:textField name="email" value="${clienteInstance?.email}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="telCel"><g:message code="cliente.telCel.label" default="Tel. Celeluar"
/></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'telCel', 'errors')}">
<g:textField name="telCel" value="${clienteInstance?.telCel}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="outrasInf"><g:message code="cliente.outrasInf.label" default="Outras
Informações" /></label>
98
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'outrasInf',
'errors')}">
<g:textField name="outrasInf" value="${clienteInstance?.outrasInf}" />
</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:submitButton name="create" class="save" value="${message(code:
'default.button.create.label', default: 'Create')}" /></span>
</div>
</g:form>
</div>
</body>
</html>
Cliente/create.gsp
<%@ page import="sisgesso.Cliente" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'cliente.label', default: 'Cliente')}" />
<title><g:message code="default.edit.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton">
<a class="home" href="${createLink(uri: '/')}">Principal</a>
</span>
<span class="menuButton">
<g:link class="list" action="list">
<g:message code="default.list.label" args="[entityName]" />
</g:link>
</span>
<span class="menuButton">
<g:link class="create" action="create">
<g:message code="default.new.label" args="[entityName]" />
</g:link>
</span>
</div>
<div class="body">
<h1><g:message code="default.edit.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:hasErrors bean="${clienteInstance}">
<div class="errors">
<g:renderErrors bean="${clienteInstance}" as="list" />
</div>
99
</g:hasErrors>
<g:form method="post" >
<g:hiddenField name="id" value="${clienteInstance?.id}" />
<g:hiddenField name="version" value="${clienteInstance?.version}" />
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="nome">
<g:message code="cliente.nome.label" default="Nome" />
</label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'nome', 'errors')}">
<g:textField name="nome" maxlength="130" value="${clienteInstance?.nome}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="cpf">
<g:message code="cliente.cpf.label" default="CPF" />
</label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'cpf', 'errors')}">
<g:textField name="cpf" maxlength="18" value="${clienteInstance?.cpf}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="rua">
<g:message code="cliente.rua.label" default="Rua" />
</label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'rua', 'errors')}">
<g:textField name="rua" value="${clienteInstance?.rua}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="numero">
<g:message code="cliente.numero.label" default="Numero"/>
</label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'numero', 'errors')}">
<g:textField name="numero" value="${fieldValue(bean: clienteInstance,
field: 'numero')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="bairro">
100
<g:message code="cliente.bairro.label" default="Bairro" />
</label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'bairro', 'errors')}">
<g:textField name="bairro" value="${clienteInstance?.bairro}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="complemento">
<g:message code="cliente.complemento.label" default="Complemento" />
</label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance,
field: 'complemento', 'errors')}">
<g:textField name="complemento" value="${clienteInstance?.complemento}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="cidade">
<g:message code="cliente.cidade.label" default="Cidade" />
</label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'cidade', 'errors')}">
<g:textField name="cidade" value="${clienteInstance?.cidade}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="uf"><g:message code="cliente.uf.label" default="UF" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'uf', 'errors')}">
<g:select name="uf" from="${clienteInstance.constraints.uf.inList}"
value="${clienteInstance?.uf}" valueMessagePrefix="cliente.uf" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="cep"><g:message code="cliente.cep.label" default="CEP" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'cep', 'errors')}">
<g:textField name="cep" value="${clienteInstance?.cep}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="tel"><g:message code="cliente.tel.label" default="Telefone" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'tel', 'errors')}">
<g:textField name="tel" value="${clienteInstance?.tel}" />
101
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="email"><g:message code="cliente.email.label" default="E-mail" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'email', 'errors')}">
<g:textField name="email" value="${clienteInstance?.email}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="telCel">
<g:message code="cliente.telCel.label" default="Tel. Celular"/></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance, field: 'telCel', 'errors')}">
<g:textField name="telCel" value="${clienteInstance?.telCel}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="outrasInf">
<g:message code="cliente.outrasInf.label" default="OutrasInformações" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance,
field: 'outrasInf', 'errors')}">
<g:textField name="outrasInf" value="${clienteInstance?.outrasInf}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="vendasaprazos">
<g:message code="cliente.vendasaprazos.label" default="Vendasaprazos" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: clienteInstance,
field: 'vendasaprazos', 'errors')}">
<ul>
<g:each in="${clienteInstance?.vendasaprazos?}" var="v">
<li>
<g:link controller="vendasaPrazo" action="show"
id="${v.id}">${v?.encodeAsHTML()}</g:link>
</li>
</g:each>
</ul>
<g:link controller="vendasaPrazo" action="create"
params="['cliente.id': clienteInstance?.id]">
${message(code: 'default.add.label', args: [message(code:
'vendasaPrazo.label', default: 'VendasaPrazo')])}</g:link>
</td>
</tr>
102
</tbody>
</table>
</div>
<div class="buttons">
<span class="button">
<g:actionSubmit class="save" action="update"
value="${message(code: 'default.button.update.label', default: 'Update')}" />
</span>
<span class="button"><g:actionSubmit class="delete" action="delete"
value="${message(code: 'default.button.delete.label', default: 'Delete')}"
onclick="return confirm('${message(code: 'default.button.delete.confirm.message',
default: 'Are you sure?')}');" />
</span>
</div>
</g:form>
</div>
</body>
</html>
Cliente/edit.gsp
<%@ page import="sisgesso.Cliente" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'cliente.label', default: 'Cliente')}" />
<title><g:message code="default.list.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton">
<a class="home" href="${createLink(uri: '/')}">Principal</a>
</span>
<span class="menuButton">
<g:link class="create" action="create">
<g:message code="default.new.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.list.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<div class="list">
<table>
<thead>
<tr>
<g:sortableColumn property="id" title="${message(code: 'cliente.id.label',
default: 'Id')}" />
<g:sortableColumn property="nome" title="${message(code: 'cliente.nome.label',
default: 'Nome')}" />
<g:sortableColumn property="cpf" title="${message(code: 'cliente.cpf.label',
default: 'CPF')}" />
<g:sortableColumn property="rua" title="${message(code: 'cliente.rua.label',
103
default: 'Rua')}" />
<g:sortableColumn property="numero" title="${message(code: 'cliente.numero.label',
default: 'Numero')}" />
<g:sortableColumn property="bairro" title="${message(code: 'cliente.bairro.label',
default: 'Bairro')}" />
<g:sortableColumn property="complemento"
title="${message(code: 'cliente.complemento.label', default: 'Complemento')}"/>
<g:sortableColumn property="cep" title="${message(code: 'cliente.cpf.label',
default: 'CEP')}"/>
<g:sortableColumn property="cidade" title="${messge(code: 'cliente.cidade.label',
default: 'Cidade')}"/>
<g:sortableColumn property="uf" title="${message(code: 'cliente.uf.label',
default: 'UF')}"/>
<g:sortableColumn property="tel" title="${message(code: 'cliente.tel.label',
default: 'Telefone')}"/>
<g:sortableColumn property="telCel" title="${message(code: 'cliente.telCel.label',
default: 'Celular')}"/>
<g:sortableColumn property="email" title="${message(code: 'cliente.email.label',
default: 'E-mail')}"/>
</tr>
</thead>
<tbody>
<g:each in="${clienteInstanceList}" status="i" var="clienteInstance">
<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
<td><g:link action="show" id="${clienteInstance.id}">
${fieldValue(bean: clienteInstance, field: "id")}</g:link></td>
<td>${fieldValue(bean: clienteInstance, field: "nome")}</td>
<td>${fieldValue(bean: clienteInstance, field: "cpf")}</td>
<td>${fieldValue(bean: clienteInstance, field: "rua")}</td>
<td>${fieldValue(bean: clienteInstance, field: "numero")}</td>
<td>${fieldValue(bean: clienteInstance, field: "bairro")}</td>
<td>${fieldValue(bean: clienteInstance, field: "complemento")}</td>
<td>${fieldValue(bean: clienteInstance, field: "cep")}</td>
<td>${fieldValue(bean: clienteInstance, field: "cidade")}</td>
<td>${fieldValue(bean: clienteInstance, field: "uf")}</td>
<td>${fieldValue(bean: clienteInstance, field: "tel")}</td>
<td>${fieldValue(bean: clienteInstance, field: "telCel")}</td>
<td>${fieldValue(bean: clienteInstance, field: "email")}</td>
</tr>
</g:each>
104
</tbody>
</table>
</div>
<div class="paginateButtons">
<g:paginate total="${clienteInstanceTotal}" />
</div>
</div>
</body>
</html>
Cliente/list.gsp
<%@ page import="sisgesso.Cliente" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'cliente.label', default: 'Cliente')}" />
<title><g:message code="default.show.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton">
<a class="home" href="${createLink(uri: '/')}">Principal</a></span>
<span class="menuButton"><g:link class="list" action="list">
<g:message code="default.list.label" args="[entityName]" /></g:link></span>
<span class="menuButton"><g:link class="create" action="create">
<g:message code="default.new.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.show.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.id.label" default="Id" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "id")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.nome.label" default="Nome" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "nome")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.cpf.label" default="Cpf" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "cpf")}</td>
</tr>
105
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.rua.label" default="Rua" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "rua")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.numero.label" default="Numero" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "numero")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.bairro.label" default="Bairro" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "bairro")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.complemento.label" default="Complemento" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "complemento")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.cidade.label" default="Cidade" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "cidade")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.uf.label" default="Uf" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "uf")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.cep.label" default="Cep" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "cep")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.tel.label" default="Tel" /></td>
<td valign="top" class="value">${fieldValue(bean: clienteInstance, field: "tel")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.email.label" default="Email" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "email")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.telCel.label" default="Tel Cel" /></td>
106
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "telCel")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.outrasInf.label" default="Outras Inf" /></td>
<td valign="top" class="value">
${fieldValue(bean: clienteInstance, field: "outrasInf")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="cliente.vendasaprazos.label" default="Vendasaprazos" /></td>
<td valign="top" style="text-align: left;" class="value">
<ul>
<g:each in="${clienteInstance.vendasaprazos}" var="v">
<li>
<g:link controller="vendasaPrazo" action="show" d="${v.id}">${v?.encodeAsHTML()}</g:link>
</li>
</g:each>
</ul>
</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<g:form>
<g:hiddenField name="id" value="${clienteInstance?.id}" />
<span class="button">
<g:actionSubmit class="edit" action="edit" value="${message(code: 'default.button.edit.label',
default: 'Edit')}" /></span>
<span class="button">
<g:actionSubmit class="delete" action="delete" value="${message(code:
'default.button.delete.label', default: 'Delete')}" onclick="return
confirm('${message(code: 'default.button.delete.confirm.message',
default: 'Are you sure?')}');" /></span>
</g:form>
</div>
</div>
</body>
</html>
Cliente/show.gsp
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"/>
<meta name="layout" content="main" />
<title>SisGesso</title>
</head>
<body>
<div class="body">
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
107
<g:each in="${clienteInstanceList}"
status="i" var="clienteInstance">
<div class="cliente">
<h2>${clienteInstance.nome}</h2>
<p class="cliente-details">
<span class="question">Endereço?</span>
<span class="answer">
${clienteInstance.rua}, ${clienteInstance.numero}, ${clienteInstance.bairro},
${clienteInstance.cidade}, ${clienteInstance.uf}, ${clienteInstance.cep}</span>
</p>
<p class="cliente-details">
<span class="question">Documento Indentificador?</span>
<span class="answer">
${clienteInstance.cpf}
</span>
</p>
<p class="cliente-details">
<span class="question">Contatos?</span>
<span class="answer">
${clienteInstance.tel}, ${clienteInstance.telCel}, ${clienteInstance.email}
</span>
</p>
</div>
</g:each>
<div class="paginateButtons">
<g:paginate total="${clienteInstanceTotal}" />
</div>
</div>
</body>
</html>
Cliente/list_resultSearch.gsp
<%@ page import="sisgesso.Usuario" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'usuario.label', default: 'Usuario')}" />
<title><g:message code="default.create.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Principal</a></span>
<span class="menuButton"><g:link class="list" action="list">
<g:message code="default.list.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.create.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:hasErrors bean="${usuarioInstance}">
<div class="errors">
<g:renderErrors bean="${usuarioInstance}" as="list" />
108
</div>
</g:hasErrors>
<g:form action="save" method="post" >
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="nome"><g:message code="usuario.nome.label" default="Nome" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'nome', 'errors')}">
<g:textField name="nome" value="${usuarioInstance?.nome}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="login"><g:message code="usuario.login.label" default="Login" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'login', 'errors')}">
<g:textField name="login" value="${usuarioInstance?.login}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="senha"><g:message code="usuario.senha.label" default="Senha" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'senha', 'errors')}">
<g:passwordField name="senha" value="${usuarioInstance?.senha}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="papel"><g:message code="usuario.papel.label" default="Papel" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'papel', 'errors')}">
<g:select name="papel" from="${usuarioInstance.constraints.papel.inList}"
value="${usuarioInstance?.papel}" valueMessagePrefix="usuario.papel" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="admin"><g:message code="usuario.admin.label" default="Admin" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'admin', 'errors')}">
<g:checkBox name="admin" value="${usuarioInstance?.admin}" />
</td>
</tr>
</tbody>
</table>
</div>
109
<div class="buttons">
<span class="button"><g:submitButton name="create" class="save"
value="${message(code: 'default.button.create.label', default: 'Create')}" /></span>
</div>
</g:form>
</div>
</body>
</html>
Usuario/create.gsp
<%@ page import="sisgesso.Usuario" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'usuario.label', default: 'Usuario')}" />
<title><g:message code="default.edit.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Principal</a></span>
<span class="menuButton"><g:link class="list" action="list"><g:message code="default.list.label"
args="[entityName]" /></g:link></span>
<span class="menuButton"><g:link class="create" action="create">
<g:message code="default.new.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.edit.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:hasErrors bean="${usuarioInstance}">
<div class="errors">
<g:renderErrors bean="${usuarioInstance}" as="list" />
</div>
</g:hasErrors>
<g:form method="post" >
<g:hiddenField name="id" value="${usuarioInstance?.id}" />
<g:hiddenField name="version" value="${usuarioInstance?.version}" />
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="nome"><g:message code="usuario.nome.label" default="Nome" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'nome', 'errors')}">
<g:textField name="nome" value="${usuarioInstance?.nome}" />
</td>
</tr>
<tr class="prop">
110
<td valign="top" class="name">
<label for="login"><g:message code="usuario.login.label" default="Login" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'login', 'errors')}">
<g:textField name="login" value="${usuarioInstance?.login}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="senha"><g:message code="usuario.senha.label" default="Senha" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'senha', 'errors')}">
<g:passwordField name="senha" value="${usuarioInstance?.senha}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="papel"><g:message code="usuario.papel.label" default="Papel" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'papel', 'errors')}">
<g:select name="papel" from="${usuarioInstance.constraints.papel.inList}"
value="${usuarioInstance?.papel}" valueMessagePrefix="usuario.papel" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="admin"><g:message code="usuario.admin.label" default="Admin" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: usuarioInstance, field: 'admin', 'errors')}">
<g:checkBox name="admin" value="${usuarioInstance?.admin}" />
</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:actionSubmit class="save" action="update"
value="${message(code: 'default.button.update.label', default: 'Update')}" /></span>
<span class="button"><g:actionSubmit class="delete" action="delete"
value="${message(code: 'default.button.delete.label', default: 'Delete')}"
onclick="return confirm('${message(code: 'default.button.delete.confirm.message',
default: 'Are you sure?')}');" /></span>
</div>
</g:form>
</div>
</body>
</html>
Usuario/edit.gsp
<%@ page import="sisgesso.Usuario" %>
<html>
111
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'usuario.label', default: 'Usuario')}" />
<title><g:message code="default.list.label" args="[entityName]" /></title>
</head>
<body>
<g:if test="${session?.usuario?.admin}">
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Principal</a></span>
<span class="menuButton"><g:link class="create" action="create">
<g:message code="default.new.label" args="[entityName]" /></g:link></span>
</div>
</g:if>
<div class="body">
<h1><g:message code="default.list.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<div class="list">
<table>
<thead>
<tr>
<g:sortableColumn property="id" title="${message(code: 'usuario.id.label', default: 'Id')}" />
<g:sortableColumn property="nome" title="${message(code: 'usuario.nome.label',
default: 'Nome')}" />
<g:sortableColumn property="login" title="${message(code: 'usuario.login.label',
default: 'Login')}" />
<g:sortableColumn property="senha" title="${message(code: 'usuario.senha.label',
default: 'Senha')}" />
<g:sortableColumn property="papel" title="${message(code: 'usuario.papel.label',
default: 'Papel')}" />
<g:sortableColumn property="admin" title="${message(code: 'usuario.admin.label',
default: 'Admin')}" />
</tr>
</thead>
<tbody>
<g:each in="${usuarioInstanceList}" status="i" var="usuarioInstance">
<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
<td><g:link action="show" id="${usuarioInstance.id}">
${fieldValue(bean: usuarioInstance, field: "id")}</g:link></td>
<td>${fieldValue(bean: usuarioInstance, field: "nome")}</td>
<td>${fieldValue(bean: usuarioInstance, field: "login")}</td>
<td>${fieldValue(bean: usuarioInstance, field: "senha")}</td>
112
<td>${fieldValue(bean: usuarioInstance, field: "papel")}</td>
<td><g:formatBoolean boolean="${usuarioInstance.admin}" /></td>
</tr>
</g:each>
</tbody>
</table>
</div>
<div class="paginateButtons">
<g:paginate total="${usuarioInstanceTotal}" />
</div>
</div>
</body>
</html>
Usuario/list.gsp
<%@ page import="sisgesso.Usuario" %>
<html>
<head>
<meta http-equiv="Content-Type"
content="text/html; charset=UTF-8"/>
<meta name="layout" content="main" />
<title>Login</title>
</head>
<body>
<div class="body">
<h1>Login</h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:form action="authenticate" method="post" >
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="login">Login:</label>
</td>
<td valign="top">
<input type="text" id="login" name="login"/>
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="senha">Senha:</label>
</td>
<td valign="top">
<input type="password" id="senha" name="senha"/>
</td>
</tr>
</tbody>
</table>
113
</div>
<div class="buttons">
<span class="button">
<input type="submit" value="Acessar" />
</span>
</div>
</g:form>
</div>
</body>
</html>
Usuário/login.gsp
<%@ page import="sisgesso.Usuario" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'usuario.label', default: 'Usuario')}" />
<title><g:message code="default.show.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Principal</a></span>
<span class="menuButton"><g:link class="list" action="list">
<g:message code="default.list.label" args="[entityName]" /></g:link></span>
<span class="menuButton"><g:link class="create" action="create">
<g:message code="default.new.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.show.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name"><g:message code="usuario.id.label" default="Id" /></td>
<td valign="top" class="value">${fieldValue(bean: usuarioInstance, field: "id")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="usuario.nome.label" default="Nome"/></td>
<td valign="top" class="value">
${fieldValue(bean: usuarioInstance, field: "nome")}</td>
</tr>
<tr class="prop">
114
<td valign="top" class="name">
<g:message code="usuario.login.label" default="Login" /></td>
<td valign="top" class="value">${fieldValue(bean: usuarioInstance, field: "login")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="usuario.senha.label" default="Senha" /></td>
<td valign="top" class="value">${fieldValue(bean: usuarioInstance, field: "senha")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="usuario.papel.label" default="Papel"/></td>
<td valign="top" class="value">${fieldValue(bean: usuarioInstance, field: "papel")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="usuario.admin.label" default="Admin" /></td>
<td valign="top" class="value">
<g:formatBoolean boolean="${usuarioInstance?.admin}" /></td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<g:form>
<g:hiddenField name="id" value="${usuarioInstance?.id}" />
<span class="button"><g:actionSubmit class="edit" action="edit"
value="${message(code: 'default.button.edit.label', default: 'Edit')}" />
</span>
<span class="button"><g:actionSubmit class="delete" action="delete"
value="${message(code: 'default.button.delete.label',
default: 'Delete')}" onclick="return
confirm('${message(code: 'default.button.delete.confirm.message',
default: 'Are you sure?')}');" /></span>
</g:form>
</div>
</div>
</body>
</html>
Usuário/show.gsp
<%@ page import="sisgesso.VendasaPrazo" %>
<html>
115
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'vendasaPrazo.label', default: 'VendasaPrazo')}" />
<title><g:message code="default.create.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Principal</a></span>
<span class="menuButton"><g:link class="list" action="list">
<g:message code="default.list.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.create.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:hasErrors bean="${vendasaPrazoInstance}">
<div class="errors">
<g:renderErrors bean="${vendasaPrazoInstance}" as="list" />
</div>
</g:hasErrors>
<g:form action="save" method="post" >
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="cliente">
<g:message code="vendasaPrazo.cliente.label" default="Cliente"/></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'cliente', 'errors')}">
<g:select name="cliente.id" from="${sisgesso.Cliente.list()}"
optionKey="id" value="${vendasaPrazoInstance?.cliente?.id}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="numVenda"><g:message code="vendasaPrazo.numVenda.label"
default="Número Venda" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'numVenda', 'errors')}">
<g:textField name="numVenda" value="${fieldValue(bean: vendasaPrazoInstance,
field: 'numVenda')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="dataVenda"><g:message code="vendasaPrazo.dataVenda.label"
default="Data de Venda" /></label>
</td>
116
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'dataVenda', 'errors')}">
<g:datePicker name="dataVenda" format="dd/mm/aaaa" precision="day"
value="${vendasaPrazoInstance?.dataVenda}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="valorTotal">
<g:message code="vendasaPrazo.valorTotal.label" default="Valor Total" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'valorTotal', 'errors')}">
<g:textField name="valorTotal" value="${fieldValue(bean: vendasaPrazoInstance,
field: 'valorTotal')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="valorPago">
<g:message code="vendasaPrazo.valorPago.label" default="Valor Pago" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'valorPago', 'errors')}">
<g:textField name="valorPago" value="${fieldValue(bean: vendasaPrazoInstance,
field: 'valorPago')}" />
</td>
</tr>
<!--tr class="prop">
<td valign="top" class="name">
<label for="valorReceber">
<g:message code="vendasaPrazo.valorReceber.label" default="Valor Receber" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'valorReceber', 'errors')}">
<g:textField name="valorReceber" value="${fieldValue(bean: vendasaPrazoInstance,
field: 'valorReceber')}" />
</td>
</tr-->
<tr class="prop">
<td valign="top" class="name">
<label for="dataPagamento">
<g:message code="vendasaPrazo.dataPagamento.label" default="Data Pagamento" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'dataPagamento', 'errors')}">
<g:datePicker name="dataPagamento" format="dd/mm/aaaa" precision="day"
value="${vendasaPrazoInstance?.dataPagamento}" />
</td>
</tr>
</tbody>
117
</table>
</div>
<div class="buttons">
<span class="button"><g:submitButton name="create" class="save"
value="${message(code: 'default.button.create.label', default: 'Create')}" /></span>
</div>
</g:form>
</div>
</body>
</html>
Vendasaprazo/create.gsp
<%@ page import="sisgesso.VendasaPrazo" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'vendasaPrazo.label', default: 'VendasaPrazo')}" />
<title><g:message code="default.edit.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Principal</a></span>
<span class="menuButton"><g:link class="list" action="list"><g:message code="default.list.label"
args="[entityName]" /></g:link></span>
<span class="menuButton"><g:link class="create" action="create">
<g:message code="default.new.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.edit.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<g:hasErrors bean="${vendasaPrazoInstance}">
<div class="errors">
<g:renderErrors bean="${vendasaPrazoInstance}" as="list" />
</div>
</g:hasErrors>
<g:form method="post" >
<g:hiddenField name="id" value="${vendasaPrazoInstance?.id}" />
<g:hiddenField name="version" value="${vendasaPrazoInstance?.version}" />
<g:hiddenField name="valorReceber" value="${vendasaPrazoInstance?.valorReceber}" />
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name">
<label for="cliente"><g:message code="vendasaPrazo.cliente.label" default="Cliente" />
</label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
118
field: 'cliente', 'errors')}">
<g:select name="cliente.id" from="${sisgesso.Cliente.list()}" optionKey="id"
value="${vendasaPrazoInstance?.cliente?.id}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="numVenda"><g:message code="vendasaPrazo.numVenda.label"
default="Número Venda" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'numVenda', 'errors')}">
<g:textField name="numVenda" value="${fieldValue(bean: vendasaPrazoInstance,
field: 'numVenda')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="dataVenda"><g:message code="vendasaPrazo.dataVenda.label"
default="Data de Venda" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'dataVenda', 'errors')}">
<g:datePicker name="dataVenda" format="dd/mm/aaaa" precision="day"
value="${vendasaPrazoInstance?.dataVenda}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="valorTotal"><g:message code="vendasaPrazo.valorTotal.label"
default="Valor Total" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'valorTotal', 'errors')}">
<g:textField name="valorTotal" value="${fieldValue(bean: vendasaPrazoInstance,
field: 'valorTotal')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="valorPago"><g:message code="vendasaPrazo.valorPago.label"
default="Valor Pago" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance, field: 'valorPago',
'errors')}">
<g:textField name="valorPago" value="${fieldValue(bean: vendasaPrazoInstance,
field: 'valorPago')}" />
</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<label for="dataPagamento"><g:message code="vendasaPrazo.dataPagamento.label"
119
default="Data Pagamento" /></label>
</td>
<td valign="top" class="value ${hasErrors(bean: vendasaPrazoInstance,
field: 'dataPagamento', 'errors')}">
<g:datePicker name="dataPagamento" format="dd/mm/aaaa" precision="day"
value="${vendasaPrazoInstance?.dataPagamento}" />
</td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<span class="button"><g:actionSubmit class="save" action="update" value="${message(code:
'default.button.update.label', default: 'Update')}" /></span>
<span class="button"><g:actionSubmit class="delete" action="delete" value="${message(code:
'default.button.delete.label', default: 'Delete')}" onclick="return
confirm('${message(code: 'default.button.delete.confirm.message',
default: 'Are you sure?')}');" /></span>
</div>
</g:form>
</div>
</body>
</html>
Vendasaprazo/edit.gsp
<%@ page import="sisgesso.VendasaPrazo" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'vendasaPrazo.label', default: 'VendasaPrazo')}" />
<title><g:message code="default.list.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Principal</a></span>
<span class="menuButton"><g:link class="create" action="create">
<g:message code="default.new.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.list.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<div class="list">
<table>
<thead>
<tr>
<g:sortableColumn property="id" title="${message(code: 'vendasaPrazo.id.label',
default: 'Id')}" />
<th><g:message code="vendasaPrazo.cliente.label" default="Cliente" /></th>
<g:sortableColumn property="numVenda" title="${message(code:
120
'vendasaPrazo.numVenda.label', default: 'Número Venda')}" />
<g:sortableColumn property="dataVenda" title="${message(code:
'vendasaPrazo.dataVenda.label', default: 'Data de Venda')}" />
<g:sortableColumn property="valorTotal" title="${message(code:
'vendasaPrazo.valorTotal.label', default: 'Valor Total')}" />
<g:sortableColumn property="valorPago" title="${message(code:
'vendasaPrazo.valorPago.label', default: 'Valor Pago')}" />
</tr>
</thead>
<tbody>
<g:each in="${vendasaPrazoInstanceList}" status="i" var="vendasaPrazoInstance">
<tr class="${(i % 2) == 0 ? 'odd' : 'even'}">
<td><g:link action="show" id="${vendasaPrazoInstance.id}">
${fieldValue(bean: vendasaPrazoInstance, field: "id")}</g:link></td>
<td>${fieldValue(bean: vendasaPrazoInstance, field: "cliente")}</td>
<td>${fieldValue(bean: vendasaPrazoInstance, field: "numVenda")}</td>
<td><g:formatDate date="${vendasaPrazoInstance.dataVenda}" /></td>
<td>${fieldValue(bean: vendasaPrazoInstance, field: "valorTotal")}</td>
<td>${fieldValue(bean: vendasaPrazoInstance, field: "valorPago")}</td>
</tr>
</g:each>
</tbody>
</table>
</div>
<div class="paginateButtons">
<g:paginate total="${vendasaPrazoInstanceTotal}" />
</div>
</div>
</body>
</html>
Vendasaprazo/list.gsp
<%@ page import="sisgesso.VendasaPrazo" %>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<meta name="layout" content="main" />
<g:set var="entityName" value="${message(code: 'vendasaPrazo.label', default: 'VendasaPrazo')}" />
<title><g:message code="default.show.label" args="[entityName]" /></title>
</head>
<body>
<div class="nav">
<span class="menuButton"><a class="home" href="${createLink(uri: '/')}">Principal</a></span>
121
<span class="menuButton"><g:link class="list" action="list">
<g:message code="default.list.label" args="[entityName]" /></g:link></span>
<span class="menuButton"><g:link class="create" action="create">
<g:message code="default.new.label" args="[entityName]" /></g:link></span>
</div>
<div class="body">
<h1><g:message code="default.show.label" args="[entityName]" /></h1>
<g:if test="${flash.message}">
<div class="message">${flash.message}</div>
</g:if>
<div class="dialog">
<table>
<tbody>
<tr class="prop">
<td valign="top" class="name"><g:message code="vendasaPrazo.id.label" default="Id" />
< /td>
<td valign="top" class="value">${fieldValue(bean: vendasaPrazoInstance, field: "id")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><g:message code="vendasaPrazo.cliente.label"
default="Cliente" /></td>
<td valign="top" class="value">
<g:link controller="cliente" action="show" id="${vendasaPrazoInstance?.cliente?.id}">
${vendasaPrazoInstance?.cliente?.encodeAsHTML()}</g:link></td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="vendasaPrazo.numVenda.label" default="Número Venda" /></td>
<td valign="top" class="value">${fieldValue(bean: vendasaPrazoInstance,
field: "numVenda")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="vendasaPrazo.dataVenda.label" default="Data de Venda" /></td>
<td valign="top" class="value">
<g:formatDate date="${vendasaPrazoInstance?.dataVenda}" /></td>
</tr>
<tr class="prop">
<td valign="top" class="name">
<g:message code="vendasaPrazo.valorTotal.label" default="Valor Total" /></td>
<td valign="top" class="value">
122
${fieldValue(bean: vendasaPrazoInstance, field: "valorTotal")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><g:message code="vendasaPrazo.valorPago.label"
default="Valor Pago" /></td>
<td valign="top" class="value">
${fieldValue(bean: vendasaPrazoInstance, field: "valorPago")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><g:message code="vendasaPrazo.valorReceber.label"
default="Valor Receber" /></td>
<td valign="top" class="value">${fieldValue(bean: vendasaPrazoInstance,
field: "valorReceber")}</td>
</tr>
<tr class="prop">
<td valign="top" class="name"><g:message code="vendasaPrazo.dataPagamento.label"
default="Data Pagamento" /></td>
<td valign="top" class="value"><g:formatDate
date="${vendasaPrazoInstance?.dataPagamento}" /></td>
</tr>
</tbody>
</table>
</div>
<div class="buttons">
<g:form>
<g:hiddenField name="id" value="${vendasaPrazoInstance?.id}" />
<span class="button"><g:actionSubmit class="edit" action="edit" value="${message(code:
'default.button.edit.label', default: 'Edit')}" /></span>
<span class="button"><g:actionSubmit class="delete" action="delete" value="${message(code:
'default.button.delete.label', default: 'Delete')}" onclick="return
confirm('${message(code: 'default.button.delete.confirm.message',
default: 'Are you sure?')}');" /></span>
</g:form>
</div>
</div>
</body>
</html>
Vendasaprazo/show.gsp
<html>
<head>
<title><g:layoutTitle default="Grails" /></title>
<link rel="stylesheet" href="${resource(dir:'css',file:'main.css')}" />
<link rel="shortcut icon" href="${resource(dir:'images',file:'gesso.ico')}" type="image/x-icon" />
<g:layoutHead />
<g:javascript library="application" />
</head>
123
<body>
<div id="spinner" class="spinner" style="display:none;">
<img src="${resource(dir:'images',file:'spinner.gif')}" alt="Spinner" />
</div>
<div id="Logo" class="logo"><a href="/SisGesso/">
<img src="${resource(dir:'images',file:'sisgesso_logo.png')}" alt="Gesso" border="0" /></a>
</div>
<g:render template="/layouts/cabecalho" />
<g:layoutBody />
<g:render template="/layouts/rodape" />
</body>
</html>
Layout/main.gsp
<g:render template="/layouts/search" />
<div id="header">
<p class="header-sub">Gesso Arte de Araruama - Sis Gesso v. beta 0.1</p>
<div id="loginHeader">
<g:loginControl />
</div>
</div>
Layout/cabecalho.gsp
<div id="rodape">
<hr />
<g:copyright
startYear="2010">Sis. Gesso, Inc. - version beta 0.1</g:copyright>
</div>
Layout/rodape.gsp
<div id="search">
<g:form url='[controller: "cliente", action: "search"]'
id="SearchForm"
name="SearchForm"
method="get">
<g:textField name="q" value="${params.q}" />
<input type="submit" value="Procurar" />
</g:form>
</div>
Layout/search.gsp