sábado, 19 de novembro de 2011

Decorators em Python: o que a Annotation gostaria de ser quando crescer

 Olá Pessoal,

 Devido à correria, faz muito tempo que não atualizo o blog. Atualmente tenho aprendido bastante sobre a linguagem Python e tenho gostado muito.

 Durante o desenvolvimento do Revelação Virtual,  desenvolvi um framework para facilitar a minha vida na infra do Google App Engine. Logo no início, comecei a pensar como seria um bom modelo de segurança. Já tive contato com Spring Security em minha época de Javeiro.

 Contudo, buscava uma solução sucinta, sem depender de frameworks e suas documentações. Por  não ter  paciência de ler, fico muito ansioso por ver as coisas funcionar. Se houver mais que uma página de documentação para ler antes de ver a um exemplo concreto funcionando, fico frustrado. Justo nessa época um amigo me falou sobre os decorators de Python (não confundir com o padrão Decorator, veja esse artigo sobre o assunto).

Programação Funcional


 Para utilizar os decorators, você precisa conhecer um conceito simples de programação funcional: função é uma variável. Para quem conhece o conceito, isso pode parecer simples, mas para quem não está acostumado, como era meu caso quando só conhecia Java, função ser uma variável pode soar bem estranho.

 Para clarificar:

def funcao(arg):
  print "Sou uma funcao e meu argumento eh: %s"%arg
 
variavel=funcao

variavel("argumento")

print type(variavel)

Na primeira linha é criada uma função em Python. Contudo, o que realmente acontece é a criação de um Objeto do tipo função cujo identificador é "funcao". Tanto isso é verdade que após a implementação do corpo, o objeto função é é atribuído à "variavel".

 Depois dessa atribuição, a função é executada através da referência "variavel". Para finalizar, o programa imprime o tipo de "variavel": function. Esse é apenas um exemplo simples, mas suficiente, para entender as próximas explicações. Se quiser saber mais, leia a documentação do próprio Python.

Segurança


Depois de entender que função é variável, é possível expandir os horizontes e utilizar funções de uma nova maneira. Com esse "novo" conceito, agora você pode passar funções como argumento e retornar funções como resultado de uma função. Suponha então que você queira executar uma função somente se for passado um valor positivo como argumento único, sem saber qual é essa funcao a princípio. O código poderia ser algo como:

def permitir_valor_positivo(regra_negocio):
  def f(valor):
    if valor>0:
      return regra_negocio(valor)
    return "Somente valor positivo permitido"
  f.__name__=regra_negocio.__name__
  return f


Vamos entender o código:
  1. É criada uma função permitir_valor_positivo que ira receber uma outra como argumento
  2. É criada uma função f que executa a função regra_de_negocio, passada como argumento, somente se o valor passado para f como argumento for positivo. Caso contrário, imprime uma mensagem de erro.
  3. O nome da função é alterado de "f" para o nome da função "regra_de_negocio"
  4. f é retornada como resultado  de permitir_valor_positivo

 Construida nossa funcao , podemos agora apenas ter a preocupação de escrever a "regra de negócio", e depois aplicar a "segurança":


def deposito(valor):
  return "Deposito no valor de: %s"%valor
 
deposito=permitir_valor_positivo(deposito)

print deposito(1)
print deposito(0)


 Nossa regra de negócio é realizar um depósito em conta. Depois de implementado o negócio, podemos fazer sua segurança utilizando nossa função permitir_valor_positivo. Depois de aplicada a segurança, tentamos fazer depósitos com valores 1 e 0 recebendo como resposta, respectivamente, "Deposito no valor de: 1" e "Somente valor positivo permitido".

Decorator


Depois de entendido o conceito acima, poderia-se utilizar a programação funcional para fazer nosso lógica de segurança em pseudo-código:

seguranca(negócio):
     se (cumpre requisitos de segurança):
            execute negocio
     se não:
            Exiba mensagem de erro

Com a função de segurança escrita, bastaria utilizar o esquema da seção anterior para "segurar" os métodos de negócio. Contudo, o Python apresenta uma açúcar sintático bem interessante chamado Decorator. Talvez inspirado pela notação de Annotation em de Java, podemos substituir as linhas abaixo:


def deposito(valor):
  return "Deposito no valor de: %s"%valor

deposito=permitir_valor_positivo(deposito)


por apenas:


@permitir_valor_positivo
def deposito(valor):
  return "Deposito no valor de: %s"%valor


Dessa maneira, o código fica menor e muito mais elegante atingindo alguns objetivos que busco em frameworks:

  1. Documentação curta e simples (esse blog apenas)
  2. Sem necessidade de configuração, apenas conhecimento da própria linguagem
  3. Lógica de Segurança separada da Lógica de Negócio
  4. Granular ao nível de função


Conclusões


