Metaprogramação em uma tarefa do mundo real

Olá! Neste artigo, quero falar sobre metaprogramação usando um exemplo de um problema comum real.





Quando alguém fala sobre metaprogramação, um programador da velha escola tem um acesso de raiva.





E há razões para isso, e em um grande projeto pode parecer loucura usar metaprogramação, pois o código se torna muito difícil de ler. E se um especialista de fora se juntar ao projeto, ele ainda mais não entenderá nada neste meta-código.





Mas não é tão simples quanto dizem - não existem ferramentas ruins. Neste artigo, tentarei mostrar com um exemplo real de trabalho como a metaprogramação pode ajudar a tornar seu código mais limpo e eliminar as repetições de rotina. E vai fazer você se alegrar com a metamágica.





Ajuda da wikipedia

Metaprogramação é um tipo de programação associada à criação de programas que geram outros programas como resultado de seu trabalho (em particular, na fase de compilação de seu código-fonte), ou programas que se alteram durante a execução (código auto-modificador) . O primeiro permite obter programas com menos tempo e esforço de codificação do que se o programador os escrevesse inteiramente à mão, o segundo permite melhorar as propriedades do código (tamanho e desempenho).





Isso conclui a introdução. Agora eu quero passar para a parte prática e falar sobre a essência do problema

Além disso, falaremos sobre a linguagem ruby ​​e o framework Rails em particular. Ruby tem reflexão e grandes oportunidades para metaprogramação. A maioria das joias, módulos e estruturas são construídas com ferramentas de reflexão poderosas.





Foto de Joshua Fuller no Unsplash





Quem escreveu algo no Rails provavelmente encontrou uma joia como o Rails Admin, se você não tentou usá-lo em seus projetos no Rails, ou se você tem análogos, eu recomendo fortemente fazer isso, já que quase pronto, você irá obtenha um CMS completo para o seu sistema.





Portanto, há um recurso desagradável , o problema com a associação has_one.





has_one . 1. , .





Imagem 1
1

. has_one (draft) CMS.





class TechProcess < ApplicationRecord
  include MdcSchema
  has_many :executor_programs, inverse_of: :tech_process, foreign_key: :barcode_tech_process, primary_key: :barcode

  validates :barcode, presence: true
  validates :barcode, uniqueness: true

  has_one :draft2tech_process, dependent: :destroy
  has_one :draft, through: :draft2tech_process

  has_many :tech_process2tech_operations
  has_many :tech_operations, through: :tech_process2tech_operations
end

      
      







. has_one , . .









def draft_id
  self.draft.try :id
end

def draft_id=(id)
  self.draft = Draft.find_by_id(id)
end
      
      



, , , CMS.









, 15 has_one ? , DRY. . .





Meta





self.reflect_on_all_associations(:has_one).each do |has_one_association|
  define_method("#{has_one_association.name}_id") do
    self.send(has_one_association.name).try :id
  end

  define_method("#{has_one_association.name}_id=") do |id|
    self.send("#{has_one_association.name}=",has_one_association.klass.find_by_id(id))
  end
end
      
      



, has_one Rails Admin





. .





, . reflect_on_all_associations , "macro" :hasone has_one , , .





, has_one . , , define_method .





has_one rails admin.





"" , . - concern





require 'active_support/concern'
module HasOneHandler
  extend ActiveSupport::Concern
  included do
    self.reflect_on_all_associations(:has_one).each do |has_one_association|
      define_method("#{has_one_association.name}_id") do
        self.send(has_one_association.name).try :id
      end

      define_method("#{has_one_association.name}_id=") do |id|
        self.send("#{has_one_association.name}=",has_one_association.klass.find_by_id(id))
      end
    end
  end
end
      
      



, . CMS has_one . , .





class TechProcess < ApplicationRecord
  include MdcSchema
  include HasOneHandler

  has_many :executor_programs, inverse_of: :tech_process, foreign_key: :barcode_tech_process, primary_key: :barcode

  validates :barcode, presence: true
  validates :barcode, uniqueness: true

  has_one :draft2tech_process, dependent: :destroy
  has_one :draft, through: :draft2tech_process

  has_many :tech_process2tech_operations
  has_many :tech_operations, through: :tech_process2tech_operations

end

      
      







Espero ter conseguido mostrar o valor prático do uso de técnicas de metaprogramação. Claro, você decide se vai usar ou não. Se você usar com muita frequência e fora do lugar, o projeto se tornará absolutamente ilegível e difícil de depurar, mas se usado corretamente, ao contrário, reduz a quantidade de código, melhora a legibilidade e elimina o trabalho rotineiro. Obrigado a todos que leram!








All Articles