terça-feira, 17 de junho de 2008

Erro esotérico ao inserir clobs e blobs no oracle

A mensagem de erro do Oracle "table or view does not exist" é simples e direta. O problema acontece quando você está fazendo uma série de inserções em uma tabela, utilizando a mesma String de SQL para todas, e lá pela sexta inserção a mensagem de erro aparece:
java.sql.SQLException: ORA-00942: table or view does not exist

Até que você descubra que pode haver algum problema com o sexto registro da tabela, você perde um bom tempo tentando encontrar algum problema de lógica ou alguma desatenção sua. Até mesmo bufa de raiva achando que é algum bug do Oracle. E de certa forma é.

No meu caso descobri que o erro acontecia quando inseria textos grandes demais em uma coluna clob. Encontrei então um documento da oracle explicando como gravar clobs no oracle utilizando JDBC. O macete é configurar o JDBC com o parâmetro SetBigStringTryClob, assim:
Properties propriedades = new Properties();
propriedades.put("user", "meuUsuario" );
propriedades.put("password", "pensaQueVouTeContar?");
propriedades.put("SetBigStringTryClob", "true");
return DriverManager.getConnection("jdbc:oracle:thin:@meuserver.com:filmes", propriedades);

Fazendo isso o estranho erro parou de acontecer. De certa forma acredito que este é um bug do Oracle, pois no mínimo a mensagem de erro deveria ser algo informando que o tamanho do valor excede o permitido na coluna. Ou até informando que não foi possivel inserir devido a erros no preenchimento da coluna tal.

Contudo, essa não foi a unica vez que este erro aconteceu comigo. Ao inserir dados em um campo blob o maldito "table or view does not exist" deu as caras novamente. No caso eu errei o tipo de dado utilizando setString. Mas dessa vez eu já estava calejado, então bastou verificar que para inserir blobs bastaria fazer algo como o mostrado abaixo:
byte[] arr = new byte[] {0,1};
ByteArrayInputStream b = new ByteArrayInputStream(arr);
statement.setBinaryStream(i, b, b.available() );

Mas que fique bem claro que o fato de eu estar inserindo clobs e blobs no banco não quer dizer que eu ache isso uma boa prática e que eu recomende. Ao contrário, acredito que seja melhor na maioria das situações que esses dados fiquem no sistema de arquivos.

sábado, 7 de junho de 2008

Cálculo da variância com Ruby

No blog Kodumaro foi publicado uma comparação de cálculos estatísticos entre linguagens imperativas, como C, e linguagens funcionais, no caso o Lisp. Os cálculos utilizados como exemplificação foram os de variância da população e variância da amostra. Como estou estudando ruby, me empolguei e resolvi fazer a versão desses cálculos nessa linguagem.

Veja como o código ficou:
class Array
def acumula()
acc = 0
each { |a| acc += yield(a) }
acc
end
def media()
total = acumula { |a| a }
total / length
end
def numerador_de_variancia()
m = media
acumula { |a| (a - media)**2 }
end
def variancia_populacional()
numerador_de_variancia / length
end
def variancia_da_amostra()
numerador_de_variancia / (length-1)
end
end
O que fiz foi criar novos métodos na classe Array para fazer todos os cálculos necessários. O interessante é que não encontrei na documentação do Ruby nenhum método que iterasse sobre um array e retornasse um acumulo dos valores calculados por um bloco. Para suprir isso criei então o método acumula.

Apesar do código ter ficado um pouco mais verboso que o código em lisp exibido no Kodumaro, o uso ficou mais simples. Como são novos métodos, para obter a variância da amostra em um array basta por exemplo fazer [2,4,6].variancia_da_amostra. Veja abaixo um exemplo de uso desses métodos:
array = [2,4,6]
puts "Media: #{array.media}"
puts "Numerador de variancia: #{array.numerador_de_variancia}"
puts "Variancia populacional: #{array.variancia_populacional}"
puts "Variancia da amostra: #{array.variancia_da_amostra}"
Mas é importante ressaltar o perigo dessa alteração de classe. Veja que não faço nenhum tratamento sobre o tipo de dado do array. No caso, estou assumindo que todo array que vá utilizar esses métodos tenham sempre todos os indices preenchidos por numeros.

EDS: Excell Driven Scrum

