0

ActiveRecord の機能の一部を再作成するプロジェクトに取り組んでいます。ここが機能していない部分です

module Associations
  def belongs_to(name, params)
    self.class.send(:define_method, :other_class) do |name, params|
      (params[:class_name] || name.camelize).constantize
    end

    self.class.send(:define_method, :other_table_name) do |other_class|
      other_class.table_name
    end
    .
    .
    .
    o_c = other_class(name, params)
    #puts this and other (working) values in a query
    query = <<-SQL
      ...
    SQL
    #sends it off with db.execute(query)...

私はこのテストファイルに向かって構築しています:

require 'all_files' #holds SQLClass & others

pets_db_file_name = File.expand_path(File.join(File.dirname(__FILE__), "pets.db"))
DBConnection.open(pets_db_file_name)

#class Person
#end

class Pet < SQLClass
  set_table_name("pets")
  set_attrs(:id, :name, :owner_id)

  belongs_to :person, :class_name => "Person", :primary_key => :id, :foreign_key => :owner_id
end

class Person < SQLClass
  set_table_name("people")
  set_attrs(:id, :name)

  has_many :pets, :foreign_key => :owner_id
end
.
.
.

私が受け取った何の変化もなく

.../active_support/inflector/methods.rb:230:in `block in constantize': uninitialized constant Person (NameError)

ファイルにクラスをロードする順序に問題があることを確認するために、空の Person クラスでファイルを開始しました。

undefined method `table_name' for Person:Class (NoMethodError)

これは学習プロジェクトであるため、コードを機能させるためにテストを変更したくありません (すべてのクラスを開き、すべてのテーブル/属性を設定してbelongs_toから、. )

SQLクラスを編集:

class SQLClass < AssignmentClass

    extend SearchMod

    extend Associations

    def self.set_table_name(table_name)
         @table_name = table_name
    end

    def self.table_name
        @table_name
    end
#some more methods for finding rows, and creating new rows in existing tables

また、AssignmentClass の関連部分はsendonを使用しattr_accessorて機能を提供し、クラスの新しいインスタンスをset_attrs作成する前に、すべての名前が を使用して設定されたものと一致することを確認します。initializeset_attrs

4

2 に答える 2

0

進捗!作成を遅らせるために使用する Alex D の提案に触発されmethod_missing、代わりにdefine_method次のように名前のメソッドを作成するために使用しました。

define_method, :other_class) do |name, params|
          (params[:class_name] || name.camelize).constantize
        end

define_method(:other_table_name) do |other_class|
  other_class.table_name
end

#etc

define_method(name) do #|params| turns out I didn't need to pass in `params` at all but:
        #p "---#{params} (This is line 31: when testing this out I got the strangest error
#.rb:31:in `block in belongs_to': wrong number of arguments (0 for 1) (ArgumentError)
#if anyone can explain this I would be grateful.
#I had declared an @params class instance variable and a getter for it,
#but nothing that should make params require an argument
        f_k = foreign_key(name, params)
        p f_k
        o_c = other_class(name, params)
        o_t_n = other_table_name(o_c)
        p_k = primary_key(params)

        query = <<-SQL
            SELECT *
            FROM #{o_t_n}
            WHERE #{p_k} = ?
        SQL

        row = DBConnection.execute(query, self.send(f_k))
        o_c.parse_all(row)
    end
于 2013-06-20T12:19:10.163 に答える
0

これは、動的で解釈された Ruby (その他) と Java/C#/C++ のような静的でコンパイルされた言語との重要な違いを浮き彫りにします。Java では、コンパイラはすべてのソース ファイルに対して実行され、すべてのクラス/メソッド定義を見つけて、それらを使用法と照合します。classRuby はこのようには機能しません。つまり、ブロックを実行した後にクラスが「存在する」のです。それ以前は、Ruby インタープリターはそれについて何も知りません。

テスト ファイルでは、Pet最初に定義します。の定義内にPet、 がありますbelongs_to :person。のクラス オブジェクトを取得しようとしていbelongs_toます。しかし、まだ存在しません!その定義は、テスト ファイルの後半にあります。:person.constantizePersonPerson

これを解決するには、いくつかの方法があります。

1 つは、Rails と同じように、各クラスを独自のファイルに定義し、ファイル名を何らかの規則に準拠させることです。をオーバーライドconstant_missingし、不足しているクラスを定義するファイルを自動的にロードします。これにより、ロード順の問題が自動的に解決されます。

別の解決策は、belongs_to怠惰にすることです。クラス オブジェクトをすぐに検索するのではなく、 と の間にPerson関連があるという事実を記録することができます。誰かが を呼び出そうとしたら、フックを使用して実際にメソッドを定義します。(おそらく、その時までにすべてのクラス定義が実行されているでしょう。)PetPersonpet.personmissing_method

別の方法は、次のようなことです。

define_method(belongs_to) do
  belongs_to_class = belongs_to.constantize
  self.class.send(:define_method, belongs_to) do
    # put actual definition here
  end
  self.send(belongs_to)
end

このコードはテストされていません。アイデアを提供するためのものです。おそらく、それはかなり頭を悩ませるアイデアですが。基本的に、最初に呼び出されたときにそれ自体を再定義するメソッドを定義します。を使用するのと同じようmethod_missingに、これにより、メソッドが実際に初めて使用されるまでクラスのルックアップを遅らせることができます。

もう1つ言えることは、「過負荷」method_missingにしたくないと言っていますが、それはあなたが思っているほど問題ではないと思います。コードをヘルパー メソッドに抽出して、method_missing管理可能な定義を維持するだけです。たぶん次のようなものです:

 def method_missing(name,*a,&b)
   if has_belongs_to_association?(name)
     invoke_belongs_to_association(name,a,b)
   elsif has_has_many_association?(name)
     invoke_has_many_association(name,a,b)
   # more...
   else
     super
   end
 end
于 2013-05-26T19:45:41.537 に答える