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.