DataMapper versus ActiveRecord

Outro dia, tive um problema chatíssimo com o ActiveRecord: eu precisava criar um código de autenticidade, que conteria várias matrículas, que conteria várias disciplinas. O problema é o seguinte: Essas matrículas só seriam válidas se, por exemplo, o total de créditos das disciplinas não ultrapassassem um determinado número. Logo, resolvi fazer a validação no modelo de autenticidade, da seguinte forma:

class Autenticidade < ActiveRecord::Base
  MAX_CREDITOS = 40
  has_many :matriculas
  has_many :disciplinas, :through => :matriculas
  validate :credito_alto?

  def credito_alto?
    total = disciplinas.inject(0) { |r, v| r += v.creditos }
    errors.add(:disciplinas, 'créditos acima do permitido') if total > MAX_CREDITOS
  end
end

class Matricula < ActiveRecord::Base
  belongs_to :disciplina
  belongs_to :autenticidade
end

class Disciplina < ActiveRecord::Base
end

Ok, o problema agora é o seguinte: Se eu crio uma autenticidade, com o parâmetro :disciplina_ids => [1, 2], por exemplo, e a autenticidade não é válida, nenhuma nova matrícula é criada – como deveria ser. Agora, se eu atualizo uma autenticidade, com o  autenticidade.update_attributes( :disciplina_ids => [1, 2]), e essa autenticidade não é válida, o ActiveRecord salva as matrículas. Nesse caso, eu tenho uma inconsistência na base de dados feia – e precisava encontrar uma alternativa que não envolvesse muitos hacks, coisa que ainda não encontrei. Mas isso me estimulou a pesquisar o DataMapper.

O DataMapper é um ORM assim como o ActiveRecord – porém com um diferencial que (para alguns) é interessante: Ele só lança os comandos SQL quando você precisa. Nossa autenticidade (fora as validações) ficaria assim:

class Autenticidade
  include DataMapper::Resource

  MAX_CREDITOS = 40
  property :id, Serial
  property :codigo_autenticidade, String
  has n, :matriculas
  has n, :disciplinas, :through => :matriculas
end

Para alguns, é uma vantagem declarar as propriedades no próprio código – dizem que é mais simples de ver o que há nas suas tabelas. Eu, discordo categoricamente disso – o código-fonte não deveria fazer o que a documentação faz (se você não tem um dicionário de dados, é culpa sua, não do ActiveRecord). Mas uma coisa legal é: Se vc fizer, por exemplo: a = Autenticidade.all[0], o DataMapper gera apenas o SQL que você precisaria (nesse caso, SELECT * FROM autenticidades LIMIT 1). Isso funciona, de acordo com a documenação do DataMapper, inclusive para relações:

autenticidades = Autenticidade.all #Não gera nenhum SQL ainda
autenticidades.each do |autenticidade| #Gera um SELECT * FROM autenticidades
  p autenticidade.matriculas #Gera um SELECT para listar TODAS as matrículas de TODAS as autenticidades, apenas na primeira iteração
end

Ok, isso elimina o tal do problema n+1 (No ActiveRecord, isso geraria um SELECT para cada autenticidade). O problema é: isso funciona muito bem, mas no has n, :through, o problema do n+1 existe! E, para piorar, um Autenticidade.all :include => :disciplinas simplesmente não existe, logo eu não tenho como sair desse erro. No fim, a vantagem virou uma desvantagem.

Outra vantagem que eu tinha visto era a possibilidade de criar adaptadores para coisas que não são bases de dados relacionais – imagine, por exemplo, você usando o DataMapper como um ORM para LDAP, MySQL, REST e YAML, tudo ao mesmo tempo – fazer associações com isso seria uma maravilha, falar que seu usuário do MySQL tem uma entrada LDAP, usando as associações do próprio DataMapper. Porém, mais uma vez, as coisas não funcionaram muito bem: has n só funciona quando as bases de dados estão no mesmo adaptador, talvez para otimizar os SQLs com JOINs, etc. Logo, mais uma vantagem que eu vi foi sub-utilizada.

No fim, a única vantagem que o DataMapper me deu foi a possibilidade de fazer isso aqui:

autenticidade = Autenticidade.first
autenticidade.disciplinas << Disciplina.get(:id => 1)
autenticidade.disciplinas << Disciplina.get(:id => 2)
autenticidade.save #Se for falso, NÃO salva as disciplinas

Pois é, eu gostaria mais de ver as vantagens do DataMapper no ActiveRecord do que o inverso…

Advertisements
This entry was posted in Ruby and tagged , , , , . Bookmark the permalink.

4 Responses to DataMapper versus ActiveRecord

  1. Seria possível um merge entre os 2 módulos?

  2. Duvido, porque são duas bibliotecas diferentes, com visões diferentes. Aparentemente o ActiveRecord é menos extensível e mais “completo”, enquanto o DataMapper é minimalista (por opção) e para completar as funcionalidades dele que se usam plugins…

  3. Pingback: Lazy Evaluation « Maurício Szabo

  4. Fabio says:

    Murício,

    Sou novo em Ruby, mas ontem estava estudando essa questão de validação e li que quando se usa update_attributes a validação não e disparada. Daí a inconsistência no seu banco de dados.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s