Posts com o tag ‘linguagens de programação’

7abr2010

Utilizando debug em aplicações Rails

(0) comentários

Muita gente acha (assim como eu achava há um tempo atrás) que não é possível utilizar debug (ou “depurar”, ou “debugar”) uma aplicação Ruby on Rails. Acredito que isso se deve ao fato de Ruby ser uma linguagem interpretada, diferente de outras linguagens que requerem compilação.

Existe uma forma muito simples de se “debugar” em Rails. Primeiro vamos instalar a gem ruby-debug.

$ sudo gem install ruby-debug

Agora vamos criar uma aplicação Rails de exemplo para utilizar o debug.

$ rails prodis-debug
$ cd prodis-debug
$ rails script/generate scaffold MyModel my_text:string my_value:float
$ rake db:migrate

E ao invés de subir a aplicação com script/server, iniciamos a aplicação em modo debug:

$ rdebug script/server
/Users/Prodis/Blog/code/prodis-debug/script/server:2
require File.expand_path('../../config/boot',  __FILE__)
(rdb:1)

Esse é o momento de adicionarmos o(s) breakpoint(s) desejados, informando o arquivo e a linha do mesmo. Vamos colocar um breakpoint na ação create de MyModelsController:

(rdb:1) break app/controllers/my_models_controller.rb:43
Breakpoint 1 file /Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb, line 43

E usamos o comando cont para prosseguir com o carregamento da aplicação no servidor.

(rdb:1) cont
=> Booting Mongrel
=> Rails 2.3.5 application starting on http://0.0.0.0:3000
=> Call with -d to detach
=> Ctrl-C to shutdown server

No browser, através do endereço http://localhost:3000/my_models/new, vamos criar um novo registro de MyModel.

Quando clicarmos no botão “Create”, a aplicação irá parar no breakpoint que colocamos na linha 43 de MyModelsController.

Breakpoint 1 at /Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb:43
/Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb:43
@my_model = MyModel.new(params[:my_model])
(rdb:6) 

A partir daqui podemos investigar o código e ter acesso as suas variáveis. Por exemplo, exibindo os valores da variável params:

(rdb:6) p params
{"my_model"=>{"my_text"=>"I am some text", "my_value"=>"50.00"},
"commit"=>"Create", "authenticity_token"=>"qN9mUhJelZD0V/P2CB1fhBkQrIxFvvk+NRkY5m6EFWg=",
"action"=>"create", "controller"=>"my_models"}

Mas a maneira mais fácil de inspecionar variáveis é pelo irb, que irá se manter no contexto atual onde a aplicação está parada.