Para quem não sabe, EDS é o Excell Driven Scrum. Ou vocês achavam que siglas com a palavra mágica Driven serviam somente para Development? Estavam enganados. O EDS ocorre quando o P.O. elabora previamente um planejamento, sprint a sprint, do que deve ser feito pelo time para um longo período de tempo. Este planejamento é então gravado em um arquivo excell read-only.

Pelo pouco que entendo de Scrum, esse padrão é uma maneira muito equivocada de se pôr em prática um ambiente de desenvolvimento ágil. Isso porque a definição dos sprints deveria ser feita com a particiação do time. E mesmo se esse tal planejamento anual fosse feito com o time, ainda sim eu consideraria errado, pois é impossivel prever com muita antecedência fatores ambientais que fatalmente mudam as necessidades e problemas que precisam ser resolvidos.

Além disso, o fato de termos escopos definidos para cada sprint, faz o sprint planning 1 ficar engessado. O time perde a liberdade de sugerir histórias prioritárias que garantam a qualidade do sistema. Isso acaba causando um desgaste durante a reunião, o time não entendendo como o P.O. pode ignorar os possiveis problemas da aplicação e os riscos que eles podem causar, e o P.O. sem entender porque o time não aceita seguir o planejamento anual.

Acontece que muitas vezes o uso deste padrão não é culpa do P.O., é uma parte da cultura anterior à adoção do Scrum que ainda não foi possivel mudar. Contudo, não se pode fechar os olhos para este problema. Para acabar com o EDS, é preciso que o P.O. brigue corajosamente contra ele. O time também precisa bater o pé o máximo possivel para mudar esta cultura. E a briga não é apenas para mudar a cabeça de quem está acima hierarquicamente, é também para mudar suas próprias mentes.

Portanto, apesar de ter pouca experiência com scrum, acredito que utilizar o padrão EDS não é uma boa alternativa de implantação dele. Pela minha vivência de menos de um ano e a participação em apenas um curso, me parece que um dos principais benefícios que o Scrum traz é a liberdade que o time tem para identificar problemas e resolve-los para garantir a qualidade do produto. Com o EDS isso não é possivel.

sexta-feira, 6 de junho de 2008

Como obter a URL do SWF

Essa aqui é básica, mas eu procurei bastante no Google e não encontrei. Acabei só achando no help do próprio flash. Caso você precise obter no seu aplicativo flash qual é o caminho completo do seu SWF, basta utilizar a propriedade loaderURL do objeto loaderInfo contido no seu MovieClip. Veja abaixo:

package {
public class Main extends MovieClip {
function Main() {
trace( "URL do SWF: " + this.loaderInfo.loaderURL );
}
}
}

quinta-feira, 15 de maio de 2008

FullScreen do flash não funciona em players abaixo de 9.0.28, e agora?

Desde a versão 9.0.28 do flash é possível definir seu swf para rodar em tela cheia de maneira bem simples. Também é possivel definir um listener para ser notificado quando o modo de exibição do flash é alterado de normal para fullscreen e vice-versa. Contudo, se você colocar essas funcionalidades, seu swf não rodará em versões anteriores a 9.028.

O jeito então é alterar seu ActionScript para só setar a o fullscreen quando a versão for compatível. Veja abaixo como isso pode ser feito:
import flash.display.StageDisplayState;

public class Main extends MovieClip {

//...

function suportaTelaCheia():Boolean {
//Verifica versao >= que 9.0.28
}
function onBotaoTelaCheiaClique(event:MouseEvent):void {
if( suportaTelaCheia() ) {
stage.displayState = StageDisplayState.FULL_SCREEN;
}
}
function onBotaSaiTelaCheiaClique(event:MouseEvent):void {
if( suportaTelaCheia() ) {
stage.displayState = StageDisplayState.NORMAL;
}
}

//...
}
Apesar da classe flash.display.StageDisplayState não existir no player com versão menor que 9.0.28, a importação dela não trará erro, pois o flash só executa a importação de uma classe quando faz uso dela. Portanto a seleção da versão feita com o método suportaTelaCheia impede seu uso e consequente importação.

O problema maior é quando precisamos definir um listener para sabermos quando o swf entrou em modo fullscreen e quando saiu. Normalmente isso é necessário quando precisamos redimensionar, ou adicionar elementos ao stage. No caso o código, sem o tratamento de versão, ou seja, o que daria erro nas versões de flash anteriores a 9.0.28 ficaria assim:
import flash.events.FullScreenEvent;

