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。但是,我不知道如何继续。 )

编辑 SQLClass:

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 的相关部分使用sendonattr_accessor来提供功能set_attrs并确保在您initialize创建一个类的新实例之前,所有名称都与使用设置的名称匹配set_attrs

4

2 回答 2

0

这突出了动态、解释型 Rub​​y(等)和静态、编译型语言(如 Java/C#/C++)之间的重要区别。在 Java 中,编译器运行所有源文件,查找所有类/方法定义,并将它们与用法相匹配。Ruby 不是这样工作的——一个类在执行它的class块之后“开始存在”。在此之前,Ruby 解释器对此一无所知。

在您的测试文件中,您Pet首先定义。在 的定义内Pet,您有belongs_to :personbelongs_to确实:person.constantize,试图获取Person. 但Person还不存在!它的定义稍后出现在测试文件中。

我认为有几种方法可以尝试解决此问题:

一种是做 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

此代码未经测试,仅供参考!虽然这是一个非常令人费解的想法,也许。基本上,您定义了一个在第一次调用时重新定义自身的方法。就像 using 一样method_missing,这允许您延迟类查找,直到第一次实际使用该方法。

如果我可以再说一件事:尽管您说您不想“超载” 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 回答
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 回答