Para fazer algo análogo em Java, seria necessário criar uma Annotation personalizada, configurar algum tipo de processamento (como o Spring no Bean Processor) e aplicar a API de Reflection. Como trabalhei justamente com Annotation e Reflection no projeto de meu TCC (JColtrane) sei que esse processo seria muito mais trabalhoso e bem menos elegante que a solução Python, além de obrigar o usuário a ter mais uma dependência em seu projeto. Como já ouvi inúmeras vezes: "para quem só conhece martelo, tudo é prego" ;)

 Se você gostou do exemplo e quer saber mais sobre Python, confira o  curso Python Pro.

 Abs e até o próximo post,
 Renzo Nuccitelli

PS:Gostaria de explicar o título do post. Não gostos de flames, cada um tem o direito de preferir trabalhar com a linguagem que quiser. Contudo, um ponto positivo do flame é gerar discussão e consequente tráfego no blog ;)

sexta-feira, 4 de março de 2011

Upload e Download de dados no Google App Engine - GAE

Olá para todos,

 Dando continuidade aos meus estudos de Python (Webapp-CE), visando um melhor uso do GAE, estudei uma funcionalidade muito bacana nessa semana: o Bulk Loader, que pelo tradutor do Google significa Carregador de Alto Volume.

 Ele serve para fazer upload de dados para o Banco de Dados (BD) no GAE e para fazer download de dados do mesmo. Então algumas tarefas interessantes podem ser feitas com ele:

  •  Backup de dados
  • Carregamento de cargas estáticas no servidor
  • Update de dados no servidor
  • Migrar dados de uma app para outra
Os dados podem ser baixados e carregados em dois formatos básicas: CSV e XML.

 Depois dessa introdução vou mostrar um exemplo na prática.

 Digamos que queremos fazer uma aplicação estados brasileiros. Vamos aos passos:

 1) Criar o modelo para a entidade de Estado (State) :


class State(db.Model):
    name = db.StringProperty(required = True)
    acronym = db.StringProperty(required = True)
    country = db.ReferenceProperty(reference_class = Country, required = True)


2) Configurar o arquvo bulkloader.yaml:

Esse arquivo configura as maneira como será feito o upload/dowload. Na documentação existe uma maneira de criar o formato básico do arquivo, mas já que vc está lendo esse post, pode copiar daqui mesmo = ). A parte inicial do arquivo contém os imports necessários para as transferências. Na documentação é ensinado como organizar imports de módulos de terceiros, caso vc deseja fazer algo mais rebuscado. Mas na maioria dos casos, o início não será alterado.

 Observe a parte final do arquivo, a partir do item "-transformers". Nessa seção que a configuração dos dados referentes ao State é feita. Primeiramente se indica o tipo do dado (kind=State). Em seguida, é configurado o tipo de arquivo (connector=csv). Portanto escolhi o formato de valores separados por vírgula, mas poderia ser XML, bastando colocar o valor simplexml.

 Depois são configuradas as propriedades do modelo. No caso, configurei 3 propriedades: a chave (key), o nome (name) e a referência para um país (country). Cada propriedade possui algumas opções de configurações.

 O item property contém o nome da propriedade em seu modelo.

 O item external_name indica o nome da propriedade no CSV. Esses nomes constarão como cabeçalhos na primeira linha do CSV

 O item external_transform indica uma transformaçao que deve ser feita no dado quando for feito o download. No caso, utilizei apenas o transform.key_id_or_name_as_string que transforma a chave (key) do modelo em String.

 O item internal_transform indica uma transformação que deve ser feita no dado quando for feito o upload. No exemplo, utilizei apenas o transform.create_foreign_key('Country',True) que serve para que seja criada uma referência para o tipo País (Country) e que se use o valor número para a chave (parâmetro com valor True.


python_preamble:
- import: base64
- import: re
- import: google.appengine.ext.bulkload.transform
- import: google.appengine.ext.bulkload.bulkloader_wizard
- import: google.appengine.ext.db
- import: google.appengine.api.datastore
- import: google.appengine.api.users

transformers:
- kind: State
  connector: csv
  connector_options:
    # TODO: Add connector options here--these are specific to each connector.
  property_map:
    - property: __key__
      external_name: key
      export_transform: transform.key_id_or_name_as_string
   
    - property: name
      external_name: name
     
    - property: country
      external_name: country
      export_transform: transform.key_id_or_name_as_string
      import_transform: transform.create_foreign_key('Country',True)



3) Montando dados a serem carregados:

 Para nosso exemplo, digamos que vc já tenha salvado o País Brasil em seu BD no GAE e que ele possua um id=1. Então construi um CSV (state.csv) com alguns estados brasileiros:


country,name
1,Acre
1,Piauí
1,Santa Catarina