public class Main extends MovieClip {

//...

private function onStage(event:Event):void {
stage.addEventListener( FullScreenEvent.FULL_SCREEN, redrawFullScreen );
}

//...

function redrawFullScreen(event:FullScreenEvent):void {
if( event.fullScreen ) {
//Posicionar elementos para tela cheia
} else {
//Posicionar elementos para tamanho normal
}
}

//....

}
O grande problema disso é que o uso da classe FullScreenEvent está na definição do método redrawFullScreen e portanto não é possivel defini-lo somente quando for uma versão ou outra. O jeito então então é mudar a assinatura do método, para ao invés de receber um FullScreenEvent receber um Event. Com essa correção o código ficaria assim:
import flash.events.FullScreenEvent;

public class Main extends MovieClip {

//...

function suportaTelaCheia():Boolean {
//Verifica versao >= que 9.0.28
}

private function onStage(event:Event):void {
if( suportaTelaCheia() ) {
stage.addEventListener( FullScreenEvent.FULL_SCREEN, redrawFullScreen );
}
}

//...

function redrawFullScreen(event:Event):void {
if( event['fullScreen'] ) {
//Posicionar elementos para tela cheia
} else {
//Posicionar elementos para tamanho normal
}
}

//....

}
Repare que além de ter mudado a assinatura do método, mudei também o uso do objeto event. Isso foi feito porque a classe Event não possui o atributo fullScreen declarado, então utilizar event.fullScreen daria erro de compilação. Mas como sabemos que ali há este atributo, podemos utilizá-lo usando a notação event['fullScreen'].

quarta-feira, 14 de maio de 2008

Como instalar o flash 9.0.16 para testar seu swf

É muito importante que ao criar um swf complexo, como um player de vídeo por exemplo, você execute testes em todas as versões de flash player que você deseja que ele suporte. Para isso a Adobe oferece em seu site diversas versões antigas do flash player para download. Contudo, se você está no Windows XP e deseja intalar a versão 9.0.16, mas já possui uma versão mais nova instalada, é preciso seguir os seguintes passos.

Primeiro é preciso desinstalar a versão atual do flash player executando o programa uninstall_flash_player.exe encontrado na página How to uninstall the Adobe Flash Player plug-in and ActiveX control. Depois de executar isso é preciso acessar o regedit e remover a seguinte chave: HKEY_LOCAL_MACHINE\SOFTWARE\Macromedia\FlashPlayer.

Feito isso, reinicie o computador. Ok ok, paciência, estamos lidando com Windows portanto todo cuidado é pouco. Mãos a obra: Iniciar > Reiniciar (Heim?).

Após reiniciado basta instalar a versão 9.0.16 que pode ser baixada em Archived Flash Players available for testing purposes. Nesse link podem ser encontradas diversas outras versões do flash player e é bem possível que o escrito aqui sirva para instalar essas outras versões.

terça-feira, 6 de maio de 2008

Undefined method *_path no uso do form_for

Essa é uma dica boba para aqueles que estão começando em ruby on rails como eu. Fazendo aqui um exemplos simples de cadastro de filmes recebi o seguinte erro:
NoMethodError in Filme#incluir

Showing filme/incluir.html.erb where line #5 raised:

undefined method `filmes_path' for #<ActionView::Base:0xb76f144c>

Extracted source (around line #5):

2:
3: <%= error_messages_for :filme %>
4:
5: <% form_for @filme do |f| %>
6: <p>
7: <b>Título</b><br />
8: <%= f.text_field :titulo %>
Pelo que eu entendi até agora, isso acontece porque eu não estou utilizando os métodos padrões do ruby on rails. Se eu estiver falando besteira por favor me corrijam. O fato é que resolvi isso definindo a ação que o form deverá executar, o código da view então ficaria da seguinte forma:
<h1>Incluir novo filme</h1>

<%= error_messages_for :filme %>

<% form_for @filme, :url => { :action => 'salvar' } do |f| %>
<p>
<b>Título</b><br />
<%= f.text_field :titulo %>
</p>

<p>
<b>Resumo</b><br />
<%= f.text_area :resumo %>
</p>

<p>
<%= f.submit "Incluir" %>
</p>
<% end %>
No caso o controlador possui o método salvar.