Versão 0.1.0 Manual FoRc Última Atualização: Maio/2007 Daniel Schmitz danieljfa@gmail.com
Sumário 1. O que é FoRc?... 3 2. O que é Adobe Flex?... 3 3. O que é Ruby on Rails?... 3 4. Flex + Ruby on Rails?... 3 5. Instalação... 4 6. Exemplo Prático 1 Populando um DataGrid... 4 6.1. Criando a aplicação Rails... 4 6.2. Inserindo dados... 5 6.3. Criando o controller... 5 6.4. Iniciando o servidor... 6 6.5. Crinado um projeto Flex... 7 6.6. Instalando o FoRc... 8 6.7. Criando o DataGrid... 10 6.8. Componente RailsConnection... 11 6.9. Componente RailsController... 12 6.10. Componente RailsAction... 14 6.11. Componente RailsButton... 15 6.12. Código Completo... 16 7. Exemplo prático 2 Inserindo Dados... 17 7.1. Revisando... 17 7.2. Criando o objeto para envio ao Rails... 18 7.3. Capturando erros... 19
1. O que é FoRc? FoRc é um conjunto de componentes destinado a prover comunicação entre os frameworks Adobe Flex e Ruby on Rails. 2. O que é Adobe Flex? O Flex está cada vez mais se destacando como um framework de desenvolvimento de aplicativos web com a utilização de aplicações ricas para a Internet, chamados de RIA. Estamos adotando neste manual que a ferramenta Adobe Flex é conhecida pelo leitor. Estaremos utilizando a ferramenta Adobe Flex Builder, versão 2.0.1, para ilustrar nossos exemplos. Maiores informações sobre Flex em: http://www.adobe.com/products/flex http://www.flex.org http://www.imasters.com.br/secao/flex 3. O que é Ruby on Rails? O RoR é um framework de desenvolvimento de sistemas web, que utiliza o padrão MVC (Modelo, Visão e Controle) e possui conceitos e metodologias que otimizam o tempo de desenvolvimento do programador. Se você desenvolve sistemas para a WEB e ainda não conhece o RoR, seria bom conhecer este fabuloso framework. Maiores informações sobre Ruby on Rails em: http://www.rubyonrails.org http://www.akitaonrails.com.br 4. Flex + Ruby on Rails? Talvez o que mais desanima um programador web é programar html. A mesclagem de html com código comum ( Rails, ASP, PHP, Java, etc) pode ser considerado, nos dias de hoje, um dos maiores fatores que contribuem para a desmotivação de um programador. Não entraremos em discussão sobre este assunto. Saiba apenas que, por mais que o Ruby on Rails se esforce, a junção de HTML no seu código o deixa um pouco pior (Como em qualquer outro tipo de linguagem). Neste momento entra o Adobe Flex, uma ferramenta para desenvolvimento de sistemas utilizando conceitos RIA. Seguindo então o padrão MVC, temos o seguinte esquema: Classes de Modelo e Controle: São orquestradas pelo Ruby on Rails, onde criamos nossos Models e Controllers. Estamos adotando que isto não é uma novidade para você. Classes de Visão: Aqui o Flex entra em ação, desenvolvendo sistemas e desenhando formulários.
5. Instalação Antes de mais nada, estamos adotando que: Você conhece Ruby on Rails e ele está instalado corretamente Você conhece Adobe Flex e sabe utilizar arquivos SWC A instalação do FoRc é muito simples. Basta baixar o arquivo FoRc.swc e adicionar esta biblioteca ao seu projeto Flex. Vamos a um exemplo prático: 6. Exemplo Prático 1 Populando um DataGrid 6.1. Criando a aplicação Rails Crie um projeto Rails chamado agendatelefonica # rails agendatelefonica Configure o acesso ao banco de dados utilizando o arquivo config/database.yml. Lembre-se que você deve possuir 2 bancos: AgendaTelefonica_development e AgendaTelefonica_test Vamos criar uma classe de modelo chamada telefone, veja: # ruby script/generate model telefone exists app/models/ exists test/unit/ exists test/fixtures/ create app/models/telefone.rb create test/unit/telefone_test.rb create test/fixtures/telefones.yml create db/migrate create db/migrate/001_create_telefones.rb Para criarmos a nossa tabela de telefones, vamos editar o arquivo db/migrate/001_create_telefones.rb class CreateTelefones < ActiveRecord::Migration def self.up create_table :telefones do t t.column :nome, :string t.column :tel, :string end end def self.down drop_table :telefones end
end E executar o seguinte comando: # rake db:migrate (in C:/workspace2/AgendaTelefonica) CreateTelefones: migrating create_table(:telefones) CreateTelefones: migrated (0.0470s) Já temos uma tabela telefones e uma classe TelefoneModel em app/db 6.2. Inserindo dados Abrimos um console e inserios alguns números de telefone: # ruby script/console Loading development environment. >> Telefone.create :nome=>'daniel', :tel=>'123456' =>#<Telefone:0x48a8e88 @errors=#<activerecord::errors:0x4865494 @errors={}, @ba se=#<telefone:0x48a8e88...>>, @attributes={"nome"=>"daniel", "id"=>1, "tel"=>"1 23456"}, @new_record=false> >> Telefone.create :nome=>'josé', :tel=>'111222' => #<Telefone:0x4860e08 @errors=#<activerecord::errors:0x48600ac @errors={}, @ba se=#<telefone:0x4860e08...>>, @attributes={"nome"=>"jos\202", "id"=>2, "tel"=>" 111222"}, @new_record=false> >> Telefone.create :nome=>'joão', :tel=>'333444' => #<Telefone:0x485c088 @errors=#<activerecord::errors:0x485b32c @errors={}, @ba se=#<telefone:0x485c088...>>, @attributes={"nome"=>"joão", "id"=>3, "tel"=>"333 444"}, @new_record=false> Temos então 3 registros em nosso banco de dados. 6.3. Criando o controller Agora vamos criar o Controller, veja: # ruby script/generate controller Telefone listar inserir exists app/controllers/ exists app/helpers/ create app/views/telefones exists test/functional/ create app/controllers/telefones_controller.rb create test/functional/telefones_controller_test.rb create app/helpers/telefones_helper.rb create app/views/telefones/listar.rhtml
create app/views/telefones/inserir.rhtml O comando acima criou a classe TelefoneController e os métodos (actions) listar e inserir. Vamos editar o arquivo app/controllers/telefones_controller.rb para: class TelefoneController < ApplicationController def listar @telefones = Telefone.find :all render :text => @telefones.to_json end def inserir @telefone = Telefone.new(params[:telefone]) @telefone.save render :text => @telefone.to_json end end E vamos alterar o arquivo app/models/telefone.rb class Telefone < ActiveRecord::Base end validates_presence_of :nome, :message => "O Campo nome não pode ser vazio" validates_presence_of :tel, :message => "O Campo telefone não pode ser vazio" 6.4. Iniciando o servidor Para testar, iremos iniciar nosso servidor: # ruby script/server => Booting WEBrick... => Rails application started on http://0.0.0.0:3000 => Ctrl-C to shutdown server; call with --help for options [2007-04-30 01:24:44] INFO WEBrick 1.3.1 [2007-04-30 01:24:44] INFO ruby 1.8.6 (2007-03-13) [i386-mswin32] [2007-04-30 01:24:44] INFO WEBrick::HTTPServer#start: pid=3888 port=3000 E acessar o endereço: http://localhost:3000, obtendo:
O resultado encontrado é algo do tipo: [{attributes: {nome: "Daniel", tel: "123456", id: "1"}}, {attributes: {nome: "Jos ", tel: "111222", id: "2"}}, {attributes: {nome: "Jo o", tel: "333444", id: "3"}}] Este formato de dados é do tipo JSON, um formato especial, conhecido por serializar objetos em JavaScript. Sempre recomendo a utilização deste padrão, ao invés do XML comum. Por enquanto não fizemos nada relativo ao FoRc, exceto por criar uma saída JSON. O método to_json é do próprio Ruby on Rails. Vamos então partir para o Flex. As classes do FoRc devem ser utilizadas no Flex, sempre. 6.5. Crinado um projeto Flex Crie um novo projeto Flex: Escolha Basic e clique em Next. Não clique em Finish Em Project Name, coloque AgendaTelefonica. Clique em Next
6.6. Instalando o FoRc A próxima tela é muito importante. Você deverá incluir a biblioteca FoRc. Clique em Library Path e depois em Add Swc. Adicione o arquivo FoRc.swc Ainda nesta tela, em OutPut Folder, coloque o caminho para a pasta public do seu rails seguido da pasta bin. Isso faz com que o arquivo SWF gerado pelo Flex seja copiado para esta pasta. Em Output Folder URL, coloque o caminho Url para a sua aplicação Rails. Geralmente é http://localhost:3000/bin. A sua tela fica deste jeito:
A sua aplicação é criada e você pode testar-la clicando no botão RUN. Acima, temos nossa aplicação, ainda vazia, mas funcionando sobre o servidor do Ruby on Rails. Voltando ao Flex, ative o modo Design de AgendaTelefonica.mxml. Na Aba Components, temos:
6.7. Criando o DataGrid Na pasta Custom, temos diversos componentes prontos para serem usados. Mas antes disso, vamos criar um DataGrid. Arquivo AgendaTelefonica.mxml : <?xml version="1.0" encoding="utf-8"?> <mx:application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute"> <mx:datagrid x="10" y="10" width="100%"> <mx:columns> <mx:datagridcolumn headertext="nome" datafield="nome"/> <mx:datagridcolumn headertext="telefone" datafield="telefone"/> </mx:columns> </mx:datagrid> </mx:application>
Temos então: 6.8. Componente RailsConnection Agora vamos usar nossos componentes. O Primeiro deles é o RailsConnection onde você fornecerá informações sobre o servidor Rails. Arrasta o componente para a sua tela, ficando deste jeito: Ao selecionar o componente RailsConnection no seu projeto, devemos observar a janela Flex Properties, no modo completo, veja:
Veja que, quando selecionamos o componente RailsConnection, devemos informar algumas propriedades para ele na aba Flex Properties. Id : rconn port: 3000 server: http://localhost Informamos qual o id do componente, pois iremos referenciá-lo nos nossos controllers. Em port, escolhemos a porta em que a aplicação está rodando. Geralmente é 3000 quando estamos na fase de desenvolvimento. Em server, inserimos o caminho para o servidor rails que está rodando a aplicação da agenda telefônica. 6.9. Componente RailsController Agora devemos arrastar outro componente para nossa aplicação, é o RailsController. E também preenchemos algumas informações sobre ele. Veja:
As propriedades do RailsController são: id: TelefoneController Connection: {rconn} Controller: telefone Informamos o seu ID, para que possamos referenciá-lo em outros componentes. Então fornecemos qual a sua conexão, através da propriedade Connection. Veja que esta propriedade está com seu valor entre colchetes, pois ele referencia um ID de outro componente. Lembre-se sempre de usar colchetes nessas ocasiões. Em Controller, inserimos qual o controller do Ruby On Rails. Como já criamos o controller telefone no início no nosso tutorial, inserimos agora telefone.
Agora inserimos mais um componente, o RailsAction, veja: 6.10. Componente RailsAction O RailsAction é reponsável em representar uma action do Ruby on Rails. As propriedades deste componente são: id: TelefoneListar Action: Controller: listar {TelefoneController} BusyCursor: true Method: GET RequestVar: UseProxy: false ErrorHandler: ErrorWindow: ResultCall: ResultCallFuncton: ResultFormat: ResultType Function {setdados} text JSON Este componente possui mais atributos que os outros pois é ele que trata relativamente da
conexão com o servidor. O atributo Action informa qual a action que será executada. O Controller é definido por {TelefoneController}. Então temos uma RailsAction que aponta para um RailsController, que por sua vez aponta para um RailsConnection. BusyCursor indica se o Cursor do mouse deve ficar em estado de espera quando a requisição estiver sendo realizada. Method é o método de passagem de parâmetros, que pode ser GET ou POST. RequestVar aponta para uma função que deve retornar um objeto contendo dados. Este objeto será passado para o Rails. Utilizamos RequestVar quando criamos um formulário para inserir/editar dados. Neste momento podemos deixar ele vazio. UseProxy: Caso necessite utilizar proxy. Geralmente esta opção é false. ErrorHandler e ErrorWindow: Para gerenciar erros. Veremos mais adiante. ResultCall: Quando o rails responder, o que devemos fazer?? Neste caso, escolhemos Function, isto é, uma função será chamada. ResultCallFunction: Se ResultCall for function, devemos informar qual a função que será executada. Neste caso, a função se chama setdados. Iremos montar esta função mais adiante. ResultFormat: O tipo de formato que devemnos esperar do Rails. Como estamos trabalhando com JSON, o formato é text. ResultType: O tipo de resposta que devemos esperar do Rails. Neste caso, usamos JSON. Veja que definimos o método setdados em ResultCallFunction. Isso indica que quando o Rails responder, ele deverá executar este método. Vamos ao código deste método: [Bindable] public var dados:arraycollection = new ArrayCollection(); public function setdados(rst:object):void { dados.removeall(); for ( var i:int=0;i<rst.length;i++) { var obj:object = new Object() obj.nome = rst[i].attributes.nome; obj.telefone = rst[i].attributes.tel; } } dados.additem(obj); Criamos uma variável do tipo ArrayCollection, chamda dados, e de acordo com o resultado do Rails, preenchemos esta variável. Agora temos que dizer ao DataGrid que use a variável dados como DataProvider. 6.11. Componente RailsButton Como última tarefa, temos que criar um botão que vai chamar o método listar do Ruby on Rails. Para isso, arraste um RailsButton para sua aplicação e define a propriedade Action para {TelefoneListar}. Quando o botão for clicado, ele automaticamente irá chamar a action em questão.
6.12. Código Completo O código completo da aplicação está abaixo: <?xml version="1.0" encoding="utf-8"?> <mx:application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:ns1="forc.connection.*" xmlns:ns2="forc.controls.*"> <mx:datagrid x="10" y="10" width="100%" dataprovider="{dados}"> <mx:columns> <mx:datagridcolumn headertext="nome" datafield="nome"/> <mx:datagridcolumn headertext="telefone" datafield="telefone"/> </mx:columns> </mx:datagrid> <ns1:railsconnection x="10" y="174" server="http://localhost" port="3000" id="rconn"/> <ns1:railscontroller x="34" y="174" id="telefonecontroller" Connection="{rconn}" Controller="telefone"/> <ns1:railsaction x="58" y="174" id="telefonelistar" Action="listar" Controller="{TelefoneController}" BusyCursor="true" Method="GET" UseProxy="false" ResultCall="Function" ResultFormat="text" ResultType="JSON" ResultCallFunction="{setDados}"/> <mx:script> <![CDATA[ import mx.collections.arraycollection; [Bindable] public var dados:arraycollection = new ArrayCollection(); public function setdados(rst:object):void { dados.removeall(); for ( var i:int=0;i<rst.length;i++) { var obj:object = new Object() obj.nome = rst[i].attributes.nome; obj.telefone = rst[i].attributes.tel; } ]]> </mx:script> } dados.additem(obj); <ns2:railsbutton x="10" y="198" id="buttonlistartelefones" Action="{TelefoneListar}" label="obter Dados"/> </mx:application>
Ao executar esta aplicação, temos: Veja que os ícones de configuração do Rails não aparecem na aplicação. Eles estão configurados para aparecerem somente no modo Design View do Flex. Veja também que, com o mínimo de código, conseguimos criar uma aplicação que conversa com o servidor rails e consegue apresentar dados na tela. 7. Exemplo prático 2 Inserindo Dados 7.1. Revisando... Agora que podemos mostrar os dados no DataGrid, vamos inserir dados no sistema. Como já criamos o action inserir def inserir @telefone = Telefone.new(params[:telefone]) @telefone.save render :text => @telefone.to_json end vamos inserí-lo no nosso projeto Flex. Para fazer isso, traga mais um componente RailsAction e ligue-o ao Controller TelefoneController. As configurações deste RailsAction ficam desta forma:
7.2. Criando o objeto para envio ao Rails Além das configurações normais que você já conhece, temos uma novidade. A primeira delas é a RequestVar, um método especial que DEVE retornar um objeto pré formatado com os dados do formulário. Este método é mostrado abaixo: public function getformdata():object { var obj:object = new Object(); obj['telefone[nome]'] = edtnome.text; obj['telefone[tel]'] = edttelefone.text; } return obj;
O segredo está no formato obj['telefone[nome]'] que será enviado ao Rails. Funciona dessa forma: Outra novidade está na forma como o RailsAction se comporta quando os dados são retornados do Ruby on Rails. Neste nosso caso, ao invés de definir uma função, estamos definindo outro RailsAction através dos parâmetros: ResultCall: ResultCallAction: RailsAction {TelefoneListar} Assim, este RailsAction, que é responsável em carregar os dados no DataGrid, é executado trazendo o novo registro. Só faltou mostrar os campos do formulário que foram criados no projeto, veja: <mx:textinput x="82" y="228" id="edtnome"/> <mx:label x="32" y="230" text="nome:"/> <mx:label x="15" y="256" text="telefone:"/> <mx:textinput x="82" y="254" id="edttelefone"/> <ns2:railsbutton x="177" y="284" label="inserir" Action="{TelefoneInserir}"/> 7.3. Capturando erros O Ruby On Rails impementa alguns conceitos como o DRY, dont repeat yourself... Não faça de novo :) Isso quer dizer que devemos fazer com que o FoRc capture os erros gerados pelo Ruby On Rails, principalmente erros de validação nos campos. Implementar esta técnica é fácil, basta inserir o componente RailsError no seu projeto, definir um id para ele e, para cada RailsAction que você deseja capturar os erros, informar a classe através da propriedade ErrorHandler. A outra propriedade, ErrorWindow, indica qual o container visual que você deseja que a mensagem de erro apareça. Geralmente colocamos this, que é a própria aplicação.
Veja: e Acima, os erros que surgiram no Flex foram definidos pela classe de modelo do Ruby on Rails, mantendo assim o conceito DRY. Atenção: A captura de erros como a chamada a outro RailsAction funcionam somente no padrão JSON. Estamos trabalhando para melhorar estas funcionalidades.
Novamente, o código completo: <?xml version="1.0" encoding="utf-8"?> <mx:application xmlns:mx="http://www.adobe.com/2006/mxml" layout="absolute" xmlns:ns1="forc.connection.*" xmlns:ns2="forc.controls.*" xmlns:ns3="forc.error.*"> <mx:datagrid x="10" y="10" width="100%" dataprovider="{dados}"> <mx:columns> <mx:datagridcolumn headertext="nome" datafield="nome"/> <mx:datagridcolumn headertext="telefone" datafield="telefone"/> </mx:columns> </mx:datagrid> <ns1:railsconnection x="10" y="174" server="http://localhost" port="3000" id="rconn"/> <ns1:railscontroller x="34" y="174" id="telefonecontroller" Connection="{rconn}" Controller="telefone"/> <ns1:railsaction x="58" y="174" id="telefonelistar" Action="listar" Controller="{TelefoneController}" BusyCursor="true" Method="GET" UseProxy="false" ResultCall="Function" ResultFormat="text" ResultType="JSON" ResultCallFunction="{setDados}"/> <mx:script> <![CDATA[ import mx.collections.arraycollection; [Bindable] public var dados:arraycollection = new ArrayCollection(); public function setdados(rst:object):void { dados.removeall(); for ( var i:int=0;i<rst.length;i++) { var obj:object = new Object() obj.nome = rst[i].attributes.nome; obj.telefone = rst[i].attributes.tel; } } dados.additem(obj); public function getformdata():object { var obj:object = new Object(); obj['telefone[nome]'] = edtnome.text; obj['telefone[tel]'] = edttelefone.text; } return obj; ]]> </mx:script> <ns2:railsbutton x="10" y="198" id="buttonlistartelefones" Action="{TelefoneListar}" label="obter Dados"/> <ns1:railsaction x="82" y="174" Action="inserir" Controller="{TelefoneController}" id="telefoneinserir" BusyCursor="true" Method="POST" UseProxy="false" RequestVar="{getFormData}"
ResultCall="RailsAction" ResultCallAction="{TelefoneListar}" ResultFormat="text" ResultType="JSON" ErrorHandler="{railserror}" ErrorWindow="{this}"/> <mx:textinput x="82" y="228" id="edtnome"/> <mx:label x="32" y="230" text="nome:"/> <mx:label x="15" y="256" text="telefone:"/> <mx:textinput x="82" y="254" id="edttelefone"/> <ns2:railsbutton x="177" y="284" label="inserir" Action="{TelefoneInserir}"/> <ns3:railserror x="106" y="174" id="railserror"/> </mx:application>