(rdb:6) irb
irb(#):001:0> params
=> {"my_model"=>{"my_text"=>"I am some text", "my_value"=>"50.00"},
"commit"=>"Create", "authenticity_token"=>"qN9mUhJelZD0V/P2CB1fhBkQrIxFvvk+NRkY5m6EFWg=",
"action"=>"create", "controller"=>"my_models"}
irb(#):002:0>

Saindo do irb (com o comando exit), nós voltamos para o ambiente de debug. Há uma série de comandos úteis para se utilizar. Um deles é o list, que exibe o trecho de código onde a aplicação está parada e indicando sua linha exata através de uma seta:

(rdb:6) list
[38, 47] in /Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb
38    end
39
40    # POST /my_models
41    # POST /my_models.xml
42    def create
=> 43      @my_model = MyModel.new(params[:my_model])
44
45      respond_to do |format|
46        if @my_model.save
47          flash[:notice] = 'MyModel was successfully created.'
(rdb:6)

Para avançar até a próxima linha, usamos o comando next:

(rdb:6) next
/Users/Prodis/Blog/code/prodis-debug/app/controllers/my_models_controller.rb:45
respond_to do |format|
(rdb:6)

Para listar todos os comandos disponíveis, basta entrarmos no help:

(rdb:6) help
ruby-debug help v0.10.3
Type 'help ' for help on a specific command
Available commands:
backtrace  delete   enable  help    next  quit     show    trace
break      disable  eval    info    p     reload   source  undisplay
catch      display  exit    irb     pp    restart  step    up
condition  down     finish  list    ps    save     thread  var
continue   edit     frame   method  putl  set      tmate   where 
(rdb:6)

Também podemos visualizar a ajuda de um comando específico:

(rdb:6) help next
n[ext][+-]?[ nnn]	step over once or nnn times,
'+' forces to move to another line.
'-' is the opposite of '+' and disables the force_stepping setting.
(rdb:6) 

E para continuar a execução da aplicação, usamos o comando continue (ou um dos seus álias c ou cont)

(rdb:6) continue
Processing MyModelsController#create (for 127.0.0.1 at 2010-03-20 12:20:54) [POST]
Parameters: {"my_model"=>{"my_text"=>"I am some text", "my_value"=>"50.00"},
"commit"=>"Create", "authenticity_token"=>"qN9mUhJelZD0V/P2CB1fhBkQrIxFvvk+NRkY5m6EFWg="}
MyModel Create (0.5ms)   INSERT INTO "my_models" ("my_text", "created_at", "updated_at", "my_value")
VALUES('I am some text', '2010-03-20 15:45:19', '2010-03-20 15:45:19', 50.0)
Redirected to http://localhost:3000/my_models/4
Completed in 1464995ms (DB: 1) | 302 Found [http://localhost/my_models]

Existe também uma outra maneira de se “debugar” com a gem ruby-debug. Nós podemos colocar uma instrução debugger (que na verdade é um método) em qualquer lugar do código para marcar como breakpoint. O Railscast #54 Debugging with ruby-debug mostra os passos de como fazer isso.

Eu particularmente gosto de configurar os breakpoints direto no Terminal, evitando assim ter que colocar qualquer informação de debug no código.

Esse esquema de debug não se restringe somente à aplicações Rails. Você pode muito bem utilizá-lo com código Ruby diretamente. Por exemplo, o código abaixo é do arquivo some_class.rb:

#lib/some_class.rb
class SomeClass
  def some_method
    # do something here
  end

  def other_method
    # do another thing here
  end
end

É só utilizar o comando rdebug passando o nome do arquivo:

$ rdebug lib/some_class.rb
/Users/Prodis/Blog/code/prodis-debug/lib/some_class.rb:2
class SomeClass
(rdb:1) 

Para informações mais detalhadas sobre a gem ruby-debug, veja a documentação completa.


Post original:
http://prodis.pro.br/2010/03/20/utilizando-debug-em-aplicacoes-rails

.

22mar2010

Métodos que retornam mais de um valor em Ruby

(0) comentários

Nas últimas semanas, a equipe que eu trabalho estava desenvolvendo um web service onde havia a necessidade de renderizar o retorno de uma lógica de negócio em representações XML. Digo representações (no plural), pois para um retorno com sucesso a representação seria uma e para retorno com erro a representação seria outra.

Por exemplo, um retorno com sucesso:

<natural-person>
   <name>Prodis</name>
   <cpf>01234567890</cpf>
</natural-person>

E um retorno sem sucesso:

<error>
   <description>CPF inválido.</description>
</error>

Como era um web service em REST, para sucesso retornamos o código de status HTTP “200 OK” e, dependendo do não sucesso da operação, o código de status HTTP da resposta poderia ser “400 Bad Request”, “404 Not Found” ou qualquer outro código 4xx ou 5xx que melhor se adequasse.

Mantendo um controller magro, a idéia era somente instanciar uma classe de negócio, chamar um método e renderizar o retorno. Algo como:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    natural_person = business.search_by_cpf params[:cpf]

    # TODO: Renderizar pessoa física ou mensagem de erro
  end
end

A partir daqui a equipe iniciou uma discussão sobre a melhor forma de se obter o(s) retorno(s) esperado(s). O controller precisava saber se a consulta havia sido feita com sucesso, para renderizar um objeto NaturalPerson retornando o código de status HTTP 200, ou se a consulta não tivesse sucesso, renderizar a mensagem de erro retornando um código de status HTTP 4xx adequado.

Como “bons programadores .NET e Java”, a primeira coisa que pensamos foi lançar uma exceção customizada caso a consulta não tivesse sucesso, capturar essa exceção no controller e, através das informações de descrição de erro e código de status HTTP contidas nessa exceção, renderizar o retorno adequado.

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new

    begin
      natural_person = business.search_by_cpf params[:cpf]

      render natural_person, 200
    rescue BusinessException => e
      render e.error, e.status_code
    end
  end
end

A gente não tinha visto muito código Ruby utilizando begin rescue, então essa solução não nos pareceu muito “Ruby way”. Achamos melhor pedir a opinião de alguém com mais experiência em Ruby. Perguntamos ao Rafael Rosa, que nos disse que cada vez que lançamos uma exceção em Ruby “alguma coisa ruim acontece no servidor” e consequentemente a aplicação ficará mais lenta.

Ele indicou um post falando a respeito:
http://www.simonecarletti.com/blog/2010/01/how-slow-are-ruby-exceptions

Rafael Rosa nos sugeriu retornar um array de duas posições: uma com o código de status HTTP e outra com o objeto a ser renderizado.

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    result = business.search_by_cpf params[:cpf]

    render result[1], result[0]
  end
end

Resolveu, mas o código não ficou muito intuitivo. A partir daí imaginamos algumas outras soluções.

Retornar um hash:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    result = business.search_by_cpf params[:cpf]

    render result[:data], result[:status_code]
  end
end

Criar uma classe de retorno:

class SomeBusinessResult
  attr_accessor :status_code, :data
end
class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    result = business.search_by_cpf params[:cpf]

    render result.data, result.status_code
  end
end

Posteriormente, o Rafael Rosa também sugeriu essa última opção, mas criando uma Struct ao invés de uma classe.

Então eu sugeri o método search_by_cpf retornar dois valores. Todos da equipe me perguntaram: “Como assim retornar dois valores?”. Falei que em Ruby um método pode retornar vários valores, que vi isso no livro The Ruby Programming Language.

O código do controller ficou bem mais intuitivo:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    status_code, data = business.search_by_cpf params[:cpf]

    render data, status_code
  end
end

O método search_by_cpf está retornando tanto o código de status HTTP quanto os dados para serem renderizados:

class SomeBusiness
  def search_by_cpf(cpf)
    # Lógica de negócio aqui
    return 200, natural_person
  end
end

Note que mesmo a linha 4 sendo a última linha de instrução do método, o retorno de mais de um valor obrigatoriamente precisa utilizar o comando return.

Quando há mais de um valor de retorno para um método, os valores são colocados implicitamente dentro de uma array e essa array fica sendo o único retorno do método.

O mesmo resultado seria obtido dessa forma:

class Business
  def search_by_cpf(cpf)
    # Lógica de negócio aqui
    [200, natural_person]
  end
end

Quem está consumindo um método que retorna mais de um valor, pode utilizar o recurso de atribuição paralela do Ruby para distribuir os valores de retorno em variáveis distintas, como é o caso no nosso controller:

class NaturalPersonController < ActiveRecord::Controller
  def index
    business = SomeBusiness.new
    status_code, data = business.search_by_cpf params[:cpf]

    render data, status_code
  end
end

O dinamismo do Ruby lhe oferece várias opções para você encontrar soluções para o mesmo problema ou questão. Cabe a você decidir qual melhor abordagem para seu tipo de problema. O interessante é você conhecer essas opções para facilitar a sua decisão.


Post original:
http://prodis.pro.br/2010/02/20/metodos-que-retornam-mais-de-um-valor-em-ruby

.

18dez2009

Serialização de objetos em JSON com Ruby on Rails

(0) comentários

Em um post anterior mostrei como serializar objetos em JSON utilizando .NET. Agora vamos fazer a mesma coisa com Ruby on Rails.

Vamos utilizar como exemplo uma classe de modelo chamada SomeFake:

class SomeFake < ActiveRecord::Base

end

Utilizando essa migration:

class CreateSomeFakes < ActiveRecord::Migration
  def self.up
    create_table :some_fakes do |t|
      t.string :text
      t.float :value
      t.timestamps
    end
  end

  def self.down
    drop_table :some_fakes
  end
end

No script/console vamos criar uma instância do modelo SomeFake com os seguintes dados:

>> fake = SomeFake.create :text => "I am a sample text.", :value => 150.85
=> #<SomeFake id: 1, text: "I am a sample text.", value: #<BigDecimal:18ac9f0,'0.15085E3',8(12)>,
created_at: "2009-12-13 19:43:28", updated_at: "2009-12-13 19:43:28">

Então queremos serializar a variável fake em JSON para obter o seguinte resultado:

{"id":1,"text":"I am a sample text.","value":150.85}

Para fazer isso, vamos chamar o método to_json na variável fake (estou usando o comando print para uma exibição melhor no console do JSON gerado):

>> print fake_json = fake.to_json
"{"some_fake": {"updated_at": "2009-12-13T19:43:28Z", "text": "I am a sample text.",
"id": 1, "value": 150.85, "created_at": "2009-12-13T19:43:28Z"}}"

O resultado que obtemos não é exatamente igual ao que estávamos querendo.

Primeiro, o nome do nosso modelo foi serializado como raiz do objeto em JSON. Isso aconteceu porque por padrão em uma aplicação Rails, a opção ActiveRecord::Base.include_root_in_json é configurada para true no arquivo config/initializers/new_rails_defaults.rb. Nós podemos alterar essa opção para false nesse arquivo, o que afeta a serialização em JSON de toda a aplicação, ou podemos alterá-lo no próprio script/console para nossos testes:

>> ActiveRecord::Base.include_root_in_json = false
=> false

Agora nosso objeto serializado fica assim:

>> print fake_json = fake.to_json
"{"updated_at": "2009-12-13T19:43:28Z", "text": "I am a sample text.", "id": 1,
"value": 150.85, "created_at": "2009-12-13T19:43:28Z"}"

