Processing.js

[edit:] maybe you’ll prefer this version in english

Encontrei há algum tempo atrás o porting para javascript do Processing. Esta versão foi desenvolvida por John Resig, o mesmo criador do jQuery e chama-se Processing.Js

O meu interesse no Processing tem a ver com duas coisas: 1. visualização / exploração de grandes volumes de informação; 2. visualização de dados em mapas (com eventual interactividade)

Apesar de já ter brincado um pouco com a versão base do Processing nunca explorei a fundo (há sempre outras coisas para fazer). O facto de poder fazer quase tudo dentro do browser acrescentou a motivação extra que necessitava.

Assim comecei a tentar desenvolver os exemplos apresentados no clássico “Visualizing Data” de Ben Fry em processing.js

Até agora já tenho os exemplos seguintes:

Algumas notas que já recolhi:

  • a mais importante é que a base para o desenho dos elementos é o tag canvas que não é suportado no internet explorer;
  • um primeira e única experiência com o excanvas não resultou em nada;
  • em relação ao carregamento de imagens, a imagem tem que ser carregada com a página, ou pelo menos já tem que haver um tag img na página que, parece-me, irá servir de placeholder para a imagem propriamente dita
  • o id dessa imagem é o mesmo id que é usado no loadImage
  • também o carregamento de ficheiros externos teve que ser diferente do que estava no livro (ver  table.js)

Share

geeky stuff: chamadas ajax

Uma das minhas ferramentas favoritas da actualidade é o jQuery mas não vale a pena estar sempre a repetir de que forma ela altera todo o processo de desenvolvimento client-side web.

Algo que passou a ser bastante trivial de fazer foram as chamadas ajax. Por esta altura já toda a gente sabe que a técnica em si mesma não é nova mas de qualquer forma não fossem um conjunto de técnicas e ferramentas que surgiram nos últimos anos e ainda estariamos na web1.0

Assim, para fazermos pedidos Ajax em jQuery temos simplesmente que:

     $.ajax({
        type: "GET",
        url: endereco.aspx,
        dataType: "text",
        data: { 'Operation': 'nome',
                'nome_parametro1': 'valor_parametro1' },
        success: function(data, textStatus){
                   alert('ok');
                 },
        complete: function(XMLHttpRequest, textStatus){
                    alert('fim');
                  },
        error: function (XMLHttpRequest, textStatus, errorThrown) {
                  alert("erro:" + XMLHttpRequest.responseText);
              }
      });

      alert('teste');

