Metaprogramación en una tarea del mundo real

¡Hola! En este artículo quiero hablar sobre la metaprogramación usando un ejemplo de un problema común real.





Cuando alguien habla de metaprogramación, un programador de la vieja escuela se enfurece.





Y hay razones para esto, y en un proyecto grande puede parecer una locura usar metaprogramación, ya que el código se vuelve muy difícil de leer. Y si un especialista del exterior se une al proyecto, tampoco entenderá nada en este metacódigo.





Pero no es tan simple como dicen, no hay malas herramientas. En este artículo intentaré mostrar con un ejemplo real de trabajo cómo la metaprogramación puede ayudar a hacer su código más limpio y eliminar las repeticiones de rutina. Y te hará regocijar en la meta-magia.





Ayuda de wikipedia

La metaprogramación es un tipo de programación asociado a la creación de programas que generan otros programas como resultado de su trabajo (en particular, en la etapa de compilación de su código fuente), o programas que se cambian durante la ejecución (código auto modificable). . El primero te permite conseguir programas con menos tiempo y esfuerzo en codificar que si el programador los escribiera completamente a mano, el segundo te permite mejorar las propiedades del código (tamaño y rendimiento).





Con esto concluye la introducción. Ahora quiero pasar a la parte práctica y contarte la esencia del problema.

Además, hablaremos sobre el lenguaje ruby ​​y el framework Rails en particular. Ruby tiene reflexión y grandes oportunidades para la metaprogramación. La mayoría de las gemas, módulos y marcos están construidos con poderosas herramientas de reflexión.





Foto de Joshua Fuller en Unsplash





Quienquiera que haya escrito algo en Rails probablemente se encontró con una joya como Rails Admin, si no ha intentado usarlo en sus proyectos sobre rieles, o si tiene análogos, le recomiendo encarecidamente que lo haga, ya que casi de inmediato obtendrá un CMS completo para su sistema.





Entonces hay una característica desagradable , el problema con la asociación has_one.





has_one . 1. , .





Foto 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 haber podido demostrar el valor práctico de utilizar técnicas de metaprogramación. Por supuesto, tú decides si lo utilizas o no. Si lo usa con demasiada frecuencia y fuera de lugar, entonces el proyecto se volverá absolutamente ilegible y difícil de depurar, pero si se usa correctamente, por el contrario, reduce la cantidad de código, mejora la legibilidad y elimina el trabajo de rutina. ¡Gracias a todos los que lo leyeron!








All Articles