我正在开发一个项目来重新创建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的相关部分使用send
上的attr_accessor
为set_attrs
提供功能,并确保在initialize
新的类实例之前所有名称匹配使用set_attrs
设置的内容。
答案 0 :(得分:0)
这突出了动态的,解释性的Ruby(等)和静态编译语言(如Java / C#/ C ++)之间的重要区别。在Java中,编译器运行所有源文件,查找所有类/方法定义,并将它们与用法进行匹配。 Ruby不会像这样工作 - 在执行class
块之后,一个类“出现”。在此之前,Ruby解释器对此一无所知。
在测试文件中,首先定义Pet
。在Pet
的定义中,您有belongs_to :person
。 belongs_to
执行:person.constantize
,尝试获取Person
的类对象。但是Person
还不存在!它的定义稍后会出现在测试文件中。
有几种方法我认为你可以尝试解决这个问题:
一个是做Rails的工作:在自己的文件中定义每个类,并使文件名符合某些约定。覆盖constant_missing
,并使其自动加载定义缺失类的文件。这将使加载顺序问题自动解决。
另一个解决方案是让belongs_to
懒惰。它不是立即查找Person
类对象,而只是记录Pet
和Person
之间存在关联的事实。当有人试图调用pet.person
时,请使用missing_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
一样,这允许您延迟类查找,直到第一次实际使用该方法。
如果我再说一句话:虽然你说你不想“超载”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
答案 1 :(得分: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