1

Q:親クラスに関するネストされたクラスの情報を共有する方法

class Post
  MODEL = self
  extend PostMod
  has_many :comments, :class_name => 'Post::Comment'

  class Comment
    include CommentMod
    belongs_to :model, :class_name => "Post"
    def method_in_comment
      puts MODEL
    end
  end
  class Another

  end
end
module  CommentMod
  def method_in_mod1
    puts self.class::MODEL
  end
  def method_in_mod2
    puts MODEL
  end
end
module  PostMod
  def method_in_mod1
    puts self::Comment
  end
end
b = Post::Comment.new
b.method_in_comment # Post
b.method_in_mod1 # uninitialized constant Post::Comment::MODEL (NameError)
b.method_in_mod2 # uninitialized constant CommentMod::MODEL (NameError)

この例(実際のシステムははるかに複雑です)のこのような設計の理由は、「モジュールを含める」だけで任意のモデルにコメントを追加することです。これにより、コントローラー、ビュー、モデルメソッドが追加されます

コメントの動作は、すべてのモデルで同様です。ただし、何かを調整する必要がある場合、モデルはCommentクラスのメソッドをオーバーライドできます。

これのトリッキーな部分は、モジュールがトップクラス(Post)と同じレベルのクラス(Comment and Another)について何も知らないということですが、それらのモジュールでいくつかのクラスメソッドを呼び出す必要があります。

現在、class.name解析を使用して、最上位クラスのクラス名を取得していますが、別の方法があるはずです。

デザインの変更など、どんな提案でも大歓迎です。

アップデート

投稿とコメントはほんの一例です。私のプロジェクトにはこのモデルはありません。

アンダースコアなし(またはCamelCase)からネストされたクラス(ArticleTranslationからArticle :: Translation)に移行しています。それは私にはもっとはっきりしているように見えます。以前のバージョンでは、モデル名を使用してクラスのクラスメソッドを呼び出していました(ModelTranslationなど)。これで、libモジュールをリファクタリングした後、model_nameを知る必要がなくなりました。

しかし、私は罠に陥りました。ルビーでは、次のようなクラスを再開できます。

class Post < ActiveRecord::Base

end
class Post::Comment < ActiveRecord::Base
  belongs_to  :language
end

class Post::Comment::Another1 < ActiveRecord::Base

end

class Post::Comment::Another2 < ActiveRecord::Base

end

class Language < ActiveRecord::Base
  has_many :post_comments, :class_name => "Post::Comment"
end

そして私は問題を抱えています:サーバーの起動直後にページがロードされた場合-すべてOKですが、そのページへの次の呼び出しはエラーをスローします:そのような関連付けはありません。含まれているモジュールメソッドの呼び出し-メソッドエラーはありません。railsがPost::Comentに対して間違ったファイルをロードすると思いますが、最悪の場合、このエラーをデバッグできません...しかし、これは別の質問です。

UPDATE2

2番目の問題は解決しました。問題はヘルパークラスにありました。

4

3 に答える 3

1

「「モジュールを含める」だけで、どのモデルにもコメントを追加できる」ということを明確にできますか?ActiveRecordモデルのコメントがあり、それを他の多くのモデルに多形的に関連付けたいということですか?

Post
  has_many :comments

Article
 has_many :comments

...等?

于 2011-02-09T23:04:19.253 に答える
1

したがって、後のコメントに基づいて、Module.extendedを使用した後の内容に少し似ています。

module PostMod
  def self.extended(klass)
    klass.send :include, InstanceMethods
  end

  module InstanceMethods
    def method_in_mod1
      puts "from PostMod#method_in_mod1: #{self.class.inspect}"
    end
  end
end

module CommentMod
  def self.extended(klass)
    klass.send :include, InstanceMethods
  end

  module InstanceMethods
    def method_in_mod1
      puts "from CommentMod#method_in_mod1: #{self.class.inspect}"
    end

    def method_in_mod2
      puts "from CommentMod#method_in_mod2: #{self.class.ancestors.inspect}"
    end
  end
end

class Post
  extend PostMod

  class Comment
    extend CommentMod

    def method_in_comment
      puts "from Post::Comment#method_in_comment: #{self.class.inspect}"
    end
  end

  class Another

  end
end


b = Post::Comment.new
b.method_in_comment # Post::Comment
b.method_in_mod1 # Post::Comment
b.method_in_mod2 # [Post::Comment, CommentMod::InstanceMethods, Object, Kernel]

出力はあなたの例と完全に同じではありませんが。実際には継承を使用しておらず、ネストされたクラスのみを使用しているため、試行している方法でクラス変数を共有するのは難しい場合があります。

それらの変数を共有する意図を詳しく説明できますか?おそらく、目標を達成するためのより良い方法がありますか?

于 2011-02-10T17:28:06.140 に答える
1

私はコードをリファクタリングすることで問題を自分で解決しました。したがって、class.nameを共有する必要はありません。これが必要な場合は、class.name.scanまたはmatchesを使用してトップレベルのクラス名を取得し、コードをリファクタリングしてclass.name.scanを削除します=)クラス名を共有することはお勧めできません。

于 2011-02-10T23:26:51.917 に答える