4) Carregando os dados:

 Importante: o Big Table, BD do GAE, não possui esquema. Por essa razão, ele só conhece o "esquema" de seus modelos após vc ter salvado ao menos um item nele. Portanto, antes de carregar dados, certifique-se de salvar um dado do tipo desejado antes de tentar fazer o upload de dados.

Acresente em seu app.yaml a seguinte configuração:


builtins:
- remote_api: on

 Faça o deploy da app. Feito isso, execute o comando, onde "sua-app": é o id da sua aplicação:

appcfg.py upload_data --config_file=bulkloader.yaml --filename=state.csv --kind=State --url=http://sua-app.appspot.com/_ah/remote_api

Vc verá mensagens dos dados sendo carregados. Após o termino do carregamento, entre no console de administração de sua app para conferir os novos dados em sua aplicação.

5) Dowload dos dados:

 Experiemente salvar novos estados em sua aplicação repetindo os passos anteriores. Depois disso, vamos fazer o dowload de todos esses dados. Execute o comando:


appcfg.py dowload_data --config_file=bulkloader.yaml --filename=state_dump.csv --kind=State --url=http://sua-app.appspot.com/_ah/remote_api

 Esse comando irá baixar seus dados para o arquivo state_dump.csv. No meu caso, eu salvei todos os estados brasileiros em minha app, e o conteúdo baixado foi:

country,name,key
1,Acre,1001
1,Piauí,1002
1,Santa Catarina,1003
1,Alagoas,2001
1,Bahia,2002
1,Amapá,3001
1,Ceará,3002
1,Maranhão,3003
1,Rio de Janeiro,3004
1,São Paulo,3005
1,Amazonas,4001
1,Espírito Santo,4002
1,Rio Grande do Norte,4003
1,Sergipe,4004
1,Distrito Federal,5001
1,Paraíba,5002
1,Pernambuco,5003
1,Roraima,5004
1,Goiás,6001
1,Minas Gerais,6002
1,Rondônia,6003
1,Mato Grosso,7001
1,Rio Grande do Sul,7002
1,Tocantins,7003
1,Mato Grosso do Sul,8001
1,Pará,8002
1,Paraná,8003


Dessa maneira, espero ter conseguido exemplificar como fazer transferências de dados de seu computador para o GAE e vice-versa. Para conferir a documentação completa da funcionalidade, confira na Documentação.

 Deixe um comentário ou crítica se achou esse post util.

 Abs
 Renzo Nuccitelli

quinta-feira, 24 de fevereiro de 2011

Desrespeito do Ponto Frio com o consumidor

 Apesar de o blog ser voltado para a área de tecnologia, empreendedorismo também é uma característica marcante. Para que um negócio de certo, respeito ao consumidor/cliente é fundamental.

 Então darei um contra exemplo, citando o mau atendimento do Ponto Frio:

Infelizmente cai na besteira de efetuar uma compra no Ponto Frio e só depois vi esse site: http://www.guiadosac.com.br/guia-do-sac/ponto-frio/base_view#1298558355

 Irei ao Procon para fazer valer meus direitos, mas segue a história.

Atendimento online realizado no Ponto Frio em 24/02/2011 às 11:25

Atendente Ponto Frio: Olá Renzo ! Bem-vindo ao atendimento online Pontofrio. Em que posso ajudar?

Renzo: Estou ligando no telefone do SAC de vcs, e apesar de estarmos no horário de atendimento, ninguém atende
Renzo: Esse atendimento possui protocolo?

Atendente Ponto Frio: Somente se for consultado algum pedido.

Renzo: Entendi
Renzo: Bom
Renzo: Antes de ontem compareci na loja do Ponto Frio no Shopping Center Vale, de São José dos Campos/SP
Renzo: e efetuei a compra de uma geladeira no valor de R$ 2.000,00
Renzo: alias, R$ 2.700,00
Renzo: mandei buscar a geladeira ontem. A mesma se encontrava totalmente embalada. Ao abrir a embalagem, justamente por baixo do isopor, ela se encontrava danificada, com um aranhão profundo

Atendente Ponto Frio: Sim .

Renzo: Compareci a loja ontem mesmo, dado o prazo de 72 horas para efetuar a troca do produto, mas o gerente se recusou a fazer a troca

Atendente Ponto Frio: Sim porque se trata de uma avaria e a empresa não efetua a troca de produtos avariados.

Renzo: então estou entrando em contato com o SAC, para pedir uma solução. Caso não haja, vou procurar meus direitos junto ao Procon e Tribunal de Pequenas causas, além da propaganda ruim que farei da loja nos meios de imprensa e internet

Atendente Ponto Frio: Só trocamos por defeito técnico.

Renzo: Então a empresa vende produtos avariados e não efetua troca?

Atendente Ponto Frio: O produto e para ser verificado no ato da entrega.

