¡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. , .
. 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
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!