Fácil clonación de objetos active_record, incluidas asociaciones y varias operaciones bajo asociaciones y atributos.
Ver aquí.
El objetivo era poder reproducir fácil y rápidamente objetos ActiveRecord, incluidos sus hijos, por ejemplo, copiando una publicación de blog manteniendo sus etiquetas o categorías asociadas.
Esta gema se llama "Amoeba" porque las amebas son (pequeñas formas de vida que son) buenas para reproducirse. Sus hijos y nietos también se reproducen rápida y fácilmente.
Una gema de extensión ActiveRecord para permitir la duplicación de objetos de registros secundarios asociados al duplicar un modelo de registro activo.
Compatible con rieles 5.2, 6.0, 6.1. Para Rails 4.2 a 5.1 utilice la versión 3.x.
Admite los siguientes tipos de asociación
has_many
has_one :through
has_many :through
has_and_belongs_to_many
Un DSL simple para configurar qué campos copiar. El DSL puede aplicarse a sus modelos de rieles o usarse sobre la marcha.
Admite que los niños STI (herencia de tabla única) hereden la configuración de ameba de sus padres.
Múltiples estilos de configuración como inclusivo, exclusivo e indiscriminado (también conocido como copiar todo).
Admite la clonación de los hijos de registros de muchos a muchos o simplemente el mantenimiento de asociaciones originales.
Admite el desglose automático, es decir, la copia recursiva de registros de hijos y nietos.
Admite el preprocesamiento de campos para ayudar a indicar la unicidad y garantizar la integridad de sus datos según sus necesidades de lógica empresarial, por ejemplo, anteponiendo "Copia de" o texto similar.
Admite el preprocesamiento de campos con bloques lambda personalizados para que pueda hacer básicamente lo que quiera si, por ejemplo, necesita alguna lógica personalizada al realizar copias.
Amoeba puede realizar las siguientes operaciones de preprocesamiento en campos de registros copiados
colocar
anteponer
añadir
anular
personalizar
expresión regular
Es de esperar que sea como era de esperar:
gema instalar ameba
o simplemente agréguelo a su Gemfile:
gema 'ameba'
Configure sus modelos con uno de los estilos a continuación y luego simplemente ejecute el método amoeba_dup
en su modelo donde normalmente ejecutaría el método dup
:
p = Post.create(:title => "¡Hola mundo!", :content => "Lorum ipsum dolor")p.comments.create(:content => "¡Me encanta!")p.comments.create(: content => "¡Esto apesta!")puts Comment.all.count # debería ser 2my_copy = p.amoeba_dupmy_copy.saveputs Comment.all.count # debería ser 4
De forma predeterminada, cuando está habilitado, amoeba copiará automáticamente todos y cada uno de los registros secundarios asociados y los asociará con el nuevo registro principal.
Puede configurar el comportamiento para incluir solo los campos que enumera o para incluir solo los campos que no excluye. De los tres, el de mayor rendimiento será el estilo indiscriminado, seguido del estilo inclusivo, y el estilo exclusivo será el más lento debido a la necesidad de una verificación adicional explícita en cada campo. Es probable que esta diferencia de rendimiento sea lo suficientemente insignificante como para que pueda elegir el estilo a usar según cuál sea más fácil de leer y escribir; sin embargo, si su árbol de datos es lo suficientemente grande y necesita controlar qué campos se copian, el estilo inclusivo probablemente sea una mejor opción. elección que el estilo exclusivo.
Tenga en cuenta que estos ejemplos son solo aproximaciones vagas de escenarios del mundo real y pueden no ser particularmente realistas; solo tienen el propósito de demostrar el uso de funciones.
Este es el caso de uso más básico y simplemente permitirá copiar cualquier asociación conocida.
Si tienes algunos modelos para un blog como este:
clase Publicación < ActiveRecord::Base has_many :commentsendclass Comentario < ActiveRecord::Base pertenece_a: posponer
simplemente agregue el bloque de configuración ameba a su modelo y llame al método enable para habilitar la copia de registros secundarios, así:
clase Publicación < ActiveRecord::Base tiene_muchos: comentarios ameba factible Comentario endendclass < ActiveRecord::Base pertenece_a: posponer
Los registros secundarios se copiarán automáticamente cuando ejecute el método amoeba_dup
.
Si sólo desea copiar algunas de las asociaciones pero no otras, puede utilizar el estilo inclusivo:
clase Publicación < ActiveRecord::Base tiene_muchos: comentarios tiene_muchos: etiquetas tiene_muchos: autores ameba doenableinclude_association: etiquetassinclude_association: autores Comentario endendclass < ActiveRecord::Base pertenece_a: posponer
Usar el estilo inclusivo dentro del bloque de ameba en realidad implica que deseas habilitar la ameba, por lo que no es necesario ejecutar el método enable, aunque tampoco estará de más:
clase Publicación < ActiveRecord::Base tiene_muchos: comentarios tiene_muchos: etiquetas tiene_muchos: autores ameba doinclude_association :tagsinclude_association :autores Comentario endendclass < ActiveRecord::Base pertenece_a: posponer
También puede especificar los campos que se copiarán pasando una matriz. Si llama a include_association
con un solo valor, se agregará a la lista de campos ya incluidos. Si pasa una matriz, su matriz sobrescribirá los valores originales.
clase Publicación < ActiveRecord::Base tiene_muchos: comentarios tiene_muchos: etiquetas tiene_muchos: autores ameba doinclude_association [:etiquetas, :autores] Comentario endendclass < ActiveRecord::Base pertenece_a: posponer
Estos ejemplos copiarán las etiquetas y los autores de la publicación, pero no sus comentarios.
El estilo inclusivo, cuando se utiliza, desactivará automáticamente cualquier otro estilo que haya sido seleccionado previamente.
Si tiene más campos para incluir que para excluir, es posible que desee acortar la cantidad de escritura y lectura que debe realizar utilizando el estilo exclusivo. Se copiarán todos los campos que no estén explícitamente excluidos:
clase Publicación < ActiveRecord::Base tiene_muchos: comentarios tiene_muchos: etiquetas tiene_muchos: autores ameba doexclude_association: comentarios Comentario endendclass < ActiveRecord::Base pertenece_a: posponer
Este ejemplo hace lo mismo que el ejemplo de estilo inclusivo: copiará las etiquetas y los autores de la publicación, pero no sus comentarios. Al igual que con el estilo inclusivo, no es necesario habilitar explícitamente ameba al especificar campos para excluir.
El estilo exclusivo, cuando se usa, deshabilitará automáticamente cualquier otro estilo que se haya seleccionado previamente, por lo que si seleccionó campos de inclusión y luego elige algunos campos de exclusión, el método exclude_association
deshabilitará el estilo inclusivo previamente seleccionado y borrará cualquier campo de inclusión correspondiente. .
Además, si necesita una ruta de condición adicional para incluir o excluir la relación, puede rutar el nombre del método a la opción :if
.
clase Publicación < ActiveRecord::Base tiene_muchos: comentarios tiene_muchos: etiquetas ameba doinclude_association: comentarios, si:: ¿popular? fin def popular?me gusta > 15 final
Después de llamar Post.first.amoeba_dup
si likes
son mayores que 15, todos los comentarios también se duplicarán, pero en otra situación, no se clonará ninguna relación. El mismo comportamiento será para exclude_association
.
¡ Ten en cuenta ! Si escribiste:
clase Publicación < ActiveRecord::Base tiene_muchos: comentarios tiene_muchos: etiquetas amoeba doexclude_association :tagsinclude_association :comentarios, si: :¿popular? fin def popular?me gusta > 15 final
¿Se elegirá la estrategia de inclusión independientemente del resultado popular?
llamada al método (lo mismo para la situación inversa).
Si está utilizando una relación de muchos a muchos, puede decirle a la ameba que haga duplicados de los registros relacionados originales en lugar de simplemente mantener la asociación con los registros originales. Clonar es fácil, simplemente dígale a ameba qué campos clonar de la misma manera que le dice qué campos incluir o excluir.
clase Publicación < ActiveRecord::Base has_and_belongs_to_many: advertencias tiene_muchos: post_widgets has_many: widgets,: a través =>: post_widgets ameba doenableclone [:widgets, :advertencias] Advertencia de endendclass < ActiveRecord::Base has_and_belongs_to_many :postsendclass PostWidget < ActiveRecord::Base pertenece_a: widget pertenece_a :widget postendclass < ActiveRecord::Base tiene_muchos: post_widgets has_many: publicaciones,: a través =>: post_widgetsend
En realidad, este ejemplo duplicará las advertencias y los widgets en la base de datos. Si originalmente había 3 advertencias en la base de datos, al duplicar una publicación, terminará con 6 advertencias en la base de datos. Esto contrasta con el comportamiento predeterminado en el que su nueva publicación simplemente se reasociaría con cualquier advertencia previamente existente y esas advertencias en sí no se duplicarían.
De forma predeterminada, amoeba reconoce e intenta copiar cualquier hijo de los siguientes tipos de asociación:
tiene uno
tiene muchos
tiene y pertenece a muchos
Puede controlar a qué tipos de asociación se aplica la ameba utilizando el método recognize
dentro del bloque de configuración de la ameba.
clase Publicación < ActiveRecord::Base tiene_uno: configuración tiene_muchos: comentarios has_and_belongs_to_many: etiquetas ameba dorecognize [:tiene_uno, :tiene_y_pertenece_a_muchos] Comentario endendclass < ActiveRecord::Base pertenece_a :postendclass Etiqueta < ActiveRecord::Base has_and_belongs_to_many: publicación enviada
Este ejemplo copiará los datos de configuración de la publicación y mantendrá las etiquetas asociadas con la nueva publicación, pero no copiará los comentarios de la publicación porque amoeba solo reconocerá y copiará elementos secundarios de las asociaciones has_one
y has_and_belongs_to_many
y, en este ejemplo, los comentarios no son una asociación has_and_belongs_to_many
.
Si desea evitar que un campo normal (no basado en asociación has_*
) conserve su valor cuando se copia, puede "poner a cero" o "anular" el campo, de esta manera:
Tema de clase < ActiveRecord::Base has_many :postsendclass Publicación < ActiveRecord::Base pertenece_a: tema tiene_muchos: comentarios ameba doenablenullify :date_publishednullify :topic_id Comentario endendclass < ActiveRecord::Base pertenece_a: posponer
Este ejemplo copiará todos los comentarios de una publicación. También anulará la fecha de publicación y disociará la publicación de su tema original.
A diferencia de los estilos inclusivo y exclusivo, especificar campos nulos no permitirá que ameba copie automáticamente todos los registros secundarios. Al igual que con cualquier objeto de registro activo, se utilizará el valor de campo predeterminado en lugar de nil
si existe un valor predeterminado en la migración.
Si desea simplemente establecer un campo con un valor arbitrario en todos los objetos duplicados, puede usar la directiva set
. Por ejemplo, si desea copiar un objeto que tiene algún tipo de proceso de aprobación asociado, es posible que desee configurar el estado del nuevo objeto como abierto o "en progreso" nuevamente.
clase Publicación < ActiveRecord::Base dosis de ameba :state_tracker => "open_for_editing" final
En este ejemplo, cuando se duplica una publicación, su campo state_tracker
siempre recibirá un valor de open_for_editing
para comenzar.
Puede agregar una cadena al comienzo del campo de un objeto copiado durante la fase de copia:
clase Publicación < ActiveRecord::Base ameba doenableprepend :title => "Copia de " final
Puede agregar una cadena al final del campo de un objeto copiado durante la fase de copia:
clase Publicación < ActiveRecord::Base ameba doenableappend :title => "Copia de " final
Puede ejecutar una consulta de búsqueda y reemplazo en el campo de un objeto copiado durante la fase de copia:
clase Publicación < ActiveRecord::Base ameba doenableregex :contents => {:replace => /perro/, :with => 'cat'} final
Puede ejecutar uno o más métodos personalizados para hacer básicamente lo que quiera, simplemente pase un bloque lambda o una matriz de bloques lambda a la directiva customize
. Cada bloque debe tener la misma forma, lo que significa que cada bloque debe aceptar dos parámetros, el objeto original y el objeto recién copiado. Luego puedes hacer lo que desees, así:
clase Publicación < ActiveRecord::Base amoeba doprepend :title => "¡Hola mundo! "personalizar(lambda { |original_post,new_post| if original_post.foo == "bar"new_post.baz = "qux" end})append :comments => "... sabes qué ¿Estoy diciendo?" final
o esto, usando una matriz:
clase Publicación < ActiveRecord::Base has_and_belongs_to_many: etiquetas amoeba doinclude_association :tagscustomize([ lambda do |orig_obj,copy_of_obj|# cosas buenas van aquí al final, lambda do |orig_obj,copy_of_obj|# más cosas buenas van aquí al final]) final
Los bloques Lambda se pasan para personalizar la ejecución, de forma predeterminada, después de realizar todas las copias y el preprocesamiento de campos. Si desea ejecutar un método antes de cualquier personalización o preprocesamiento de campos, puede utilizar override
el primo de customize
. El uso es el mismo que el anterior.
clase Publicación < ActiveRecord::Base amoeba doprepend :title => "¡Hola mundo!" override(lambda { |original_post,new_post| if original_post.foo == "bar"new_post.baz = "qux" end})append :comments => "... sabes qué ¿Estoy diciendo?" final
Puede aplicar un único preprocesador a varios campos a la vez.
clase Publicación < ActiveRecord::Base amoeba doenableprepend :title => "Copia de", :contents => "Contenido copiado: " final
Puede aplicar varias directivas de preprocesamiento a un solo modelo a la vez.
clase Publicación < ActiveRecord::Base amoeba doprepend :title => "Copia de ", :contents => "Contenido original: "append :contents => " (versión copiada)"regex :contents => {:replace => /dog/, :with => ' gato'} final
Este ejemplo debería resultar en algo como esto:
publicación = Publicación.crea( :title => "Hola mundo", :contents => "Me gustan los perros, los perros son increíbles.")new_post = post.amoeba_dupnew_post.title # "Copia de Hola mundo"new_post.contents # "Contenido original: Me gustan los gatos, los gatos son increíbles. (versión copiada)"
Al igual que nullify
, las directivas de preprocesamiento no habilitan automáticamente la copia de registros secundarios asociados. Si solo se utilizan directivas de preprocesamiento y desea copiar registros secundarios y no se proporciona ninguna lista include_association
o exclude_association
, aún debe habilitar explícitamente la copia de registros secundarios llamando al método enable desde dentro del bloque de ameba en su modelo.
Puede utilizar una combinación de métodos de configuración dentro del bloque de amebas de cada modelo. Los tipos de asociación reconocidos tienen prioridad sobre las listas de inclusión o exclusión. El estilo inclusivo tiene prioridad sobre el estilo exclusivo, y estos dos estilos explícitos tienen prioridad sobre el estilo indiscriminado. En otras palabras, si enumera los campos para copiar, amoeba solo copiará los campos que enumera, o solo copiará los campos que no excluye, según sea el caso. Además, si no se reconoce un tipo de campo, no se copiará, independientemente de si aparece en una lista de inclusión. Si desea que amoeba copie automáticamente todos sus registros secundarios, no incluya ningún campo utilizando include_association
o exclude_association
.
La sintaxis de ejemplo siguiente es perfectamente válida y dará como resultado el uso de un estilo inclusivo. El orden en el que llamas a los métodos de configuración dentro del bloque ameba no importa:
Tema de clase < ActiveRecord::Base has_many :postsendclass Publicación < ActiveRecord::Base pertenece_a: tema tiene_muchos: comentarios tiene_muchos: etiquetas tiene_muchos: autores amoeba doexclude_association :authorsinclude_association :tagsnullify :date_publishedprepend :title => "Copia de "append :contents => " (versión copiada)"regex :contents => {:replace => /dog/, :with => 'cat'}include_association :autoressenablenullify :topic_id Comentario endendclass < ActiveRecord::Base pertenece_a: posponer
Este ejemplo copiará todas las etiquetas y autores de una publicación, pero no sus comentarios. También anulará la fecha de publicación y disociará la publicación de su tema original. También preprocesará los campos de la publicación como en el ejemplo de preprocesamiento anterior.
Tenga en cuenta que, debido a la precedencia, se utiliza el estilo inclusivo y nunca se consulta la lista de campos excluidos. Además, el método enable
es redundante porque la ameba se habilita automáticamente cuando se usa include_association
.
Las directivas de preprocesamiento se ejecutan después de copiar los registros secundarios y se ejecutan en este orden.
Campos nulos
antepone
anexa
Buscar y reemplazar
Las directivas de preprocesamiento no afectan las listas de inclusión y exclusión.
Puedes hacer que la ameba siga copiando la cadena tanto como quieras, simplemente agrega bloques de ameba a cada modelo que desees copiar a sus hijos. Amoeba recurrirá automáticamente a cualquier nieto habilitado y también los copiará.
clase Publicación < ActiveRecord::Base tiene_muchos: comentarios ameba factible Comentario endendclass < ActiveRecord::Base pertenece_a: publicación tiene_muchos: calificaciones ameba factible Clasificación endendclass < ActiveRecord::Base pertenece_a: comentar
En este ejemplo, cuando se copia una publicación, amoeba copiará todos los comentarios de una publicación y también copiará las calificaciones de cada comentario.
Usar la asociación has_one :through
es simple, solo asegúrate de habilitar amoeba en cada modelo con una asociación has_one
y amoeba profundizará automática y recursivamente, así:
clase Proveedor < ActiveRecord::Base tiene_uno: cuenta has_one :historial, :a través => :cuenta ameba factible Cuenta endendclass < ActiveRecord::Base pertenece_a:proveedor tiene_uno: historial ameba factible Historial endendclass < ActiveRecord::Base pertenece_a: cuenta
La copia de has_many :through
asociaciones funciona automáticamente. Realizan la copia de la misma manera que la asociación has_and_belongs_to_many
, lo que significa que los registros secundarios reales no se copian, sino que las asociaciones simplemente se mantienen. Puedes agregar algunos preprocesadores de campo al modelo intermedio si lo deseas, pero esto no es estrictamente necesario:
clase Ensamblaje < ActiveRecord::Base tiene_muchos: manifiestos has_many :partes, :a través => :manifiestos ameba factible Manifiesto endendclass < ActiveRecord::Base pertenece_a: asamblea pertenece_a: parte ameba doprepend :notas => "Copia de " Parte endendclass < ActiveRecord::Base tiene_muchos: manifiestos has_many: ensamblados,: a través =>: manifiestos ameba factible final
Puedes controlar cómo la ameba copia tu objeto, sobre la marcha, pasando un bloque de configuración al método ameba del modelo. El método de configuración es estático pero la configuración se aplica caso por caso.
clase Publicación < ActiveRecord::Base tiene_muchos: comentarios ameba doenableprepend :title => "Copia de " Comentario endendclass < ActiveRecord::Base pertenece_a: postendclass PostsController <ActionController def duplicado_a_postold_post = Post.create( :title => "Hola mundo", :contents => "Lorum ipsum")old_post.class.amoeba prepend :contents => "Aquí hay una copia: "endnew_post = old_post.amoeba_dupnew_post.title # debería ser "Copia de Hola mundo"new_post.contents # debería ser "Aquí hay una copia: Lorum ipsum"new_post.save final
Si está utilizando la herencia de tabla única proporcionada por ActiveRecord, puede hacer que amoeba procese automáticamente las clases secundarias de la misma manera que sus padres. Todo lo que necesita hacer es llamar al método propagate
dentro del bloque ameba de la clase principal y todas las clases secundarias deberían copiarse de manera similar.
create_table :productos, :force => true do |t| t.string: escriba # esta es la columna STI # estos pertenecen a todos los productos t.cadena: título t.decimal :precio # estos son solo para camisas t.decimal: longitud_manga t.decimal: tamaño_collar # estos son solo para computadoras t.entero: tamaño_ram t.integer :hard_drive_sizeendclass Producto < ActiveRecord::Base tiene_muchos: imágenes has_and_belongs_to_many: categorías ameba que se puede propagar endendclass Camisa < Productendclass Computadora < Productendclass ProductosControlador def some_methodmy_shirt = Shirt.find(1)my_shirt.amoeba_dupmy_shirt.save# esta camiseta ahora debería:# - tener su propia copia de todas las imágenes principales# - estar en las mismas categorías que la principal final
Este ejemplo debería duplicar todas las imágenes y secciones asociadas con esta camiseta, que es secundaria del Producto.
De forma predeterminada, la propagación utiliza una crianza sumisa, lo que significa que se aplicarán las configuraciones del padre, pero cualquier configuración secundaria, si está presente, se agregará o sobrescribirá la configuración del padre dependiendo de cómo llame a los métodos DSL.
Puede cambiar este comportamiento, el llamado "estilo de crianza", para dar preferencia a la configuración de los padres o ignorar todas y cada una de las configuraciones de los hijos.
El estilo de crianza :relaxed
preferirá la configuración de los padres.
clase Producto < ActiveRecord::Base tiene_muchos: imágenes has_and_belongs_to_many:secciones ameba doexclude_association:imagespropagate:relajado Camisa endendclass < Producto incluir_asociación: imágenes include_association: secciones anteponer :title => "Copia de "end
En este ejemplo, se ignorarán las configuraciones conflictivas include_association
en el niño y se utilizará la configuración exclude_association
principal.