輕鬆複製 active_record 對象,包括關聯以及關聯和屬性下的多個操作。
請參閱此處。
目標是能夠輕鬆快速地重現 ActiveRecord 物件(包括其子物件),例如複製部落格文章並維護其關聯的標籤或類別。
這種寶石被命名為“變形蟲”,因為變形蟲(小型生命形式)善於繁殖。他們的子孫也能快速、輕鬆地繁衍後代。
ActiveRecord 擴充 gem,允許在複製活動記錄模型時複製關聯的子記錄物件。
相容於 Rails 5.2、6.0、6.1。對於 Rails 4.2 到 5.1,請使用版本 3.x。
支援以下關聯類型
has_many
has_one :through
has_many :through
has_and_belongs_to_many
一個簡單的 DSL,用於配置要複製的欄位。 DSL 可以應用於您的導軌模型或即時使用。
支援 STI(單表繼承)子級繼承其父級變形蟲設定。
多種配置風格,例如包容性、排他性和無差別(又稱複製一切)。
支援克隆多對多記錄的子記錄或僅保留原始關聯
支援自動向下鑽取,即遞歸複製子記錄和孫記錄。
支援欄位預處理,以協助指示唯一性並根據您的業務邏輯需求確保資料的完整性,例如預先新增「副本」或類似文字。
支援使用自訂 lambda 區塊對欄位進行預處理,因此您基本上可以做任何您想做的事情,例如,如果您在製作副本時需要一些自訂邏輯。
Amoeba 可以對複製記錄的欄位進行以下預處理操作
放
前置
附加
無效化
客製化
正規表示式
希望如您所期望的:
寶石安裝阿米巴
或只是將其添加到您的 Gemfile 中:
寶石“阿米巴”
使用以下樣式之一配置您的模型,然後在您通常執行dup
方法的模型上執行amoeba_dup
方法:
p = Post.create(:title => "Hello World!", :content => "Lorum ipsum dolor")p.comments.create(:content => "我喜歡它!")p.comments.create(: content => "這太糟糕了!")puts Comment.all.count # 應該是2my_copy = p.amoeba_dupmy_copy.saveputs Comment.all.count # 應該是 4
預設情況下,啟用後,amoeba 將自動複製任何和所有關聯的子記錄並將它們與新的父記錄關聯。
您可以將行為配置為僅包含您列出的欄位或僅包含您不排除的欄位。在這三種風格中,表現最高的是不加區分的風格,其次是包容風格,而獨佔風格將是最慢的,因為需要對每個欄位進行額外的明確檢查。這種效能差異可能可以忽略不計,您可以根據最容易讀寫的方式選擇要使用的樣式,但是,如果您的資料樹足夠大並且您需要控制複製哪些字段,那麼包含式樣式可能是更好的選擇選擇而不是獨特的風格。
請注意,這些範例只是現實世界場景的鬆散近似,可能不是特別現實,它們僅用於演示功能使用的目的。
這是最基本的用例,將簡單地啟用任何已知關聯的複製。
如果您有一些類似這樣的部落格模型:
類別 Post < ActiveRecord::Base has_many :commentsendclass 註解 < ActiveRecord::Base 屬於:postend
只需將 amoeba 配置區塊新增到您的模型中並呼叫 enable 方法即可啟用子記錄的複製,如下所示:
類別 Post < ActiveRecord::Base 有_很多:評論 阿米巴原蟲可行 endendclass 註解 < ActiveRecord::Base 屬於:postend
當您執行amoeba_dup
方法時,子記錄將會自動複製。
如果您只想複製某些關聯而不複製其他關聯,則可以使用包含樣式:
類別 Post < ActiveRecord::Base 有_很多:評論 有_很多:標籤 has_many :作者 阿米巴 doenableinclude_association :tagsinclude_association :authors endendclass 註解 < ActiveRecord::Base 屬於:postend
在 amoeba 區塊中使用包容性樣式實際上意味著您希望啟用 amoeba,因此無需運行 enable 方法,儘管它也不會造成損害:
類別 Post < ActiveRecord::Base 有_很多:評論 有_很多:標籤 has_many :作者 阿米巴 doinclude_association :tagsinclude_association :作者 endendclass 註解 < ActiveRecord::Base 屬於:postend
您也可以透過傳遞陣列來指定要複製的欄位。如果您使用單一值呼叫include_association
,它將被附加到已包含欄位的清單中。如果您傳遞一個數組,您的數組將覆蓋原始值。
類別 Post < ActiveRecord::Base 有_很多:評論 有_很多:標籤 has_many :作者 阿米巴 doinclude_association [:標籤、:作者] endendclass 註解 < ActiveRecord::Base 屬於:postend
這些範例將複製貼文的標籤和作者,但不會複製其評論。
使用包容性樣式時,將自動停用先前選擇的任何其他樣式。
如果要包含的字段多於要排除的字段,您可能希望透過使用獨佔樣式來縮短所需的鍵入和閱讀量。所有未明確排除的欄位都將被複製:
類別 Post < ActiveRecord::Base 有_很多:評論 有_很多:標籤 has_many :作者 阿米巴 doexclude_association :評論 endendclass 註解 < ActiveRecord::Base 屬於:postend
此範例與包容性樣式範例執行相同的操作,它將複製貼文的標籤和作者,但不複製其評論。與包容性風格一樣,在指定要排除的欄位時無需明確啟用 amoeba。
使用獨佔樣式時,將自動停用先前選擇的任何其他樣式,因此如果您選擇了包含字段,然後選擇了一些排除字段,則exclude_association
方法將停用先前選擇的包含樣式並清除任何相應的包含字段。
此外,如果您需要為包含或排除關係指定額外條件,您可以將方法名稱指定為:if
選項。
類別 Post < ActiveRecord::Base 有_很多:評論 有_很多:標籤 阿米巴 doinclude_association :評論,如果: :受歡迎? 結尾 def 受歡迎? 結束
在調用Post.first.amoeba_dup
後,如果likes
大於 15,所有評論也會被複製,但在另一種情況下 - 不會克隆任何關係。 exclude_association
也有相同的行為。
請注意!如果你寫道:
類別 Post < ActiveRecord::Base 有_很多:評論 有_很多:標籤 amoeba doexclude_association :tagsinclude_association :評論,如果: :受歡迎? 結尾 def 受歡迎? 結束
不管結果如何popular?
方法調用(反向情況相同)。
如果您使用多對多關係,您可以告訴阿米巴實際上複製原始相關記錄,而不僅僅是維持與原始記錄的關聯。克隆很簡單,只需告訴阿米巴要克隆哪些字段,就像告訴阿米巴要包含或排除哪些字段一樣。
類別 Post < ActiveRecord::Base has_and_belongs_to_many :警告 has_many :post_widgets has_many :widgets, :through => :post_widgets 阿米巴 doenableclone [:小部件, :警告] endendclass 警告 < ActiveRecord::Base has_and_belongs_to_many :postsendclass PostWidget < ActiveRecord::Base 屬於:小工具 屬於_to :postendclass Widget < ActiveRecord::Base has_many :post_widgets has_many :posts, :through => :post_widgetsend
該範例實際上將複製資料庫中的警告和小部件。如果資料庫中最初有 3 個警告,那麼在複製貼文後,資料庫中最終會出現 6 個警告。這與預設行為相反,在預設行為中,您的新貼文只會與任何先前存在的警告重新關聯,而這些警告本身不會重複。
預設情況下,amoeba 識別並嘗試複製以下關聯類型的任何子級:
有一個
有很多
擁有並屬於許多
您可以使用 amoeba 配置區塊中的recognize
方法來控制 amoeba 應用到哪些關聯類型。
類別 Post < ActiveRecord::Base 有_一個:配置 有_很多:評論 has_and_belongs_to_many :標籤 變形蟲辨識 [:has_one, :has_and_belongs_to_many] endendclass 註解 < ActiveRecord::Base 屬於_to :postendclass 標籤 < ActiveRecord::Base has_and_belongs_to_many :postsend
此範例將複製帖子的配置資料並保留與新帖子關聯的標籤,但不會複製帖子的評論,因為 amoeba 只會識別和複製has_one
和has_and_belongs_to_many
關聯的子級,並且在此示例中,評論不是has_and_belongs_to_many
關聯。
如果您希望防止常規(非基於has_*
關聯)字段在複製時保留其值,您可以「清零」或「無效」該字段,如下所示:
類別主題 < ActiveRecord::Base has_many :postsendclass Post < ActiveRecord::Base 屬於:主題 有_很多:評論 阿米巴 doenablenullify :date_publishednullify :topic_id endendclass 註解 < ActiveRecord::Base 屬於:postend
此範例將複製帖子的所有評論。它還將使發布日期無效,並將該帖子與其原始主題分離。
與包含和獨佔樣式不同,指定空白欄位不會自動使amoeba 複製所有子記錄。與任何活動記錄物件一樣,如果遷移中存在預設值,則將使用預設欄位值而不是nil
。
如果您希望將所有重複物件上的欄位設為任意值,您可以使用set
指令。例如,如果您想要複製具有某種與之關聯的審批流程的對象,您可能會想要再次將新對象的狀態設為開啟或「正在進行」。
類別 Post < ActiveRecord::Base 阿米巴劑量組:state_tracker =>“open_for_editing” 結束
在此範例中,重複貼文時,它的state_tracker
欄位將始終被賦予open_for_editing
值以啟動。
您可以在複製階段將字串新增至複製物件欄位的開頭:
類別 Post < ActiveRecord::Base amoeba doenableprepend :title => "副本" 結束
您可以在複製階段將字串新增至複製物件欄位的結尾:
類別 Post < ActiveRecord::Base amoeba doenableappend :title => "副本" 結束
您可以在複製階段對複製物件的欄位執行搜尋和取代查詢:
類別 Post < ActiveRecord::Base 阿米巴 doenableregex :contents => {:replace => /dog/, :with => 'cat'} 結束
您可以執行一個或多個自訂方法來執行基本上您喜歡的任何操作,只需將 lambda 區塊或 lambda 區塊陣列傳遞給customize
指令即可。每個區塊必須具有相同的形式,這意味著每個區塊必須接受兩個參數,原始物件和新複製的物件。然後你可以做任何你想做的事,就像這樣:
類別 Post < ActiveRecord::Base amoeba doprepend :title => "Hello world! "customize(lambda { |original_post,new_post| if original_post.foo == "bar"new_post.baz = "qux" end})append :comments => "...知道什麼我說呢? 結束
或者這樣,使用陣列:
類別 Post < ActiveRecord::Base has_and_belongs_to_many :標籤 amoeba doinclude_association :tagscustomize([ lambda do |orig_obj,copy_of_obj|# 好東西放在這裡 end, lambda do |orig_obj,copy_of_obj|# 更多好東西放在這裡 end]) 結束
預設情況下,在所有複製和欄位預處理之後,Lambda 區塊會傳遞給自訂運行。如果您希望在任何自訂或欄位預處理之前運行一個方法,您可以使用override
的customize
方法。用法與上面相同。
類別 Post < ActiveRecord::Base amoeba doprepend :title => "Hello world!" override(lambda { |original_post,new_post| if original_post.foo == "bar"new_post.baz = "qux" end})append :comments => "...知道什麼我說呢? 結束
您可以一次將單一預處理器套用至多個欄位。
類別 Post < ActiveRecord::Base amoeba doenableprepend :title => "複製", :contents => "複製的內容:" 結束
您可以一次將多個預處理指令套用至單一模型。
類別 Post < ActiveRecord::Base amoeba doprepend :title => "複製", :contents => "原始內容: "append :contents => " (複製版本)"regex :contents => {:replace => /dog/, :with => '貓'} 結束
這個例子應該會產生如下結果:
發布 = Post.create( :title => "你好世界", :contents => "我喜歡狗,狗太棒了。")new_post = post.amoeba_dupnew_post.title # "Hello world 的副本"new_post.contents # "原始內容:我喜歡貓,貓太棒了。(複製版)"
與nullify
一樣,預處理指令不會自動啟用關聯子記錄的複製。如果僅使用預處理指令,且您確實想要複製子記錄,且未提供include_association
或exclude_association
列表,則仍必須透過從模型上的 amoeba 區塊內呼叫 enable 方法來明確啟用子記錄的複製。
您可以在每個模型的阿米巴區塊中使用配置方法的組合。已識別的關聯類型優先於包含或排除清單。包容風格優先於排他風格,這兩種顯式風格優先於不加區別的風格。換句話說,如果您列出要複製的字段,amoeba 將僅複製您列出的字段,或者僅複製您不排除的字段(視情況而定)。此外,如果某個欄位類型無法識別,則無論它是否出現在包含清單中,都不會複製該欄位類型。如果您希望 amoeba 自動複製所有子記錄,請不要使用include_association
或exclude_association
列出任何欄位。
以下範例語法完全有效,並且將導致使用包含樣式。在 amoeba 區塊中呼叫配置方法的順序並不重要:
類別主題 < ActiveRecord::Base has_many :postsendclass Post < ActiveRecord::Base 屬於:主題 有_很多:評論 有_很多:標籤 has_many :作者 。 include_association :authorsenablenullify :topic_id endendclass 註解 < ActiveRecord::Base 屬於:postend
此範例將複製貼文的所有標籤和作者,但不複製其評論。它還將使發布日期無效,並將該帖子與其原始主題分離。它還將像前面的預處理範例一樣預處理帖子的欄位。
請注意,由於優先順序的原因,使用包含樣式並且從不查閱排除欄位清單。此外, enable
方法是多餘的,因為使用include_association
時會自動啟用amoeba。
預處理指令在複製子記錄後運行,並按此順序運行。
空白字段
前置
追加
搜尋和替換
預處理指令不影響包含和排除清單。
您可以讓變形蟲繼續沿著鏈複製,只要您願意,只需將變形蟲塊添加到您希望複製其子代的每個模型中即可。 Amoeba 將自動遞歸到任何啟用的孫子級並複製它們。
類別 Post < ActiveRecord::Base 有_很多:評論 阿米巴原蟲可行 endendclass 註解 < ActiveRecord::Base 屬於:帖子 has_many :評級 阿米巴原蟲可行 endendclass 評級 < ActiveRecord::Base 屬於:評論結束
在此範例中,當複製貼文時,變形蟲將複製貼文的所有評論,並且還將複製每個評論的評分。
使用has_one :through
關聯很簡單,只需確保在具有has_one
關聯的每個模型上啟用 amoeba,amoeba 就會自動遞歸地向下鑽取,如下所示:
供應商類別 < ActiveRecord::Base 有_一個:帳戶 has_one :歷史記錄, :通過 => :帳戶 阿米巴原蟲可行 endendclass 帳號 < ActiveRecord::Base 所屬:供應商 有_一個:歷史 阿米巴原蟲可行 endendclass 歷史 < ActiveRecord::Base 屬於:帳戶結束
複製has_many :through
關聯會自動進行。它們以與has_and_belongs_to_many
關聯相同的方式執行複製,這意味著不會複製實際的子記錄,而是簡單地維護關聯。如果您願意,您可以向中間模型添加一些字段預處理器,但這並不是絕對必要的:
類別 Assembly < ActiveRecord::Base has_many :清單 has_many :零件, :通過 => :清單 阿米巴原蟲可行 endendclass 清單 < ActiveRecord::Base 屬於:程序集 屬於:部分 amoeba doprepend :notes => “副本” endendclass 部份 < ActiveRecord::Base has_many :清單 has_many :組件, :through => :清單 阿米巴原蟲可行 結束
您可以透過將配置區塊傳遞給模型的 amoeba 方法來控制 amoeba 如何動態複製物件。配置方法是靜態的,但配置是基於每個實例應用的。
類別 Post < ActiveRecord::Base 有_很多:評論 amoeba doenableprepend :title => "副本" endendclass 註解 < ActiveRecord::Base 屬於_to :postendclass PostsController < ActionController defplicated_a_postold_post = Post.create( :title => "Hello world", :contents => "Lorum ipsum")old_post.class.amoeba do prepend :contents => "這是一份副本:"endnew_post = old_post.amoeba_dup_post. #應該是「Hello world 的副本」new_post.contents # 應該是「這是一個副本:Lorum ipsum」new_post.save 結束
如果您使用ActiveRecord提供的單表繼承,您可能會導致amoeba以與父類別相同的方式自動處理子類別。您需要做的就是在父類別的amoeba 區塊中呼叫propagate
方法,所有子類別都應該以類似的方式複製。
create_table :products, :force => true do |t| t.string :type # 這是 STI 列 # 這些屬於所有產品 t.string :標題 t.小數:價格 # 這些僅適用於襯衫 t.decimal :sleeve_length t.decimal :collar_size # 這些僅適用於計算機 t.整數:ram_size t.integer :hard_drive_sizeendclass 產品 < ActiveRecord::Base 有_許多:圖像 has_and_belongs_to_many :類別 阿米巴原蟲可傳播 endendclass 襯衫 < Productendclass 電腦 < Productendclass ProductsController def some_methodmy_shirt = Shirt.find(1)my_shirt.amoeba_dupmy_shirt.save# 這件襯衫現在應該:# - 擁有所有父圖像的自己的副本# - 與父圖像屬於同一類別 結束
此範例應複製與這件襯衫相關的所有圖像和部分,該襯衫是 Product 的子項
預設情況下,傳播使用順從父級,這意味著將應用父級上的配置設置,但任何子級設置(如果存在)將添加到或覆蓋父級設置,具體取決於您調用 DSL 方法的方式。
您可以更改此行為,即所謂的“育兒風格”,以優先考慮父設定或忽略任何和所有子設定。
:relaxed
育兒風格會更喜歡父母的設定。
產品類別 < ActiveRecord::Base 有_許多:圖像 has_and_belongs_to_many :節 阿米巴 doexclude_association :imagespropagate :relaxed endendclass 襯衫 < 產品 include_association:圖像 include_association :部分 前置:標題=>「副本」結束
在此範例中,子層級上衝突的include_association
設定將被忽略,而父級的exclude_association
設定將被使用,