La métaprogrammation dans une tâche du monde réel

salut! Dans cet article, je veux parler de métaprogrammation en utilisant un exemple de problème réel commun.





Quand quelqu'un parle de métaprogrammation, un codeur de la vieille école devient furieux.





Et il y a des raisons à cela, et sur un gros projet, il peut sembler fou d'utiliser la métaprogrammation, car le code devient très difficile à lire. Et si un spécialiste de l'extérieur rejoint le projet, alors il ne comprendra encore plus rien à ce méta-code.





Mais ce n'est pas aussi simple qu'on le dit - il n'y a pas de mauvais outils. Dans cet article, je vais essayer de montrer avec un exemple concret comment la métaprogrammation peut aider à rendre votre code plus propre et à éliminer les répétitions de routine. Et vous fera vous réjouir de la méta-magie.





Aide de wikipedia

La métaprogrammation est un type de programmation associé à la création de programmes qui génèrent d'autres programmes du fait de leur travail (en particulier, au stade de la compilation de leur code source), ou des programmes qui se modifient en cours d'exécution (code auto-modifiable) . Le premier vous permet d'obtenir des programmes avec moins de temps et d'efforts de codage que si le programmeur les écrivait entièrement à la main, le second vous permet d'améliorer les propriétés du code (taille et performances).





Ceci conclut l'introduction. Maintenant, je veux passer à la partie pratique et vous parler de l'essence du problème

Plus loin, nous parlerons du langage ruby ​​et du framework Rails en particulier. Ruby a de la réflexion et de grandes opportunités pour la métaprogrammation. La plupart des gemmes, modules et frameworks sont construits avec de puissants outils de réflexion.





Photo de Joshua Fuller sur Unsplash





Quiconque a écrit quelque chose sur Rails est probablement tombé sur un joyau tel que Rails Admin, si vous n'avez pas essayé de l'utiliser dans vos projets sur rails, ou si vous avez des analogues, je vous recommande fortement de le faire, car vous le ferez presque immédiatement. obtenez un CMS à part entière pour votre système.





Il y a donc une fonctionnalité désagréable , le problème de l'association has_one.





has_one . 1. , .





Image 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

      
      







J'espère avoir pu montrer la valeur pratique de l'utilisation des techniques de métaprogrammation. Bien sûr, vous décidez de l'utiliser ou non. Si vous l'utilisez trop souvent et hors de propos, le projet deviendra absolument illisible et difficile à déboguer, mais s'il est utilisé correctement, au contraire, réduit la quantité de code, améliore la lisibilité et élimine le travail de routine. Merci à tous ceux qui l'ont lu!








All Articles