A segunda diferença é que não queremos que os atributos de timestamps (created_at, updated_at) sejam serializados. Então vamos dizer para o método to_json não serializar esses atributos, utilizando a opção except:

>> print fake_json = fake.to_json(:except => [:created_at, :updated_at])
"{"text": "I am a sample text.", "id": 1, "value": 150.85}"

Para fazer o inverso, transformar dados em JSON para um objeto, criamos uma nova instância da classe SomeFake e chamamos o método from_json passando a variável fake_json como parâmetro:

>> other_fake = SomeFake.new
=> #<SomeFake id: nil, text: nil, value: nil, created_at: nil, updated_at: nil>
>> other_fake.from_json fake_json
=> #<SomeFake id: nil, text: "I am a sample text.", value: #<BigDecimal:1708a04,'0.15085E3',8(12)>,
created_at: nil, updated_at: nil>

Caso você precise serializar objetos em JSON sem os atributos timestamps com frequência, ao invés de sempre passar a opção except para o método to_json, podemos incluir um novo método na classe ActiveRecord::Base que faça a serialização sem esses atributos. Dessa forma, todos os nossos modelos terão essa funcionalidade.

Vamos chamar esse método de to_json_no_timestamps, o qual sua implementação é mostrada abaixo:

class ActiveRecord::Base
  def to_json_no_timestamps(options = {})
    timestamps_options = [:created_at, :updated_at]

    if (options.has_key? :except)
      if (options[:except].class == Array)
        timestamps_options = options[:except] | timestamps_options
      else
        timestamps_options < < options[:except].to_sym unless options[:except].nil?
      end
    end

    options[:except] = timestamps_options

    to_json options
  end
