Kloning objek active_record dengan mudah termasuk asosiasi dan beberapa operasi di bawah asosiasi dan atribut.
Lihat di sini.
Tujuannya adalah untuk dapat dengan mudah dan cepat mereproduksi objek ActiveRecord termasuk turunannya, misalnya menyalin postingan blog dengan mempertahankan tag atau kategori terkait.
Permata ini diberi nama "Amoeba" karena amuba (bentuk kehidupan kecil yang) pandai bereproduksi. Anak cucu mereka juga memperbanyak diri dengan cepat dan mudah.
Permata ekstensi ActiveRecord untuk memungkinkan duplikasi objek rekaman anak terkait saat menduplikasi model rekaman aktif.
Kompatibel dengan Rel 5.2, 6.0, 6.1. Untuk Rails 4.2 hingga 5.1 gunakan versi 3.x.
Mendukung jenis asosiasi berikut
has_many
has_one :through
has_many :through
has_and_belongs_to_many
DSL sederhana untuk konfigurasi bidang mana yang akan disalin. DSL dapat diterapkan pada model rel Anda atau digunakan dengan cepat.
Mendukung anak-anak IMS (Single Table Inheritance) yang mewarisi pengaturan amuba induknya.
Berbagai gaya konfigurasi seperti inklusif, eksklusif, dan sembarangan (alias menyalin semuanya).
Mendukung pengklonan anak-anak dari catatan Banyak-ke-Banyak atau sekadar mempertahankan asosiasi asli
Mendukung penelusuran otomatis, yaitu penyalinan catatan anak dan cucu secara rekursif.
Mendukung prapemrosesan bidang untuk membantu menunjukkan keunikan dan memastikan integritas data Anda tergantung pada kebutuhan logika bisnis Anda, misalnya menambahkan "Salinan" atau teks serupa.
Mendukung prapemrosesan bidang dengan blok lambda khusus sehingga pada dasarnya Anda dapat melakukan apa pun yang Anda inginkan jika, misalnya, Anda memerlukan logika khusus saat membuat salinan.
Amoeba dapat melakukan operasi prapemrosesan berikut pada bidang rekaman yang disalin
mengatur
tambahkan
menambahkan
membatalkan
menyesuaikan
ekspresi reguler
semoga seperti yang Anda harapkan:
permata instal amuba
atau tambahkan saja ke Gemfile Anda:
permata 'amoeba'
Konfigurasikan model Anda dengan salah satu gaya di bawah ini lalu jalankan metode amoeba_dup
pada model Anda di mana Anda akan menjalankan metode dup
secara normal:
p = Post.create(:title => "Halo Dunia!", :content => "Lorum ipsum dolor")p.comments.create(:content => "Saya menyukainya!")p.comments.create(: content => "Ini menyebalkan!")puts Comment.all.count # harusnya 2my_copy = p.amoeba_dupmy_copy.saveputs Comment.all.count # harusnya 4
Secara default, ketika diaktifkan, amuba akan menyalin setiap dan semua catatan anak terkait secara otomatis dan mengaitkannya dengan catatan induk baru.
Anda dapat mengonfigurasi perilaku untuk hanya menyertakan bidang yang Anda cantumkan atau hanya menyertakan bidang yang tidak Anda kecualikan. Dari ketiganya, yang paling berkinerja adalah gaya indiscriminate, diikuti oleh gaya inklusif, dan gaya eksklusif akan menjadi yang paling lambat karena perlunya pemeriksaan ekstra eksplisit pada setiap bidang. Perbedaan kinerja ini mungkin cukup kecil sehingga Anda dapat memilih gaya yang akan digunakan berdasarkan mana yang paling mudah dibaca dan ditulis. Namun, jika pohon data Anda cukup besar dan Anda memerlukan kontrol atas bidang apa yang akan disalin, gaya inklusif mungkin lebih baik. pilihan daripada gaya eksklusif.
Harap dicatat bahwa contoh-contoh ini hanyalah perkiraan longgar dari skenario dunia nyata dan mungkin tidak terlalu realistis, contoh-contoh ini hanya bertujuan untuk menunjukkan penggunaan fitur.
Ini adalah kasus penggunaan paling dasar dan hanya akan memungkinkan penyalinan semua asosiasi yang diketahui.
Jika Anda mempunyai beberapa model untuk blog kira-kira seperti ini:
kelas Posting < ActiveRecord::Base has_many :commentsendclass Komentar < ActiveRecord::Base milik_untuk :postend
cukup tambahkan blok konfigurasi amoeba ke model Anda dan panggil metode aktifkan untuk mengaktifkan penyalinan catatan anak, seperti ini:
kelas Posting < ActiveRecord::Base has_many :komentar amuba bisa dilakukan kelas endend Komentar < ActiveRecord::Base milik_untuk :postend
Catatan anak akan disalin secara otomatis ketika Anda menjalankan metode amoeba_dup
.
Jika Anda hanya ingin beberapa pengaitan disalin tetapi tidak yang lain, Anda dapat menggunakan gaya inklusif:
kelas Posting < ActiveRecord::Base has_many :komentar memiliki_banyak :tag has_many :penulis amoeba dapat diaktifkaninclude_association :tagsinclude_association :penulis kelas endend Komentar < ActiveRecord::Base milik_untuk :postend
Menggunakan gaya inklusif dalam blok amuba sebenarnya menyiratkan bahwa Anda ingin mengaktifkan amuba, jadi tidak perlu menjalankan metode aktifkan, meskipun tidak ada salahnya juga:
kelas Posting < ActiveRecord::Base has_many :komentar memiliki_banyak :tag has_many :penulis amoeba doinclude_association :tagsinclude_association :penulis kelas endend Komentar < ActiveRecord::Base milik_untuk :postend
Anda juga dapat menentukan bidang yang akan disalin dengan meneruskan array. Jika Anda memanggil include_association
dengan satu nilai, nilai tersebut akan ditambahkan ke daftar kolom yang sudah disertakan. Jika Anda meneruskan sebuah array, array Anda akan menimpa nilai aslinya.
kelas Posting < ActiveRecord::Base has_many :komentar memiliki_banyak :tag has_many :penulis amoeba doinclude_association [:tags, :penulis] kelas endend Komentar < ActiveRecord::Base milik_untuk :postend
Contoh berikut akan menyalin tag dan penulis kiriman, tetapi tidak menyalin komentarnya.
Gaya inklusif, bila digunakan, secara otomatis akan menonaktifkan gaya lain yang dipilih sebelumnya.
Jika Anda memiliki lebih banyak bidang untuk disertakan daripada dikecualikan, Anda mungkin ingin mempersingkat jumlah pengetikan dan membaca yang perlu Anda lakukan dengan menggunakan gaya eksklusif. Semua bidang yang tidak dikecualikan secara eksplisit akan disalin:
kelas Posting < ActiveRecord::Base has_many :komentar memiliki_banyak :tag has_many :penulis amuba doexclude_association :komentar kelas endend Komentar < ActiveRecord::Base milik_untuk :postend
Contoh ini melakukan hal yang sama seperti contoh gaya inklusif, contoh ini akan menyalin tag dan penulis postingan, tetapi tidak menyalin komentarnya. Seperti halnya gaya inklusif, tidak perlu mengaktifkan amuba secara eksplisit saat menentukan bidang yang akan dikecualikan.
Gaya eksklusif, ketika digunakan, akan secara otomatis menonaktifkan gaya lain yang dipilih sebelumnya, jadi jika Anda memilih bidang penyertaan, lalu Anda memilih beberapa bidang pengecualian, metode exclude_association
akan menonaktifkan gaya inklusif yang dipilih sebelumnya dan menghapus semua bidang penyertaan yang sesuai. .
Juga jika Anda perlu jalur kondisi tambahan untuk menyertakan atau mengecualikan hubungan, Anda dapat jalur nama metode ke opsi :if
.
kelas Posting < ActiveRecord::Base has_many :komentar memiliki_banyak :tag amoeba doinclude_association :komentar, jika: :populer? akhir pasti populer?suka > 15 akhir
Setelah panggilan Post.first.amoeba_dup
jika likes
lebih besar 15 maka semua komentar akan diduplikasi juga, tetapi dalam situasi lain - tidak ada relasi yang akan dikloning. Perilaku yang sama akan berlaku untuk exclude_association
.
Sadarlah ! Jika Anda menulis:
kelas Posting < ActiveRecord::Base has_many :komentar memiliki_banyak :tag amoeba doexclude_association :tagsinclude_association :komentar, jika: :populer? akhir pasti populer?suka > 15 akhir
strategi inklusi akan dipilih terlepas dari hasil yang popular?
pemanggilan metode (sama untuk situasi sebaliknya).
Jika Anda menggunakan hubungan Banyak-ke-Banyak, Anda dapat meminta amuba untuk benar-benar membuat duplikat dari catatan terkait yang asli, bukan hanya mempertahankan hubungan dengan catatan asli. Kloning itu mudah, cukup beri tahu amuba bidang mana yang akan dikloning dengan cara yang sama seperti Anda memberi tahu bidang mana yang harus disertakan atau dikecualikan.
kelas Posting < ActiveRecord::Base has_and_belongs_to_many :peringatan memiliki_banyak :post_widgets has_many :widget, :melalui => :post_widgets amoeba doenableclone [:widget, :peringatan] peringatan kelas endend < ActiveRecord::Base has_and_belongs_to_many :postsendclass PostWidget < ActiveRecord::Base milik_untuk :widget milik_to :Widget kelas postend < ActiveRecord::Base memiliki_banyak :post_widgets has_many :posts, :through => :post_widgetsend
Contoh ini sebenarnya akan menduplikasi peringatan dan widget di database. Jika awalnya ada 3 peringatan di database, maka saat menduplikasi postingan, Anda akan mendapatkan 6 peringatan di database. Hal ini berbeda dengan perilaku default di mana postingan baru Anda hanya akan dikaitkan kembali dengan peringatan yang sudah ada sebelumnya dan peringatan itu sendiri tidak akan diduplikasi.
Secara default, amuba mengenali dan mencoba menyalin anak mana pun dari jenis asosiasi berikut:
memiliki satu
memiliki banyak
dimiliki dan menjadi milik banyak orang
Anda dapat mengontrol jenis asosiasi amuba mana yang diterapkan dengan menggunakan metode recognize
dalam blok konfigurasi amuba.
kelas Posting < ActiveRecord::Base has_one :config has_many :komentar has_and_belongs_to_many :tag amuba tidak mengenali [:has_one, :has_and_belongs_to_many] kelas endend Komentar < ActiveRecord::Base milik_to :postendclass Tag < ActiveRecord::Base has_and_belongs_to_many :postsend
Contoh ini akan menyalin data konfigurasi postingan dan tetap mengaitkan tag dengan postingan baru, namun tidak akan menyalin komentar postingan karena amuba hanya akan mengenali dan menyalin turunan dari asosiasi has_one
dan has_and_belongs_to_many
dan dalam contoh ini, komentar bukan merupakan asosiasi has_and_belongs_to_many
.
Jika Anda ingin mencegah bidang biasa (yang tidak berbasis asosiasi has_*
) mempertahankan nilainya saat disalin, Anda dapat "menghilangkan" atau "membatalkan" bidang tersebut, seperti ini:
topik kelas < ActiveRecord::Base has_many :postsendclass Posting < ActiveRecord::Base milik_untuk :topik has_many :komentar amoeba doenablenullify :date_publishednullify :topic_id kelas endend Komentar < ActiveRecord::Base milik_untuk :postend
Contoh ini akan menyalin semua komentar postingan. Ini juga akan membatalkan tanggal penerbitan dan memisahkan postingan dari topik aslinya.
Tidak seperti gaya inklusif dan eksklusif, menentukan kolom null tidak akan secara otomatis mengaktifkan amuba untuk menyalin semua catatan anak. Seperti halnya objek rekaman aktif, nilai bidang default akan digunakan sebagai ganti nil
jika ada nilai default pada migrasi.
Jika Anda hanya ingin menyetel bidang ke nilai arbitrer pada semua objek duplikat, Anda dapat menggunakan direktif set
. Misalnya, jika Anda ingin menyalin objek yang memiliki semacam proses persetujuan yang terkait dengannya, Anda mungkin ingin mengatur status objek baru menjadi terbuka atau "sedang berlangsung" lagi.
kelas Posting < ActiveRecord::Base dosis amuba :state_tracker => "open_for_editing" akhir
Dalam contoh ini, ketika sebuah postingan diduplikasi, kolom state_tracker
akan selalu diberi nilai open_for_editing
untuk memulai.
Anda dapat menambahkan string ke awal bidang objek yang disalin selama fase penyalinan:
kelas Posting < ActiveRecord::Base amoeba doenableprepend :title => "Salinan " akhir
Anda dapat menambahkan string ke akhir bidang objek yang disalin selama fase penyalinan:
kelas Posting < ActiveRecord::Base amoeba doenableappend :title => "Salinan " akhir
Anda dapat menjalankan kueri pencarian dan penggantian pada bidang objek yang disalin selama fase penyalinan:
kelas Posting < ActiveRecord::Base amoeba doenableregex :contents => {:replace => /dog/, :with => 'cat'} akhir
Anda dapat menjalankan metode atau metode khusus untuk melakukan apa pun yang Anda suka, cukup teruskan blok lambda, atau larik blok lambda ke direktif customize
. Setiap blok harus mempunyai bentuk yang sama, artinya setiap blok harus menerima dua parameter yaitu objek asli dan objek yang baru disalin. Anda kemudian dapat melakukan apa pun yang Anda inginkan, seperti ini:
kelas Posting < ActiveRecord::Base amoeba doprepend :title => "Halo dunia! "customize(lambda { |original_post,new_post| if original_post.foo == "bar"new_post.baz = "qux" end})append :comments => "... tahu apa maksudku?" akhir
atau ini, menggunakan array:
kelas Posting < ActiveRecord::Base has_and_belongs_to_many :tag amoeba doinclude_association :tagscustomize([ lambda do |orig_obj,copy_of_obj|# barang bagus lainnya ada di sini akhir, lambda do |orig_obj,copy_of_obj|# barang bagus lainnya ada di sini akhir]) akhir
Blok Lambda diteruskan untuk menjalankan penyesuaian, secara default, setelah semua penyalinan dan pra-pemrosesan bidang. Jika Anda ingin menjalankan metode sebelum penyesuaian atau pra-pemrosesan bidang apa pun, Anda dapat menggunakan override
sepupu dari customize
. Cara penggunaannya sama seperti di atas.
kelas Posting < ActiveRecord::Base amoeba doprepend :title => "Halo dunia! "override(lambda { |original_post,new_post| if original_post.foo == "bar"new_post.baz = "qux" end})append :comments => "... tahu apa maksudku?" akhir
Anda dapat menerapkan satu praprosesor ke beberapa bidang sekaligus.
kelas Posting < ActiveRecord::Base amoeba doenableprepend :title => "Salinan", :contents => "Isi yang disalin: " akhir
Anda dapat menerapkan beberapa arahan prapemrosesan ke satu model sekaligus.
kelas Posting < ActiveRecord::Base amoeba doprepend :title => "Salinan ", :contents => "Isi asli: "append :contents => " (versi salinan)"regex :contents => {:replace => /dog/, :with => ' kucing'} akhir
Contoh ini akan menghasilkan sesuatu seperti ini:
posting = Posting.buat( :title => "Halo dunia", :contents => "Aku suka anjing, anjing itu keren.")new_post = post.amoeba_dupnew_post.title # "Salinan Hello world"new_post.contents # "Isi asli: Aku suka kucing, kucing itu keren. (versi salinan)"
Seperti nullify
, arahan prapemrosesan tidak secara otomatis mengaktifkan penyalinan catatan anak terkait. Jika hanya arahan pra-pemrosesan yang digunakan dan Anda ingin menyalin catatan anak dan tidak ada daftar include_association
atau exclude_association
yang disediakan, Anda masih harus secara eksplisit mengaktifkan penyalinan catatan anak dengan memanggil metode aktifkan dari dalam blok amoeba pada model Anda.
Anda dapat menggunakan kombinasi metode konfigurasi dalam setiap blok amuba model. Jenis asosiasi yang diakui lebih diutamakan daripada daftar penyertaan atau pengecualian. Gaya inklusif lebih diutamakan daripada gaya eksklusif, dan kedua gaya eksplisit ini lebih diutamakan daripada gaya sembarangan. Dengan kata lain, jika Anda membuat daftar bidang yang akan disalin, amuba hanya akan menyalin bidang yang Anda daftarkan, atau hanya menyalin bidang yang tidak Anda kecualikan, tergantung kasusnya. Selain itu, jika jenis bidang tidak dikenali maka jenis bidang tersebut tidak akan disalin, terlepas dari apakah jenis bidang tersebut muncul dalam daftar penyertaan. Jika Anda ingin amoeba menyalin semua catatan anak Anda secara otomatis, jangan cantumkan bidang apa pun menggunakan include_association
exclude_association
.
Contoh sintaksis berikut ini benar-benar valid, dan akan menghasilkan penggunaan gaya inklusif. Urutan pemanggilan metode konfigurasi dalam blok amoeba tidak menjadi masalah:
topik kelas < ActiveRecord::Base has_many :postsendclass Posting < ActiveRecord::Base milik_untuk :topik has_many :komentar memiliki_banyak :tag has_many :penulis amoeba doexclude_association :authorsinclude_association :tagsnullify :date_publishedprepend :title => "Salinan dari "append :contents => " (versi yang disalin)"regex :contents => {:replace => /dog/, :with => 'cat'}include_association :authorsenablenullify :topic_id kelas endend Komentar < ActiveRecord::Base milik_untuk :postend
Contoh ini akan menyalin semua tag dan penulis postingan, namun tidak menyalin komentarnya. Ini juga akan membatalkan tanggal penerbitan dan memisahkan postingan dari topik aslinya. Ini juga akan melakukan praproses pada kolom postingan seperti pada contoh prapemrosesan sebelumnya.
Perhatikan bahwa, karena diutamakan, gaya inklusif digunakan dan daftar bidang pengecualian tidak pernah dikonsultasikan. Selain itu, metode enable
berlebihan karena amuba diaktifkan secara otomatis saat menggunakan include_association
.
Arahan prapemrosesan dijalankan setelah rekaman anak disalin dan dijalankan dalam urutan ini.
Bidang nol
Ditambahkan
Menambahkan
Cari dan Ganti
Arahan pra-pemrosesan tidak memengaruhi daftar penyertaan dan pengecualian.
Anda dapat menyebabkan amuba terus menyalin rantai sejauh yang Anda suka, cukup tambahkan blok amuba ke setiap model yang ingin Anda salin turunannya. Amoeba akan secara otomatis muncul kembali menjadi cucu yang diaktifkan dan menyalinnya juga.
kelas Posting < ActiveRecord::Base has_many :komentar amuba bisa dilakukan kelas endend Komentar < ActiveRecord::Base milik_untuk :posting has_many : peringkat amuba bisa dilakukan Peringkat kelas akhir < ActiveRecord::Base milik_ke: komentar akhir
Dalam contoh ini, ketika sebuah postingan disalin, amuba akan menyalin seluruh komentar postingan dan juga akan menyalin peringkat setiap komentar.
Menggunakan has_one :through
asosiasi itu sederhana, pastikan untuk mengaktifkan amoeba pada setiap model dengan asosiasi has_one
dan amoeba akan menelusuri secara otomatis dan rekursif, seperti:
Pemasok kelas < ActiveRecord::Base has_one :akun has_one :sejarah, :melalui => :akun amuba bisa dilakukan Akun kelas endend < ActiveRecord::Base milik_untuk :pemasok has_one :sejarah amuba bisa dilakukan Riwayat kelas akhir < ActiveRecord::Base milik_ke :accountend
Menyalin has_many :through
asosiasi berfungsi secara otomatis. Mereka melakukan penyalinan dengan cara yang sama seperti asosiasi has_and_belongs_to_many
, yang berarti catatan anak sebenarnya tidak disalin, namun asosiasi tersebut dipertahankan begitu saja. Anda dapat menambahkan beberapa praprosesor lapangan ke model tengah jika Anda mau, tetapi ini tidak sepenuhnya diperlukan:
Majelis kelas < ActiveRecord::Base has_many :manifestasi has_many :bagian, :melalui => :manifests amuba bisa dilakukan Manifes kelas endend < ActiveRecord::Base milik_untuk :perakitan milik_untuk :bagian amoeba doprepend :catatan => "Salinan " Bagian endendclass < ActiveRecord::Base has_many :manifestasi has_many :assemblies, :through => :manifests amuba bisa dilakukan akhir
Anda dapat mengontrol bagaimana amuba menyalin objek Anda, dengan cepat, dengan meneruskan blok konfigurasi ke metode amuba model. Metode konfigurasinya statis tetapi konfigurasi diterapkan per instans.
kelas Posting < ActiveRecord::Base has_many :komentar amoeba doenableprepend :title => "Salinan " kelas endend Komentar < ActiveRecord::Base milik_to :postendclass PostsController < ActionController def duplikat_a_postold_post = Post.create( :title => "Halo dunia", :contents => "Lorum ipsum")old_post.class.amoeba do prepend :contents => "Ini salinannya: "endnew_post = old_post.amoeba_dupnew_post.title # seharusnya "Salinan Hello world"new_post.contents # seharusnya "Ini salinannya: Lorum ipsum"new_post.simpan akhir
Jika Anda menggunakan Warisan Tabel Tunggal yang disediakan oleh ActiveRecord, Anda dapat menyebabkan amuba memproses kelas anak secara otomatis dengan cara yang sama seperti orang tuanya. Yang perlu Anda lakukan hanyalah memanggil metode propagate
dalam blok amoeba dari kelas induk dan semua kelas anak harus menyalin dengan cara yang sama.
buat_tabel :produk, :force => true lakukan |t| t.string :type # ini adalah kolom STI # ini milik semua produk t.string :judul t.desimal :harga # ini hanya untuk kaos saja t.desimal :panjang_lengan t.desimal :ukuran_kerah # ini hanya untuk komputer t.integer :ram_size t.integer :hard_drive_sizeendclass Produk < ActiveRecord::Base has_many :gambar has_and_belongs_to_many :kategori amoeba doenablepropagate Kemeja endendclass < Komputer Productendclass < Productendclass ProductsController def some_methodmy_shirt = Shirt.find(1)my_shirt.amoeba_dupmy_shirt.save# kemeja ini sekarang harus:# - memiliki salinan sendiri dari semua gambar induk# - berada dalam kategori yang sama dengan induknya akhir
Contoh ini harus menduplikasi semua gambar dan bagian yang terkait dengan Kaos ini, yang merupakan turunan dari Produk
Secara default, propagasi menggunakan pola asuh yang tunduk, yang berarti pengaturan konfigurasi pada induk akan diterapkan, namun pengaturan anak apa pun, jika ada, akan menambah atau menimpa pengaturan induk tergantung pada bagaimana Anda memanggil metode DSL.
Anda dapat mengubah perilaku ini, yang disebut "gaya pengasuhan", untuk memberikan preferensi pada pengaturan orang tua atau mengabaikan semua pengaturan anak.
Gaya pengasuhan :relaxed
akan lebih memilih pengaturan orang tua.
kelas Produk < ActiveRecord::Base has_many :gambar has_and_belongs_to_many :bagian amoeba doexclude_association :imagespropagate :santai Kemeja endendclass < Produk include_association :gambar include_association :bagian tambahkan :title => "Salinan dari "end
Dalam contoh ini, setelan include_association
yang bertentangan pada turunan akan diabaikan dan setelan exclude_association
induk akan digunakan,