1

私はCharacterクラスと​​クラスを持っています。 1つの文字が別の「兄弟」で、私はcharacter_id(編集中の「主」の文字)でSiblingshipインスタンスを保存することを示すために代わりにリレーションクラスでafter_addメソッドを定義するにはどうすればよいですか?

sibling_id(文字が主要キャラクターの兄弟としてマークされています) 。

逆ID付きのインスタンスを自動的に作成する機能を追加したいとします(したがって、アリスをボブの兄弟としてマークした場合、ボブはアリスの兄弟としてマークします)。私は、文字クラスのロジックを置く場合

これは簡単after_add次のようになります。

class Character < ActiveRecord::Base 
    has_many :siblingships 
    has_many :siblings, through: :siblingships, after_add: :reciprocate 

    def reciprocate(sibling) 
    ... 
    end 
end 

しかし、これはこのような〜100の異なる関係を持つ大規模プロジェクトがある(ダース異なる「コンテンツ」のクラスを結びます、文字だけでなく)、after_add(そしてしばしば逆のafter_remove)をCharacterモデルに格納することは非常に扱いにくいものになります。

私はその関連を保持するクラスではなく、その関連に「このタイプの関連付けが作成されたときに何をするのか」のロジックを保存したいと思います。以下のような

何か:

class Character < ActiveRecord::Base 
    has_many :siblingships 
    has_many :siblings, through: :siblingships, after_add: Siblingship.reciprocate 
end 

はこれを行うには良い方法はありますか?私はまた、[(、0を指定した1を予想)引数の間違った番号を]のみがエラーん

has_many :siblings, through: :siblingships, after_add: Siblingship.new.reciprocate 

でもないように、インスタンスメソッドとしてreciprocateを指定しようとした方法で、それはまた間違って感じてきましたインスタンスメソッドに到達するためにここで兄弟姉妹をインスタンス化すること。

Characterクラスをきれいに保ち、好ましくはそのリレーションのクラス内の各リレーションについて論理を保持しながら、この問題を解決する方法については、非常に感謝します。

+0

私はあなたが何をしようとしているかは完全にはわかりませんが、after_addにはシンボルまたはlamba/procを渡す必要があることに注意してください。あなたの 'Siblingship.reciprocate'は、モデルクラスが定義された時に評価されます_ –

+0

私は、POROクラスを作成して全体を管理し、相互関係を作成することが推奨されます。なぜなら、これは「兄弟姉妹」や「兄弟」の関心事ではなく、これらの関係の創造を監督する「兄弟姉妹」のようなものによって管理されるべきであるからです。 – engineersmnky

+0

@engineersmnky保存コールバックをトリガーするのではなく、モデル(文字)の更新を含むサービスのようなものがありますか?理想的には、各関係のマネージャーを作成しない方がよいでしょう。そこには〜100人がいるからです。 – drusepth

答えて

0

これは、参加する関連クラス(例:Character)に追加するのではなく、参加モデルの単純なフック(たとえば​​)で解決できるようです。

コードは、コードの重複を最小限に抑えて無制限の参加クラスを対象としたソリューションが必要だったため、私が質問で示した具体的な例から少し抽象化されています。各参加クラス、そして

class Character < ActiveRecord::Base 
    has_many :siblingships 
    has_many :siblings, through: :siblingships 
end 

そして:ここで私は2ウェイ(作成および削除の両方)のリンクを取得するためにやってしまったものだ作業

class Siblingship < ActiveRecord::Base 
    include SmartContentLinking 
    LINK_TYPE = :two_way 

    belongs_to :character 
    belongs_to :sibling, class_name: 'Character' 

    # Since this is a two-way relation, also create an opposite relation 
    after_create do 
    self.reciprocate relation: :siblingships, parent_object_ref: :character, added_object_ref: :sibling 
    end 

    # Since this is a two-way relation, also delete any opposite relation 
    after_destroy do 
    this_object = Character.find_by(id: self.character_id) 
    other_object = Character.find_by(id: self.sibling_id) 

    other_object.siblings.delete this_object 
    end 