end

E então basta chamar nosso novo método em uma instância de qualquer modelo:

>> fake = SomeFake.first
=> #<SomeFake id: 1, text: "I am a sample text.", value: #<BigDecimal:1712798,'0.15085E3',8(12)>,
created_at: "2009-12-13 19:43:28", updated_at: "2009-12-13 19:43:28">
>> print fake_json = fake.to_json_no_timestamps
"{"text": "I am a sample text.", "id": 1, "value": 150.85}"


Post original:
http://prodis.pro.br/2009/12/13/serializacao-de-objetos-em-json-com-ruby-on-rails

.

15dez2009

Locaweb dedica dia ao Software Livre

(3) comentários

Aqui na Locaweb utilizamos alguns projetos de Software Livre para auxiliar na criação de nossos produtos. Além de utilizarmos esses softwares, tentamos sempre que possível contribuir para a evolução dos projetos.
2009-12-11 13.13.56
Pensando nisso, decidimos na última sexta-feira dedicar um dia inteiro de alguns desenvolvedores à contribuição ao Software Livre.

Desta vez, o projeto escolhido foi o Gitorious. O gitorious é um gerenciador de projetos git (uma versão livre do Github). Cerca de 12 desenvolvedores entraram em uma sala de reunião e seguiram uma série de tarefas pré-definidas em um backlog. Estas tarefas foram geradas a partir de problemas reais sentidos por clientes do gitorious (no caso os próprios desenvolvedores), além de sugestões de features que poderiam ser implementadas.
2009-12-11 13.17.43

