Atribuição de atributos de data sem ActiveRecord

O ActiveRecord às vezes parece uma caixa preta de magia e unicórnios, podemos odiá-lo, mas não podemos negar que ele faz muitas coisas por nós. Uma dessas coisas é instanciar e atribuir magicamente objetos de data aos atributos de data do nosso modelo, mas o que acontece quando nos desviamos de seu caminho mágico?

Se você está construindo uma aplicação Rails sem AR ao enviar formulários com datas, você pode ter tropeçado neste tipo de parâmetro

{ "name" => "James Bond", "birth_date(3i)"=>"11", "birth_date(2i)"=>"11", "birth_date(1i)"=>"1920" }

Digamos que você tenha um modelo simples com um atributo birth_date e name, por uma questão de simplicidade, vamos assumir que BaseModel adiciona todos os clichês necessários para fazer nossa classe se comportar como um modelo ActiveRecord

class Person < BaseModel
attr_accessor
:birth_date, :name

def initialize(attributes = nil)
super
end
end

Agora, ao enviar, nosso nome de formulário será atribuído, mas data de nascimento não.

A razão é simples: data de nascimento (3i), data de nascimento (2i) nem data de nascimento (1i) são atributos da classe, mas o “ `assign attribute“` do AR faz toda a mágica necessária para atribuir datas e outros atributos multiparâmetros .

Conforme afirmado anteriormente, podemos basear nossa implementação em ActiveRecord assign_attributee iterar através dos atributos passados, selecionar aqueles que são do tipo multiparâmetro, extrair seus valores e atribuí-los a um novo hash a ser enviado à classe pai.

class Person < BaseModel
attr_accessor
:birth_date, :name

def initialize(attributes = nil)
super

assign_dates
(attributes) if attributes
end


protected


def assign_dates(attributes)
new_attributes
= attributes.stringify_keys
multiparameter_attributes
= extract_multiparameter_attributes(new_attributes)

multiparameter_attributes
.each do |multiparameter_attribute, values_hash|
set_values
= (1..3).collect{ |position| values_hash[position].to_i }

self.send("#{multiparameter_attribute}=", Date.new(*set_values))
end
end

def extract_multiparameter_attributes(new_attributes)
multiparameter_attributes
= []

new_attributes
.each do |k, v|
if k.include?('(')
multiparameter_attributes
<< [k, v]
end
end

extract_attributes
(multiparameter_attributes)
end

def extract_attributes(pairs)
attributes
= {}

pairs
.each do |pair|
multiparameter_name
, value = pair
attribute_name
= multiparameter_name.split('(').first
attributes
[attribute_name] = {} unless attributes.include?(attribute_name)

attributes
[attribute_name][find_parameter_position(multiparameter_name)] ||= value
end

attributes

end

def find_parameter_position(multiparameter_name)
multiparameter_name
.scan(/(([0-9]*).*)/).first.first.to_i
end
end

Mergulhar na fonte do ActiveRecord é um exercício gratificante que ajuda muito a compreender as capacidades aparentemente mágicas desta caixa preta.