2012-02-22 7 views
6

私は、モジュールを含むクラスからアクセスできないメソッドを、モジュール内に持たせたいと思っています。次の例を考える:含まれているモジュールメソッドをどのようにしてRubyにカプセル化しますか?

class Foo 
    include Bar 

    def do_stuff 
    common_method_name 
    end 
end 

module Bar 
    def do_stuff 
    common_method_name 
    end 

    private 
    def common_method_name 
    #blah blah 
    end 
end 

私はモジュールがそれから隠そうとしているメソッドにアクセスしようとしているので、Foo.new.do_stuffを爆破したいです。上記のコードでは、しかし、Foo.new.do_stuffは細かい:(

に動作します私はRubyで何をしたいかを達成するための方法はあります

UPDATE - ?実際のコード

class Place < ActiveRecord::Base 
    include RecursiveTreeQueries 

    belongs_to :parent, {:class_name => "Place"} 
    has_many :children, {:class_name => 'Place', :foreign_key => "parent_id"} 
end 


module RecursiveTreeQueries 

    def self_and_descendants 
    model_table = self.class.arel_table 
    temp_table = Arel::Table.new :temp 
    r = Arel::SelectManager.new(self.class.arel_engine).from(model_table).project(model_table.columns).join(temp_table).on('true').where(model_table[:parent_id].eq(temp_table[:id])) 
    nr = Place.scoped.where(:id => id) 
    q = Arel::SelectManager.new(self.class.arel_engine) 
    as = Arel::Nodes::As.new temp_table, nr.union(r) 
    arel = Arel::SelectManager.new(self.class.arel_engine).with(:recursive,as).from(temp_table).project(temp_table[:id]) 
    self.class.where(model_table[:id].in(arel)) 
    end 

    def self_and_ascendants 
    model_table = self.class.arel_table 
    temp_table = Arel::Table.new :temp 
    r = Arel::SelectManager.new(self.class.arel_engine).from(model_table).project(model_table.columns).join(temp_table).on('true').where(temp_table[:parent_id].eq(model_table[:id])) 
    nr = Place.scoped.where(:id => id) 
    q = Arel::SelectManager.new(self.class.arel_engine) 
    as = Arel::Nodes::As.new temp_table, nr.union(r) 
    arel = Arel::SelectManager.new(self.class.arel_engine).with(:recursive,as).from(temp_table).project(temp_table[:id]) 
    self.class.where(model_table[:id].in(arel)) 
end 

end 

明らかに、このコードはハックアウトされているため、深刻なリファクタリングが原因で、私の質問の目的は、ActiveRecord :: Baseやその他のモジュールにいくつかのメソッドを誤って上書きしてしまうことがないように、 in place.rb

+0

最初の例では、 'Bar#do_stuff'は本質的に' common_method_name'へのパブリックインターフェイスなので、コードが中断するロジックはありません。あなたが 'Foo.new.common_method_name'を実行した場合に壊れるはずです。 –

答えて

5

これを行うための簡単な方法はありません。これは設計によるものです。振る舞いのカプセル化が必要な場合は、おそらくモジュールではなくクラスが必要になるでしょう。

Rubyでは、プライベートメソッドとパブリックメソッドの主な違いは、プライベートメソッドは明示的な受信者なしでしか呼び出せないことです。 MyObject.new.my_private_methodを呼び出すとエラーになりますが、MyObjectのメソッド定義内でmy_private_methodを呼び出しても問題ありません。

あなたがクラスにモジュールを混在する場合、そのモジュールのメソッドは、クラスの中に「コピー」されています

[I] F我々はクラス定義でのモジュールを含むが、その方法が効果的に追加されます、または "ミックスイン"することができます。 - Ruby User's Guide

このクラスに関しては、モジュールは外部エンティティとして存在しなくなります(ただし、下記のMarc Talbotのコメントを参照してください)。レシーバを指定せずにクラス内からモジュールのメソッドのいずれかを呼び出すことができるため、モジュールのプライベートメソッドでなく、クラスのプライベートメソッドだけになります。

+1

厳密に言えば、これはあまり真実ではありません。モジュールは継承チェーンにあるものの、別個のエンティティとして存在しますクラス自体は、モジュールにあるメソッドについては何も知らない。クラスに対してモジュールメソッド呼び出しが行われたとき、クラスは基本的に「どうしたらよいかわからない。 (モジュールが混在している)チェーンの上に置く。 –

+0

@MarcTalbotありがとう。私は答えを修正した。 – Brandan

0

Mar kモジュールが含まれている場合はprivateメソッド。

module Bar 
    def do_stuff 
    common_method_name 
    end 

    def common_method_name 
    #blah blah 
    end 

    def self.included(klass) 
     klass.send(:private, :common_method_name) 
    end 
end 
+2

これは、インクルードするクラスが 'common_method_name'を呼び出すのを妨げません。これは、そのメソッドをプライベートとしてマークするだけです。これは既にOPのコードのケースです。 – Brandan

+0

ええ、あなたはまったく正しい...私は少し質問を誤解したと思います。この場合、その背後にあるすべての目的は私にはちょっと混乱しています。私はそれをどうやって行うのか分かりません。 : – Veraticus

+1

目的は、モジュール内のヘルパーメソッドのカプセル化を有効にすることです。同じプライベートメソッド名を持つ2つ以上のモジュールを含めると、奇妙な振る舞いを引き起こさないようにします。 –

1

これはかなり古い質問ですが、回答にはRubyの重要な機能が欠けているので、私はそれに答えてくれると思います。あなたは実際に必要

class Foo 
    include RecursiveTreeQueries.new 
end 

機能がModule Buildersと呼ばれ、ここにあなたがそれを達成するためのモジュールを定義する方法ですされています

class RecursiveTreeQueries < Module 
    def included(model_class) 
    model_table = model_class.arel_table 
    temp_table = Arel::Table.new :temp 
    nr = Place.scoped.where(:id => id) 
    q = Arel::SelectManager.new(model_class.arel_engine) 
    arel_engine = model_class.arel_engine 

    define_method :self_and_descendants do 
     r = Arel::SelectManager.new(arel_engine).from(model_table).project(model_table.columns).join(temp_table).on('true').where(model_table[:parent_id].eq(temp_table[:id])) 
     as = Arel::Nodes::As.new temp_table, nr.union(r) 
     arel = Arel::SelectManager.new(arel_engine).with(:recursive,as).from(temp_table).project(temp_table[:id]) 
     self.class.where(model_table[:id].in(arel)) 
    end 

    define_method :self_and_ascendants do 
     r = Arel::SelectManager.new(arel_engine).from(model_table).project(model_table.columns).join(temp_table).on('true').where(temp_table[:parent_id].eq(model_table[:id])) 
     as = Arel::Nodes::As.new temp_table, nr.union(r) 
     arel = Arel::SelectManager.new(arel_engine).with(:recursive,as).from(temp_table).project(temp_table[:id]) 
     self.class.where(model_table[:id].in(arel)) 
    end 
    end 
end 

今、あなたがモジュールを含むことができ、 RecursiveTreeQueriesはモジュール自体ではなくクラス(Moduleクラスのサブクラス)であるため、モジュールをインスタンス化します。メソッド間の重複を減らすためにこれをさらにリファクタリングすることができます。私はあなたがそのコンセプトを実証するために持っていたものを取りました。

+0

素晴らしい答え!テッド! –