Alguns exemplos de histórias implementadas:

  • Apresentar o conteúdo do arquivo README na página incial do projeto.
  • Bug-Fix: Authorized_Keys deve ser gerado com permissão certa.
  • Refatorações e melhoria na cobertura de testes

A empresa também patrocinou refrigerante e pizza para deixar o clima mais descontraído.
A14E22E7-E35C-414E-BC52-12542C1E519D
O resultado está publicado no próprio gitorious no branch do Fabio Hisamoto.

28out2009

Serialização de objetos em JSON com .NET

(1) comentário

Serializar e deserializar instâncias de objetos no formato JSON com .NET acaba sendo muito simples.

Vamos utilizar como exemplo a classe abaixo:

public class SomeFakeClass
{
    public int ID { get; set; }
    public string Text { get; set; }
    public decimal Value { get; set; }
}

E criamos uma instância dela:

SomeFakeClass fake = new SomeFakeClass
{
    ID = 123,
    Text = "I am a sample text.",
    Value = 150.85M
};

Então queremos serializar a variável fake em JSON para obter o seguinte resultado:

{"ID":123,"Text":"I am a sample text.","Value":150.85}

A classe DataContractJsonSerializer torna essa tarefa muito fácil. Basta escrever os dados serializados para um Stream usando o método WriteObject e depois recuperar uma string a partir desse Stream:

MemoryStream stream = new MemoryStream();
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(SomeFakeClass));
serializer.WriteObject(stream, obj);
string json = return Encoding.Default.GetString(stream.ToArray());

Para fazer o inverso, transformar dados em JSON para um objeto, usamos o método ReadObject passando o Stream que possui os dados serializados:

string json = "{"ID":123,"Text":"I am a sample text.","Value":150.85}";
MemoryStream stream = new MemoryStream(json);
DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(SomeFakeClass));
SomeFakeClass fake = (SomeFakeClass)serializer.ReadObject(stream);

Um detalhe importante é que a partir do .NET Framework 3.5 Service Pack 1 não há mais a necessidade de decorar a classe que queremos serializar com os atributos DataContract e DataMember, como o exemplo abaixo:

[DataContract]
public class SomeFakeClass
{
    [DataMember]
    public int ID { get; set; }

    [DataMember]
    public string Text { get; set; }

    [DataMember]
    public decimal Value { get; set; }
}

Isso somente se faz necessário se quisermos ter uma controle maior do que será serializado, como por exemplo, serializar uma propriedade com um nome diferente. Na classe abaixo, a propriedade Text é configurada para ser serializada com o nome Message:

[DataContract]
public class SomeFakeClass
{
    [DataMember]
    public int ID { get; set; }

    [DataMember(Name = "Message")]
    public string Text { get; set; }

    [DataMember]
    public decimal Value { get; set; }
}

Os dados serializados em JSON podem ser algo como:

{"ID":123,"Message":"I am a sample text.","Value":150.85}

Para usar a classe DataContractJsonSerializer, que está no namespace System.Runtime.Serialization.Json, é necessário adicionar referências para dois assemblies no seu projeto:

  • System.Runtime.Serialization
  • System.ServiceModel.Web

Agora que já sabemos como serializar e deserializar em JSON, podemos criar um utilitário que faz esse trabalho. O exemplo abaixo utiliza Extensions Methods combinados com Generics:

public static class JsonSerializerExtensions
{
    public static string ToJson<T>(this T obj)
    {
        MemoryStream stream;

        using (stream = new MemoryStream())
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            serializer.WriteObject(stream, obj);
        }

        return Encoding.Default.GetString(stream.ToArray());
    }

    public static T FromJson<T>(this string json)
    {
        T obj;

        using (MemoryStream stream = new MemoryStream(Encoding.Default.GetBytes(json)))
        {
            DataContractJsonSerializer serializer = new DataContractJsonSerializer(typeof(T));
            obj = (T)serializer.ReadObject(stream);
        }

        return obj;
    }
}

E um exemplo de sua utilização:

SomeFakeClass fake = new SomeFakeClass
{
    ID = 123,
    Text = "I am a sample text.",
    Value = 150.85M
};

string fakeJson = fake.ToJson();
SomeFakeClass otherFake = fakeJson.FromJson<SomeFakeClass>();

.
O código-fonte desse utilitário você pode baixar aqui.


Post original:
http://prodis.pro.br/2009/10/23/serializacao-de-objetos-em-json-com-net

.