関連付けや、関連付けと属性に基づくいくつかの操作を含む、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 (単一テーブル継承) の子をサポートします。
包括的、排他的、無差別 (別名すべてコピー) などの複数の構成スタイル。
多対多レコードの子のクローン作成、または単に元の関連付けを維持することをサポートします。
自動ドリルダウン、つまり子レコードと孫レコードの再帰的コピーをサポートします。
ビジネス ロジックのニーズに応じて、一意性を示し、データの整合性を確保するためにフィールドの前処理をサポートします (たとえば、「コピー」または類似のテキストを先頭に追加します)。
カスタム ラムダ ブロックを使用したフィールドの前処理をサポートしているため、たとえば、コピーの作成中にカスタム ロジックが必要な場合など、基本的にやりたいことを何でも行うことができます。
Ameba は、コピーされたレコードのフィールドに対して次の前処理操作を実行できます。
セット
先頭に追加する
追加する
無効化する
カスタマイズ
正規表現
ご想像のとおりであることを願っています:
gem インストールアメーバ
または、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 である必要があります
デフォルトでは、有効にすると、アメーバは関連付けられたすべての子レコードを自動的にコピーし、新しい親レコードに関連付けます。
リストしたフィールドのみを含めるか、除外しないフィールドのみを含めるように動作を構成できます。 3 つの中で、最もパフォーマンスが高いのは無差別スタイルで、次に包括的スタイルが続きます。排他的スタイルは、各フィールドで追加の明示的なチェックが必要なため、最も遅くなります。このパフォーマンスの違いはおそらく無視できる程度であるため、読み書きが最も簡単であることに基づいて使用するスタイルを選択できます。ただし、データ ツリーが十分に大きく、コピーされるフィールドを制御する必要がある場合は、包括的なスタイルの方がおそらく優れています。排他的なスタイルよりも選択。
これらの例は、現実世界のシナリオの大まかな近似にすぎず、特に現実的ではない可能性があることに注意してください。機能の使用法を示すことのみを目的としています。
これは最も基本的な使用例であり、既知の関連付けをコピーできるだけです。
次のようなブログのモデルがあるとします。
クラス Post < ActiveRecord::Base has_many :commentsendclass コメント < ActiveRecord::Base 所属先:postend
次のように、アメーバ構成ブロックをモデルに追加し、enable メソッドを呼び出して子レコードのコピーを有効にするだけです。
クラス Post < ActiveRecord::Base has_many :コメントあり アメーバ可能 endclass コメント < ActiveRecord::Base 所属先:postend
amoeba_dup
メソッドを実行すると、子レコードが自動的にコピーされます。
一部の関連付けのみをコピーし、他の関連付けはコピーしたくない場合は、包括的なスタイルを使用できます。
クラス Post < ActiveRecord::Base has_many :コメントあり :タグがたくさんあります has_many :著者 アメーバ doenableinclude_association :tagsinclude_association :authors endclass コメント < ActiveRecord::Base 所属先:postend
アメーバ ブロック内で包含スタイルを使用することは、実際にはアメーバを有効にすることを意味するため、enable メソッドを実行する必要はありませんが、害はありません。
クラス Post < ActiveRecord::Base has_many :コメントあり :タグがたくさんあります has_many :著者 アメーバ doinclude_association :tagsinclude_association :authors endclass コメント < ActiveRecord::Base 所属先:postend
配列を渡すことで、コピーするフィールドを指定することもできます。単一の値を指定してinclude_association
呼び出すと、その値は既に含まれているフィールドのリストに追加されます。配列を渡すと、配列によって元の値が上書きされます。
クラス Post < ActiveRecord::Base has_many :コメントあり :タグがたくさんあります has_many :著者 アメーバ doinclude_association [:tags, :authors] endclass コメント < ActiveRecord::Base 所属先:postend
これらの例では、投稿のタグと作成者はコピーされますが、コメントはコピーされません。
包括的スタイルを使用すると、以前に選択されていた他のスタイルが自動的に無効になります。
除外するフィールドよりも含めるフィールドの方が多い場合は、排他的なスタイルを使用して入力と読み取りの作業を短縮することができます。明示的に除外されていないフィールドはすべてコピーされます。
クラス Post < ActiveRecord::Base has_many :コメントあり :タグがたくさんあります has_many :著者 アメーバ doexclude_association :コメント endclass コメント < ActiveRecord::Base 所属先:postend
この例は、包括的なスタイルの例と同じことを行います。投稿のタグと作成者はコピーされますが、コメントはコピーされません。包含スタイルの場合と同様、除外するフィールドを指定するときにアメーバを明示的に有効にする必要はありません。
排他的スタイルを使用すると、以前に選択されていた他のスタイルが自動的に無効になります。そのため、包含フィールドを選択し、その後いくつかの除外フィールドを選択した場合、 exclude_association
メソッドは以前に選択した包含スタイルを無効にし、対応する包含フィールドをすべて消去します。 。
また、包含関係または除外関係に追加の条件をパスする必要がある場合は、メソッド名を:if
オプションにパスすることができます。
クラス Post < ActiveRecord::Base has_many :コメントあり :タグがたくさんあります amoeba doinclude_association :comments、if: :popular? 終わり 間違いなく人気?いいね > 15 終わる
Post.first.amoeba_dup
を呼び出した後、 likes
が 15 より大きい場合は、すべてのコメントも複製されますが、別の状況では、リレーションは複製されません。 exclude_association
についても同じ動作になります。
注意してください!次のように書いた場合:
クラス Post < ActiveRecord::Base has_many :コメントあり :タグがたくさんあります アメーバ doexclude_association :tagsinclude_association :comments、if: :popular? 終わり 間違いなく人気?いいね > 15 終わる
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 所属先:ウィジェット 所属先 :postendclass ウィジェット < ActiveRecord::Base has_many :post_widgets has_many :posts, :through => :post_widgetsend
この例では、実際にデータベース内の警告とウィジェットを複製します。データベース内に元々 3 つの警告があった場合、投稿を複製すると、データベース内に 6 つの警告が存在することになります。これは、新しい投稿が以前の既存の警告に再関連付けされるだけで、それらの警告自体は複製されないデフォルトの動作とは対照的です。
デフォルトでは、アメーバは次の関連付けタイプの子を認識し、コピーしようとします。
1つあります
たくさん持っています
多くのものを持っており、多くの人に属しています
アメーバ設定ブロック内のrecognize
メソッドを使用して、アメーバがどの関連付けタイプに適用されるかを制御できます。
クラス Post < ActiveRecord::Base has_one :config has_many :コメントあり 多くの人に所属している:タグ アメーバは [:has_one, :has_and_belongs_to_many] を認識します endclass コメント < ActiveRecord::Base 所属先 :postendclass タグ < ActiveRecord::Base has_and_belongs_to_many :postsend
この例では、投稿の構成データをコピーし、新しい投稿に関連付けられたタグを保持しますが、投稿のコメントはコピーしません。これは、アメーバがhas_one
およびhas_and_belongs_to_many
関連付けの子のみを認識してコピーするためであり、この例では、コメントはhas_and_belongs_to_many
関連付けではないためです。
通常の ( has_*
関連ベースではない) フィールドがコピー時にその値を保持しないようにしたい場合は、次のようにフィールドを「ゼロにする」か「無効にする」ことができます。
クラス トピック < ActiveRecord::Base has_many :postsendclass Post < ActiveRecord::Base 所属先:トピック has_many :コメントあり アメーバ doenablenullify :date_publishednullify :topic_id endclass コメント < ActiveRecord::Base 所属先:postend
この例では、投稿のコメントをすべてコピーします。また、公開日が無効になり、投稿が元のトピックから切り離されます。
包括的スタイルや排他的スタイルとは異なり、null フィールドを指定しても、アメーバがすべての子レコードをコピーできるようには自動的にはなりません。他のアクティブ レコード オブジェクトと同様、移行時にデフォルト値が存在する場合はnil
の代わりにデフォルトのフィールド値が使用されます。
複製されたすべてのオブジェクトのフィールドを任意の値に設定したい場合は、 set
ディレクティブを使用できます。たとえば、何らかの承認プロセスが関連付けられているオブジェクトをコピーしたい場合は、新しいオブジェクトの状態をオープンまたは「進行中」に再度設定することができます。
クラス Post < ActiveRecord::Base アメーバ doset :state_tracker => "編集用に開く" 終わる
この例では、投稿が複製されると、その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'} 終わる
カスタム メソッドを実行して、基本的に好きなことを行うことができます。単にラムダ ブロックまたはラムダ ブロックの配列をcustomize
ディレクティブに渡すだけです。各ブロックは同じ形式でなければなりません。つまり、各ブロックは元のオブジェクトと新しくコピーされたオブジェクトという 2 つのパラメーターを受け入れる必要があります。次に、次のように好きなことを実行できます。
クラス Post < ActiveRecord::Base amoeba doprepend :title => "Hello world! "customize(lambda { |original_post,new_post| iforiginal_post.foo == "bar"new_post.baz = "qux" end})append :comments => "...知っていること私が言ってるの?」 終わる
または、配列を使用して次のようにします。
クラス Post < ActiveRecord::Base 多くの人に所属している:タグ amoeba doinclude_association :tagscustomize([ lambda do |orig_obj,copy_of_obj|# 良いものはここにあります end, lambda do |orig_obj,copy_of_obj|# 良いものはここにあります end]) 終わる
カスタマイズに渡される Lambda ブロックは、デフォルトでは、すべてのコピーとフィールドの前処理の後に実行されます。カスタマイズまたはフィールドの前処理の前にメソッドを実行したい場合は、 customize
のいとこであるoverride
使用できます。使い方は上記と同じです。
クラス Post < ActiveRecord::Base amoeba doprepend :title => "Hello world! "override(lambda { |original_post,new_post| iforiginal_post.foo == "bar"new_post.baz = "qux" end})append :comments => "...知っていること私が言ってるの?」 終わる
単一のプリプロセッサを複数のフィールドに一度に適用できます。
クラス Post < ActiveRecord::Base amoeba doenableprepend :title => "のコピー", :contents => "コピーされたコンテンツ: " 終わる
複数の前処理ディレクティブを 1 つのモデルに一度に適用できます。
クラス Post < ActiveRecord::Base amoeba doprepend :title => "のコピー", :contents => "元のコンテンツ: "append :contents => " (コピーされたバージョン)"regex :contents => {:replace => /dog/, :with => '猫'} 終わる
この例では、次のような結果が得られます。
post = Post.create( :title => "こんにちは、世界", :contents => "私は犬が好きです、犬は素晴らしいです。")new_post = post.amoeba_dupnew_post.title # "Hello world のコピー"new_post.contents # "元のコンテンツ: 私は猫が好きです、猫は素晴らしいです。(コピー版)"
nullify
と同様、前処理ディレクティブは、関連付けられた子レコードのコピーを自動的に有効にしません。前処理ディレクティブのみが使用されており、子レコードをコピーする必要があり、 include_association
またはexclude_association
リストが提供されていない場合でも、モデルのアメーバ ブロック内からenable メソッドを呼び出して、子レコードのコピーを明示的に有効にする必要があります。
各モデルのアメーバ ブロック内で構成方法を組み合わせて使用できます。認識された関連付けタイプは、包含リストまたは除外リストよりも優先されます。包括的なスタイルは排他的なスタイルよりも優先され、これら 2 つの明示的なスタイルは無差別的なスタイルよりも優先されます。言い換えれば、コピーするフィールドをリストすると、アメーバはリストしたフィールドのみをコピーするか、場合によっては除外しないフィールドのみをコピーします。さらに、フィールド タイプが認識されない場合、包含リストに表示されるかどうかに関係なく、そのフィールド タイプはコピーされません。アメーバにすべての子レコードを自動的にコピーさせたい場合は、 include_association
またはexclude_association
使用してフィールドをリストしないでください。
次の構文例は完全に有効であり、包括的なスタイルが使用されます。アメーバ ブロック内で設定メソッドを呼び出す順序は重要ではありません。
クラス トピック < ActiveRecord::Base has_many :postsendclass Post < ActiveRecord::Base 所属先:トピック has_many :コメントあり :タグがたくさんあります has_many :著者 amoeba doexclude_association :authorsinclude_association :tagsnullify :date_publishedprepend :title => "「append :contents => " (コピーされたバージョン) のコピー」regex :contents => {:replace => /dog/, :with => 'cat'}include_association :authorsenablenullify :topic_id endclass コメント < ActiveRecord::Base 所属先:postend
この例では、投稿のタグと作成者をすべてコピーしますが、コメントはコピーしません。また、公開日が無効になり、投稿が元のトピックから切り離されます。また、前の前処理の例と同様に、投稿のフィールドも前処理します。
優先順位のため、包含スタイルが使用され、除外フィールドのリストは決して参照されないことに注意してください。さらに、 include_association
使用するとアメーバが自動的に有効になるため、 enable
メソッドは冗長です。
前処理ディレクティブは、子レコードがコピーされた後にこの順序で実行されます。
ヌルフィールド
先頭に追加
追加
検索と置換
前処理ディレクティブは、包含リストと除外リストには影響しません。
アメーバに好きなだけチェーンをコピーさせ続けることができます。その子をコピーしたい各モデルにアメーバ ブロックを追加するだけです。 Ameba は、有効になっている孫を自動的に再帰し、それらもコピーします。
クラス Post < ActiveRecord::Base has_many :コメントあり アメーバ可能 endclass コメント < ActiveRecord::Base 所属:投稿 has_many :評価 アメーバ可能 endendclass 評価 < ActiveRecord::Base 所属:コメント終了
この例では、投稿がコピーされると、アメーバは投稿のすべてのコメントをコピーし、各コメントの評価もコピーします。
has_one :through
関連付けの使用は簡単です。has_one has_one
付けを使用して各モデルでアメーバを有効にするだけで、アメーバは次のように自動的かつ再帰的にドリルダウンします。
クラス サプライヤー < ActiveRecord::Base has_one :アカウント has_one :履歴、:スルー => :アカウント アメーバ可能 endendclass アカウント < ActiveRecord::Base 所属:サプライヤー has_one :履歴 アメーバ可能 endendclass 履歴 < ActiveRecord::Base 所属先:アカウント
has_many :through
association のコピーは自動的に機能します。 has_and_belongs_to_many
関連付けと同じ方法でコピーを実行します。つまり、実際の子レコードはコピーされず、関連付けが単純に維持されます。必要に応じて、いくつかのフィールド プリプロセッサを中間モデルに追加できますが、これは厳密に必要なわけではありません。
クラス アセンブリ < ActiveRecord::Base has_many :マニフェスト has_many :parts, :through => :manifests アメーバ可能 endendclass マニフェスト < ActiveRecord::Base 所属:アセンブリ 所属:パート アメーバ doprepend :notes => "のコピー" endendclass パート < ActiveRecord::Base has_many :マニフェスト has_many :アセンブリ、:through => :マニフェスト アメーバ可能 終わる
モデルのアメーバ メソッドに構成ブロックを渡すことで、アメーバがオブジェクトをコピーする方法をオンザフライで制御できます。構成方法は静的ですが、構成はインスタンスごとに適用されます。
クラス Post < ActiveRecord::Base has_many :コメントあり amoeba doenableprepend :title => "のコピー" endclass コメント < ActiveRecord::Base 所属先 :postendclass PostsController < ActionController def copy_a_postold_post = Post.create( :title => "Hello world", :contents => "Lorum ipsum")old_post.class.amoeba do prepend :contents => "これがコピーです: "endnew_post = old_post.amoeba_dupnew_post.title # 「Hello world のコピー」である必要があります new_post.contents # 「ここにコピーがあります: Lorum」である必要がありますipsum「new_post.save」 終わる
ActiveRecord が提供する単一テーブル継承を使用している場合は、アメーバが親クラスと同じ方法で子クラスを自動的に処理するようにすることができます。親クラスのアメーバ ブロック内でpropagate
メソッドを呼び出すだけで、すべての子クラスが同様の方法でコピーする必要があります。
create_table :products, :force => true do |t| t.string :type # これは STI 列です # これらはすべての製品に属します t.string :タイトル t.10進数:価格 # これらはシャツのみです t.10 進数 :sleeve_length t.10 進数 :collar_size # これらはコンピュータ専用です t.integer :ram_size t.integer :hard_drive_sizeendclass 製品 < ActiveRecord::Base has_many :画像あり has_and_belongs_to_many :カテゴリー アメーバ実行可能伝播 endendclass Shirt < Productendclass Computer < Productendclass ProductsController def some_methodmy_shirt = Shirt.find(1)my_shirt.amoeba_dupmy_shirt.save# このシャツは次のようになります:# - すべての親画像の独自のコピーを持つ# - 親と同じカテゴリに属する 終わる
この例では、Product の子であるこの Shirt に関連付けられたすべての画像とセクションを複製する必要があります。
デフォルトでは、伝播では従順なペアレンティングが使用されます。つまり、親の構成設定が適用されますが、子の設定が存在する場合、DSL メソッドの呼び出し方法に応じて親の設定に追加または上書きされます。
この動作、いわゆる「ペアレンティング スタイル」を変更して、親の設定を優先したり、すべての子の設定を無視したりすることができます。
:relaxed
子育てスタイルでは、親の設定が優先されます。
クラス Product < ActiveRecord::Base has_many :画像あり has_and_belongs_to_many :セクション アメーバ doexclude_association :imagespropagate :relaxed endendclass シャツ < 製品 include_association :画像 include_association :セクション prepend :title => "のコピー" end
この例では、子の競合するinclude_association
設定は無視され、親のexclude_association
設定が使用されます。