Passando dados do form para os objetos no Ruby on Rails

Olá,

Hoje vos escrevo sobre algo básico, porém útil, mas que ninguém que trabalha com Rails soube me responder.

Trata-se da passagem de todos os campos do form direto para um objeto, mas tudo isso em 1 linha de código, Ohhhh!!

Esse código é gerado pelo scaffolding do Rails, mas fazendo o administrativo do site na mão (pois o scaffolding é básico e ninguém realmente usa isso) precisei disso, de passar tudo direto para o objeto sem precisar escrever atributo por atributo, então segue ai um exemplo da estrutura que os campos do form precisam ter para analisar-mos.

<input name=”id” value=”150″ type=”hidden” />

<input name=”dados[nome]” value=”teste” type=”text” maxlenght=”50″ />

<input name=”dados[ativo]” value=”1″ type=”checkbox” />

Vamos supor que tenhamos uma classe chamada Cliente que possui os atributos:

nome [varchar: 50]

ativo [tinyint: 1]

Para jogar-mos esses dois atributos para uma instância da classe Cliente e gravar no banco precisamos fazer assim no Rails:

cliente = Cliente.new(params[:dados])

cliente.save

e para alterar os atributos de um Cliente já existente, usamos:

cliente = Cliente.find(params[:id])

cliente.update_attributes(params[:dados])

#não precisa do cliente.save, pois o método update_attributes já faz isso por você

Agora, a explicação é bem simples, quando você cria um array de campos e cada campo tem o nome de um atributo da classe final, quando você passar este array por parâmetro o Ruby on Rails vai verificar e jogar automaticamente os campos do form para cada atributo do objeto.

Então se tempo no form um campo chamado dados[nome] e em nome classe temos o atributo nome, na passagem dos dados automaticamente o atributo nome recebe o valor do campo dados[nome].

Pelo scaffold, ele cria os forms contendo o nome do array sendo o nome da classe, então se você gerar um scaffold para cliente, ele vai gerar os nomes dos campos assim:  cliente[nome], cliente[ativo].

Mas isso pouco importa, pois o nome do array não muda em nada, apenas trocariamos de:

Cliente.new(params[:dados])

para

Cliente.new(params[:cliente])

Para economizar trabalho, eu sempre crio os forms com os mesmos nomes de campos, assim quando vou criar um form igual apenas copio as mesmas views e está tudo certo, sei que o nome do array sempre será dados, mas isso é de cada um, o importante é entender o funcionamento.

Espero que tenham gostado, pois isso é algo que dificilmente você encontrará explicando por ai, eu mesmo não achei, só testando o scaffold mesmo para notar isso.

—————————————————————————————-

Estou editando o post pois um amigo (Marcus Vinicus) da lista rails-br me alertou sobre uma forma de hackearem sua aplicação através desta facilidade chamada mass-assignment que foi explicada acima.

Vou tentar explicar o funcionamento de como alguém pode burlar uma aplicação usando o mass-assignment e como podemos nos proteger facilmente com o Rails.

Bom, como expliquei, os valores dos campos são passados diretamente para o objeto, mas nós só colocamos em nosso form os campos nome e ativo, e se tivessemos um atributo em nossa classe chamado admin, ficando assim nossa estrutura:

nome [varchar: 50]

ativo [tinyint: 1]

admin [tinyint: 1]

Sendo que em nosso form só temos 2 campos e este campo admin nós não usamos ele no cadastro do registro nem na alteração, pois somente um usuário é admin e este usuário é um registro qualquer do nosso banco de dados que tem direito a fazer tudo na aplicação, então não precisamos disponibilizar em nosso form esse campo para informar se o usuário é admin ou não, já que decidimos que em nossa aplicação só tem 1 admin e ele já está lá com o valor 1 neste campo da tabela.

Mas ai, vamos supor que um engraçadinho, usando o Firebug do Firefox editou o html e adicionou em nosso form que antes era:

<input name=”dados[nome]” value=”teste” type=”text” maxlenght=”50″ />

<input name=”dados[ativo]” value=”1″ type=”checkbox” />

e mudou para:

<input name=”dados[nome]” value=”teste” type=”text” maxlenght=”50″ />

<input name=”dados[ativo]” value=”1″ type=”checkbox” />

<input name=”dados[admin]” value=”1″ type=”text” />

E quando ele fez o submit no form, o Rails jogou tudo pro objeto Cliente, inclusive o valor do campo admin, e com isso atribuiu para o objeto Cliente o valor 1 para o atributo admin, fazendo com que aquele usuário seja também um administrador do sistema e tenha acesso a tudo.

Obviamente, o Rails sempre tem seus métodos de proteção, e agora vou explicar como permitir somente que alguns campos sejam definidos pelo mass-assignment.

Para fazer essa façanha super difícil, basta na sua classe você especificar assim (com base em nossa classe Cliente):

class Cliente < ActiveRecord::Base
attr_accessible :nome, :ativo
end

Com isso, estamos dizendo que em nossa classe Cliente, somente nos atributos nome e ativo é permitido usar o mass-assignment ou também podemos proibir os campos que não queremos que o mass-assignment atribua valores, assim:

class Cliente < ActiveRecord::Base
attr_protected :admin
end

Assim, estamos definindo em nossa classe Cliente que o campo admin está protegido e que não se pode usar o mass-assignment nele.

Bem simples né? Como tudo no Rails.

Obrigado ao Marcus pela dica do mass-assignment.

Deixe um comentário

Obs: A moderação de comentários está ativa e seu comentário pode demorar a aparecer. Não é necessário enviar seu comentário novamente.