end 

は明らかに引くべき事が残っていますコードをもっときれいにする(SmartContentLinkingの懸念事項に完全に抽象化されている)が、これは今のところうまくいく。

SmartContentLinkingの懸念:

require 'active_support/concern' 
module SmartContentLinking 
    extend ActiveSupport::Concern 

    # Default linking to one-way. All possible values: 
    # - :one_way 
    # - :two_way 
    LINK_TYPE = :one_way 

    included do 
    def reciprocate relation:, parent_object_ref:, added_object_ref: 
     parent_object = self.send(parent_object_ref) 
     added_object = self.send(added_object_ref) 

     # if some_character.siblingships.pluck(:sibling_id).include?(parent_object.id) 
     if added_object.send(relation).pluck("#{added_object_ref}_id").include?(parent_object.id) 
     # Two-way relation already exists 
     else 
     # If a two-way relation doesn't already exist, create it 
     added_object.send(relation) << relation.to_s.singularize.camelize.constantize.create({ 
      "#{parent_object_ref}": added_object, # character: sibling 
      "#{added_object_ref}": parent_object # sibling: character 
     }) 
     end 
    end 
    end 
end 
0

のは、ActiveRecordのを無視して、あなたが達成しようとしているものを見てみましょう。

キャラクタに新しい兄弟が追加されている場合、兄弟の既存の 兄弟関係を再整列する必要があります。

あなたは〜100の同様の関係を持つプロジェクトだと言いました。だから、我々は上記を一般化可能性:新たな関係が形成されている場合

、両方 サイドの既存の関係を再編成する必要があります。

残りのドメインがわからないため、最初の定義で作業します。この例は、ドメインの理解度に基づいて変更できます。

定義に基づいて、コードのようなものになります。

class Character 
    def add_sibling_relationship(another_character) 
    siblings.add(another_character) 
    another_character.realign_sibling_relationships(self) 
    end 

    def realign_sibling_relationships(sibling) 
    siblings.add(sibling) 
    end 
end 

をすべては関係が育つたら問題になるだろうされ、Characterにまだあります。ですから、プロセスをActiveRecordモデルから切り離してみましょう。それは、我々は結果のクラスと呼ぶことにしますものですので、私たちは、ここでsiblinghood話している

class Siblinghood 
    def initialize(sibling_one, sibling_two) 
    @sibling_one, @sibling_two = sibling_one, sibling_two 
    end 

    def form 
    @sibling_one.siblings << @sibling_two unless @sibling_one.siblings.include?(@sibling_two) 
    @sibling_two.siblings << @sibling_one unless @sibling_two.siblings.include?(@sibling_one) 
    end 
end 

これは、私たちが兄弟を作成する方法を変更します。代わりの

@character.siblings.add(@other_character) 

我々は今、呼び出す必要があります:

Siblinghood.new(@character, @other_character).form 

するのは、さらに一歩それを見てみましょう。この兄弟関係を取り除きたいのであれば、その論理を置く場所はどこでしょうか?

Siblinghood.new(@character, @other_character).destroy 

ここで、兄弟姉妹を管理するロジックを統合するところがあります。しかし、これは私たちのコントローラと他のクラスが、兄弟がどのように形成されるかについて他のクラスが気にするものではない、Siblinghoodを意識する必要があることを意味します。

呼び出しをCharacterに戻しましょう。

class Character 
    def add_sibling(other_character) 
    Siblinghood.new(self, other_character).form 
    end 

    def remove_sibling(other_character) 
    Siblinghood.new(self, other_character).destroy 
    end 
end 

これは私たちに素敵なバランスを提供します - 外部のクラスを知っている必要があり、管理兄弟のために必要なすべてのロジックがきちんと隠れているようCharacterはできるだけ多くのメソッドを実装します。

関連する問題