Renzo:
Isso não foi avisado, além dela ter vindo embalada
Renzo: Engraçado que em qualquer outra loja, eles realizam a troca, principalmente se mandaram a peça avariada.

Atendente Ponto Frio: E na nota fiscal consta termo de conformidade informando que o produto foi entregue em perfeito estado.

Renzo: pois é, o gerente não entregou a nota do produto

Atendente Ponto Frio: Se for assinado não temos mais como troca.

Renzo: e se recusou a entregar a mesma depois que compareci a loja
Renzo: esta bem, poderia me fornecer um protocolo de atendimento, de forma que possa comparecer ao procon?
Renzo: Em minha nota fiscal consta um termo de vistoria, com um carimbo, com as opções sim e não

Atendente Ponto Frio: Só e gerado um protocolo se for aberto alguma reclamação mais como se trata de uma avaria não geramos reclamação.

Renzo: e o mesmo não está com nenhuma opção assinalada, e muito menos assinado
Renzo: mas eu estou reclamando de um produto que nem a vistoria foi assinada, quer dizer que nem um protocolo eu posso receber? Em qualquer empresa que ligo ao sac, a primeira coisa que me informam é o protocolo
Renzo: Se trata de uma avaria em cuja nota não consta a assinatura do produto ter sido vistoriado pelo cliente

Atendente Ponto Frio: .

Renzo: Existe uma Ouvidoria no Ponto Frio?

Atendente Ponto Frio: Sim mais só trata casos de imprensa.

Renzo: Poderia me passar o telefone?

Atendente Ponto Frio: A ouvidoria que efetua contato com o cliente mais e como eu informei antes eles só tratam casos de imprensa.

Renzo: Não existe um telefone para contato com o Cliente? Então vai ser a primeira empresa que vejo com esse procedimento, totalmente em desacordo com o Código do Consumidor. Não fornecem protocolo de atendimento no SAC e não possui telefone de contato com uma Ouvidoria. Desse jeito não vai ser difícil conseguir colocar isso na imprensa e eles entrarem em contato comigo

Atendente Ponto Frio: E como informado até após não trocamos por avaria se o cliente retira o produto da loja a responsabilidade passa a ser do
mesmo.

Renzo: Estou com a nota que consta o campo de vistoria do produto que não está assinado. Basicamente vc não tem protocolo para me dar desse atendimento, não tem o telefone da Ouvidoria para me informar e está dizendo que o problema é meu, correto?

Houve um erro de conexão.
Por favor, reconecte-se para efetuar um novo atendimento. 

 Enfim, fica a dica para evitaram entrar numa fria comprando no Ponto Frio.

quarta-feira, 2 de fevereiro de 2011

Webapp-CE: Mais um framework saindo do forno

 Bom dia!

 Estava meio sumido do Blog, mas a Nuccitec tem consumido praticamente todo meu tempo :).
 O fato é que agora estou me aprofundando para aprender Python, influenciado pelo Google App Engine(GAE), infra da qual gosto muito. E para aprender, nada melhor que logo fazer framework...rs.
 Quando você lê os primeiro passos no GAE voltado para o Python, acaba se decepcionando com o webapp, pequeno framework web que o Google fez. A necessidade de ficar mapeando URL´s para handlers é um saco. Inclusive na própria documentação se comenta pra usar outros frameworks.
 Conversando sobre isso com meu amigo Reginaldo, ele me deu a idéia de fazer um REST seguindo uma convenção para executar os handlers, eliminando a tarefa braçal, chata, e suscetível a erros de ficar mapeando URL´s para Handlers. Assim nasceu o Webapp-CE, ou Webapp Convetion Engine.
 A idéia básica da convenção é parecida com existente no VRaptor, framework Java para WEB, que também é brasileiro. Suponha que você tem um handler br.com.SeuHandler que possua o método executar. A convenção consiste em mapear automaticamente esse método para a URL /br/com/SeuHandler/executar. E o que vir depois desse padrão é passado como parâmetro para o método executar.
 O framework também faz o inverso, ou seja, você passa o método e ele te devolve a URL. Isso torna o processo de mapeamento automático e se você mudar o nome do Handler ou até mesmo movê-lo para outro diretório, a URL muda de forma automática.
 O legal de tudo isso foi ter feito o framework em duas madrugadas. Ele possui apenas dois scripts. Mas no fonte do projeto existem também os testes unitários.
 A experiência serviu para me indicar que efetivamente o Python te da uma liberdade imensa e é uma tremenda ferramenta. A introspecção dele é excelente, bem como a parte de testes unitários. Cada vez mais tenho menos saudades do Java...rs. Em breve devo lançar o curso Python Básico e Python para GAE na Nuccitec.

 Espero que curtam o projeto.
 Renzo Nuccitelli