De notar que neste exemplo estou a assumir que o resultado vai ser um pedaço de JSON.
Uma questão que convém sempre relembrar é que, pelo menos neste exemplo, as chamadas são assíncronas (é o primeiro A de Ajax), e por isso a ordem pelo qual o código é executado pode pregar algumas partidas, ou seja, aquele ultimo alert(‘teste’) poderá não correr só depois de completada a instrução $.ajax já que, a partir do momento em que é feita a chamada ajax, esse bocado do código passa a correr “paralelamente” (não é, mas quase) ao código que está na instrução seguinte (neste caso o alert(‘teste’).
De referir ainda que o bocado que está no “complete” vai ser sempre executado indepdentemente do resultado ter sido “success” ou “error”.

Share

best practices… not

Um dos podcasts que acompanho regularmente é o hanselminutes de scott hanselman. neste último episódio ele conversou com o codinghorror jeff atwood acerca do site stackoverflow.

Conversa interessante sobre tecnologia e tal, asp.net mvc, sql, sigla para aqui sigla para ali, mas a parte mais interessante é mesmo quando se começa a falar do dia-a-dia de desenvolvimento e se percebe a forma como a equipa stackoverflow trabalha é, no mínimo, ágil demais.

Pelos vistos o scott hanselman já participou no desenvolvimento de aplicações para o sector financeiro e num ambiente em que a segurança era levada muito a sério (não como algumas estórias que ouvi sobre o desenvolvimento de alguns homebankings nacionais), assim quando o jeff atwood diz coisas como o servidor web e o servidor de base de dados estão na mesma máquina, ou que fazem o acompanhamento live via remote desktop no próprio servidor de alguns queries para perceber onde podem aumentar a performance, quase que se houve o scott hanselmann a engolir em seco.

É de facto dificil perceber ás vezes onde está a linha entre o pragmatismo e a irresponsabilidade quando se ignoram assim tantas normas que têm como objectivo mitigar potenciais questões de segurança ou outras como escalabilidade.

Num nível parecido lembro-me que faz agora um ano em que também tive o meu momento quase ridiculo no que diz respeito a desenvolvimento de software e infraestruturas de IT. Foi um projecto que me deu muito gozo porque me permitiu voltar ao desenvolvimento web que já não fazia há uns anos, mas também deu muito trabalho (basicamente uma semana de 7diasx12horas) porque era bastante ambicioso nos objectivos e nas tecnologias utilizadas.

Conseguimos ter uma aplicação suficiente para entrar em produção, mas a verdade é que de um momento para o outro começamos a ter imensos problemas, basicamente imensos acessos ao site tinham como resposta um timeout e o pior era quando a meio da utilização do mesmo, de um momento para o outro, ele dava erro. Depois de muitas tentativas para demonstrar que o problema não era da aplicação nem do servidor conseguimos convencer os responsaveis pela infraestrutura que a questão era mesmo a largura de banda disponivel que não era suficiente. Assumida essa questão passou-se para o ponto seguinte, como aumentar essa largura de banda. Como não era possível em tempo útil disponibilizar a largura de banda necessária já que os prazos para isso não se coadunavam com o previsivel (e depois confirmado) pico de utilização do site a decisão foi mesmo levar mudar o servidor de instalações fisicas.

Claro que podíamos ter decido pela instalação de todo o software (IIS, SQL, aplicação e outras configurações) numa máquina já instalada nas novas instalações mas isso ía fazer subir o potencial de novos problemas, daí que tivessemos optado por levar mesmo o servidor para o novo local. Não consigo deixar de sorrir quando penso no técnico que tratou disso, e que eu acompanhei, a desligar o servidor (um blade da dell) retira-lo do rack, pô-lo (com alguma dificuldade) debaixo do braço e levar o servidor até à mala do seu carro, onde “com muito cuidado” o pousou, depois disso foram uns 15/30 minutos pela cidade do porto até ao novo local onde se repetiu a mesma história em modo inverso… tira o servidor da mala, vamos até ao edificio, carrega no botão do elevador… espera…espera… subimos para o andar da sala de servidores… espera…espera… eh pah isto é pesado… deixa pousar o servidor aqui neste sofazito enquanto não nos abrem a porta da sala dos servidores e lá ficou esse servidor que depois serviu (e bem) durante cerca de um mês uma população de cerca de quatro mil utiizadores.

Mas é mesmo caso para dizer e pur si muove porque a verdade

Share

geeky stuff: como sublinhar algumas palavras numa página

Uma funcionalidade muito interessante que o GMail tem é, quando fazemos uma pesquisa, sublinhar depois no mail a palavra que procuramos. Em principio isto deverá permitir que o utilizar direccione mais rapidamente a sua atenção para o pedaço de texto que realmente interessa.

(rever os artigos “F-Shaped Pattern For Reading Web Content” e “Top Ten Mistakes in Web Design” de Jakob Nielsen)

Como a aplicação que estou a desenvolver é, a nivel de conceito pelo menos, um motor de pesquisa achei que fazia todo o sentido usar algo do género. O meu cenário é o seguinte, tenho uma página onde vou pondo uns filtros e a dado momento quero ver o resultado da pesquisa por esses filtros. Na página que mostra esse resultado estou quero então, de alguma forma, destacar as palavras que usei como filtro.

Como estou a usar intensamente o jQuery (que como já disseram “Puts the Fun Back into Browser Scripting“) procurei um plugin que já fizesse isso. Encontrei alguns e estou a usar o “Higlighting jQuery plugin” que faz quase tudo o que queria.

Tive que fazer 3 alterações:

  • o código original tinha um if inicial com código diferente para IE e para os outros browsers mas por alguma razão no IE a função não se limitava ao contexto definido na chamada da função (por exemplo um div) e fazia o highlight da palavra em todo o documento. Optei pela solução radical de comentar esse código e usar só a parte para os outros browsers mas que funciona também em IE;
  • o código original seleccionava todas as instâncias da string que nós indicassemos, por exemplo se dissesse para fazer o highlight de palavra “joão” essa função ia seleccionar quer a palavra joão quer todas as outras variações sobre essa palavra. como não era esse o meu objectivo acrescentei um novo parâmetro chamado “exactValue” para destacar só as palavras que fossem exactamente iguais à palavra indicada;
  • finalmente esta função obrigava à existência de uma css classe chamada highlight e tinha esse nome hard-coded. como não me dava muito jeito acrescentei um novo parâmetro opcional com o nome da classe que queremos usar.

O código final que estou a utilizar e que enviei ao autor original é o seguinte:

/*

highlight v2

Highlights arbitrary terms.



MIT license.

Johann Burkard



*/

$(function() {
/* jQuery.highlight = document.body.createTextRange ? */
jQuery.highlight =
/*
 (Complicated) version for Mozilla and Opera using span tags.
*/
  function(node, te, exactValue, className) {
   var pos, skip, spannode, middlebit, endbit, middleclone;
   if (className == undefined) className = 'highlight';
   if (exactValue == undefined) exactValue = false;
   skip = 0;
   if (node.nodeType == 3) {
    pos = node.data.toUpperCase().indexOf(te.toUpperCase());
    if ((pos >= 0) &&
		(exactValue ?  $.trim(node.data.toUpperCase()) == $.trim(te.toUpperCase()) : true) ) {
     spannode = document.createElement('span');
     spannode.className = className;
     middlebit = node.splitText(pos);
     endbit = middlebit.splitText(te.length);
     middleclone = middlebit.cloneNode(true);
     spannode.appendChild(middleclone);
     middlebit.parentNode.replaceChild(spannode, middlebit);
     skip = 1;
    }
   }
   else if (node.nodeType == 1 && node.childNodes && !/(script|style)/i.test(node.tagName)) {
    for (var i = 0; i < node.childNodes.length; ++i) {
     i += $.highlight(node.childNodes[i], te, exactValue, className);
    }
   }
   return skip;
  }

 ;
});

jQuery.fn.removeHighlight = function(className) {
 if (className == undefined) className = 'highlight';
 this.find("span." + className).each(function() {
  with (this.parentNode) {
   replaceChild(this.firstChild, this);
   normalize();
  }
 });
 return this;
};

Share

geeky stuff: confirmar que o utilizador gravou as alterações

Até há uns tempos atrás o principal mecanismo de feedback numa aplicação web era o próprio recarregar da página quando fazíamos alguma coisa, fosse clicar num link, carregar num botão de um form, seleccionar uma opção numa combobox… praticamente qualquer interacção que provocasse previsivelmente uma resposta iria levar a que o servidor nos brindasse com esse compasso de espera que não era mais do que ir até ao servidor buscar a resposta que tinha sido pedida.

Numa aplicação AJAX em que queremos limitar o número de visitas até ao servidor, ou pelo menos restringir esses pedidos à informação necessária e suficiente para responder ao pedido (em vez de mandar sempre a página toda) esse feedback “natural” pura e simplesmente desaparece… é o exemplo perfeito de como uma nova tecnologia em vez de levar à redução de trabalho leva ao (re)aparecimente de outros problemas que se pensavam resolvidos.

No meu caso concreto estou neste momento a desenvolver uma aplicação que mistura algumas interacções em modo tradicional (click => reload page) com outras em modo ajax (click => reload area) pelo que é aceitavel que o utilizador não saiba ao certo que tipo de feedback vai ter… será isto um code-smell?, ou melhor UI-smell… no caso concreto acho que não mas tem uma implicação. Tenho que ser extra-cuidadoso no feedback que dou ao utilizador.

Este exemplo tem a ver com o preenchimento de forms. Como garantir que o utilizador é avisado quando sai de um form sem o ter gravado?

Em “Using JQuery to Warn Users About Losing Data When Navigating Away from Form” encontrei a solução que me parece resolver este problema.

$(document).ready(function(){
  $(':input').bind("change", function() { setConfirmUnload(true); }); // Prevent accidental navigation away

  $("form").submit(function() {
    setConfirmUnload(false);
  });

});

function setConfirmUnload(on) {
  window.onbeforeunload = (on) ? unloadMessage : null;
}

function unloadMessage() {
  return 'You have entered new data on this page.  If you navigate away from this page without first saving your data, the changes will be lost.';
}

Share

geeky stuff: autocomplete

Um dos plugins de jQuery que estou a utilizar actualmente é o autocomplete. O objectivo deste plugin é ir dando às pessoas, enquanto elas escrevem numa caixa de texto, uma lista com conteúdos apropriados. A ideia pode ser parecida com a da combobox que indica logo à partida o universo das opções disponiveis mas, no autocomplete, numa perspectiva mais genérica na medida em que as opções que vamos mostrando às pessoas são definidas tendo por base aquilo que estamos a escrever.

Como praticamente todas as funções do jQuery a utilização deste plugin é muito simples. Definimos um contexto para utilização (tipicamente uma textobox) e associamos a função autocomplete com meia dúzia de opções, aqueles que mais utilizei foram:

  • multiple: [false | true] => Whether to allow more than one autocompleted-value to enter.
  • matchContains: [false | true ] => Whether or not the comparison looks inside (i.e. does “ba” match “foo bar”) the search results.
  • formatItem: função => Provides advanced markup for an item.
  • formatResult: função => Similar to formatItem, but provides the formatting for the value to be put into the input field
  • result: função => Is executed when the user selects a value

A receita básica que usei foi a seguinte:

	function formatItem_entidade(row) {
		return row[0] + " (id: " + row[1] + ")";
	}
	function formatResult_entidade(row) {
		return row[0].replace(/(< .+?>)/gi, '');
	}

	$("#MyMatrix_ctl13_txtEntidade").autocomplete(GetAjaxServer() + '&Operation=GetSugestoesKeywordsExperienciasEntidades', {
		width: 300,
		multiple: false,
		matchContains: false,
		formatItem: formatItem_entidade,
		formatResult: formatResult_entidade
	});

Share

geeky stuff: repeaters

A programação declarativa é uma das modas actuais mas como ainda não estou a usar WPF e as (supostas) maravilhas do XAML limito-me a aproveitar os controles de ASP.Net que já existem desde a versão 1.0.

Um dos controles que mais gosto é o repeater pela facilidade com que multiplicamos texto e pelo facto de já incluir alguma flexibilidade no que diz respeito aquelas situações habituais quando estamos numa de multiplicar texto nomedamente: ItemTemplate, AlternatingItemTemplate, FooterTemplate, HeaderTemplate e SeparatorTemplate.

Mesmo assim por vezes surgem situações que levam a que seja necessário um pouco de programação para conseguirmos acomodar os templates às nossas necessidades. Estou a pensar em dois cenários concretos: nested repeaters e repeaters sem valores.

O caso dos nested repeaters é aquele em que por exemplo queremos representar uma relação hierarquica. Temos n registos, mas dentro que cada um desses registos ainda temos mais y que queremos apresentar. A resolução do problema tem 2 partes, uma que diz respeito ao acesso a dados, a outra que diz respeito à atribuição do subconjunto de informação especifico que queremos mostrar no repeater de nivel inferior.

Em relação ao acesso a dados, podemos simplesmente ir buscar os dados da tabela mãe e depois por cada linha dessa tabela ir buscar os outros dados, por exemplo:

    Private Sub InitRptAgrupamentos()

        With rptAgrupamentos
            .DataSource = _competenciasEngine.GetBundles()
            .DataBind()
        End With

    End Sub

    Private Sub rptAgrupamentos_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptAgrupamentos.ItemDataBound

        Dim item As RepeaterItem = e.Item
        Dim rpt As Repeater = CType(item.FindControl("rptCompetencias"), Repeater)
        Dim agrupamento As String = CType(item.DataItem, DataRowView).Item("Name").ToString()

        With rpt
            .DataSource = _competenciasEngine.GetTags(agrupamento)
            .DataBind()
        End With

    End Sub

ou então trazer as 2 tabelas de uma só vez e depois “dizer” qual a relação que tem que ser usada para ir buscar o subconjunto correcto de dados. algo como:


        ds.Tables.Add(dtUtilizadores)
        ds.Tables.Add(dtExperiencias)
        ds.Relations.Add(New DataRelation("Utilizadores_Experiencias", dtUtilizadores.Columns("UserId"), dtExperiencias.Columns("UserId")))

    Private Sub rptUtilizadores_ItemDataBound(ByVal sender As Object, ByVal e As System.Web.UI.WebControls.RepeaterItemEventArgs) Handles rptUtilizadores.ItemDataBound

        Dim item As RepeaterItem = e.Item
        Dim rptExperiencias As Repeater = CType(item.FindControl("rptExperiencias"), Repeater)

        With rptExperiencias
            .DataSource = CType(item.DataItem, DataRowView).CreateChildView("Utilizadores_Experiencias")
            .DataBind()
        End With
    End Sub

mais informação em: Nesting Repeaters with Hierarchical Data and Server-Binding

A outra situação de não processar o repeater quando não há dados só é relevante se tivermos a usar o HeaderTemplate ou FooterTemplate já que os outros só são processados se realmente houver dados.
Nesse caso a solução passa por verificar antes de o controlo ser renderizado se realmente há dados.

    Protected Sub rptExperiencias_PreRender(ByVal sender As Object, ByVal e As System.EventArgs)

        If CType(sender, Repeater).Items.Count = 0 Then CType(sender, Repeater).Visible = False

    End Sub

Share

Problemas básico CSLA

Dataportal.Create failed
You need to override the DataPortal_Create method.

Overriding AddNewCore
When you override AddNewCore, is anyone raising the AddingNew event?
In your collection class, are you overriding AddNewCore?  It should look similar to this:

Problem get Datagrid to work
In your collection class, are you overriding AddNewCore?  It should look similar to this:
protected override AddNewCore() {
TimeCardEntry entry;

entry = TimeCardEntry.NewTimeCardEntry();
OnAddingNew( new AddingNewEventArgs( entry ) );

Add( entry );

return entry;
}

Address Line Disappearing on Datagrid
I found the problem.  Because my ID for my object is the Primary Key from a table in Ms SQL2005, I didn’t put a value in for the Object Id.  So every new object had an Id of zero (0).  So when ever you were on a new line and you went into edit mode and cancelled it, the code would remove every object with an Id of zero because that Id matched the object that was created for the line that you started editing.

Share

Gestão de Projecto

Parkinson’s Law states that “work expands so as to fill the time available for its completion.”
“Managers know that software development follows Parkinson’s Law: Work will expand to fill the time allote to it. (…) corollary to Parkinson called the Ninety-Ninety Rule, atributed to Tom Cargil of Bell Labs: ” The first 90% of the code acount for the first 90% of development time. The remaining 10% of the code accounts for the other 90% of the development time.”

Hofstadter’s law
Hofstadter’s Law states that: It always takes longer than you expect, even when you take into account Hofstadter’s Law.
A (somewhat joking) rule of thumb introduced by Hofstadter for figuring out an accurate time is to double the number and step up to the next higher units. For example, a job estimated at 1 hour can be accomplished in 2 days, while a 3-month project will take you 6 years.

Share