什么时候一个Ruby类不是那个Ruby类?

什么时候一个Ruby类不是那个Ruby类?,ruby,redefinition,Ruby,Redefinition,我的控制器中有Rails应用程序的以下代码: def delete object = model.datamapper_class.first(:sourced_id => params[:sourced_id]) if object.blank? render :xml => "No #{resource} with sourced_id #{params[:sourced_id]}", :status => :not_found and re

我的控制器中有Rails应用程序的以下代码:

  def delete
    object = model.datamapper_class.first(:sourced_id => params[:sourced_id])
    if object.blank?
      render :xml => "No #{resource} with sourced_id #{params[:sourced_id]}", :status => :not_found and return
    end
    object.destroy
    render :xml => "", :status => :no_content
  rescue MysqlError => e
    puts "raised MysqlError #{e.message}"
    render :xml => e.message, :status => :unprocessable_entity and return
  rescue Mysql::Error => e
    puts "raised Mysql::Error #{e.message}"
    render :xml => e.message, :status => :unprocessable_entity and return
  rescue Exception => e
    puts "not a MysqlError, instead it was a #{e.class.name}"
    render :xml => e.message, :status => :unprocessable_entity and return
  end
当我运行规范以确保外键约束有效时,我得到以下结果:

not a MysqlError, instead it was a MysqlError
这到底是怎么回事


一些祖先信息:当我改变营救给我这个:

puts MysqlError.ancestors
puts "****"
puts Mysql::Error.ancestors
puts "****"
puts e.class.ancestors
这就是我得到的:

Mysql::Error
StandardError
Exception
ActiveSupport::Dependencies::Blamable   ...
****
Mysql::Error
StandardError
Exception
ActiveSupport::Dependencies::Blamable   ...
****
MysqlError
StandardError
Exception
ActiveSupport::Dependencies::Blamable   ...

全局命名空间中是否存在使MysqlError类无法访问的别名?

Ruby类只是对象,因此比较基于对象标识(即,引擎盖下的同一指针)

不确定在您的案例中发生了什么,但我会尝试在一些位置进行调试,看看您从MysqlError中得到了什么对象ID和祖先。我怀疑在不同的模块中有两个这样的对象,而您的catch子句引用了错误的对象

编辑:

这很奇怪。我现在猜测,MysqlError或它的一个祖先已经包含在控制器自己的类链上的两个不同点上,这在某种程度上导致了异常捕获

理论#2是,由于rails重新定义const_missing以满足自动要求,因此您希望在异常处理子句中得到一个未定义的常量异常,而不是在源代码树中找到一个名为“天知道在哪里”的东西。您应该能够通过使用auto-required-off进行测试(即在dev和prod模式下进行一些调试)来了解情况是否如此

有一种语法可以强制引用从根开始,如果您能找到正确的引用,这可能会有所帮助:

::Foo::Bar
咆哮:

这就是我认为ruby的一些缺陷所在。Ruby的对象模型和范围都是相互指向的对象结构,这与javascript或其他基于原型的语言非常相似。但这在语言中使用的类/模块语法中表现得不一致。看起来,通过一些仔细的重构,您可以使这些内容更清晰,同时简化语言,尽管这当然与现有代码高度不兼容

提示:


使用puts进行调试时,请尝试执行puts foo.inspect,因为这将以您从irb中习惯的方式显示它。

这是一个简单的类重新定义错误。Ruby允许你重新定义一个顶级常量,但是当你这样做的时候,它不会破坏原来的常量。仍然保存对该常量的引用的对象仍然可以使用它,因此它仍然可以用于生成异常,就像我遇到的问题一样

由于我的重新定义是在依赖项中进行的,所以我通过在对象空间中搜索原始类并在捕获异常时保留对它的引用来解决这个问题。我将此行添加到我的控制器:

ObjectSpace.each_object(Class){|k| @@mysql_error = k if k.name == 'MysqlError'}
获取对MysqlError原始版本的引用。然后我就能做到这一点:

  rescue @@mysql_error => e
    render :xml => e.message, :status => :unprocessable_entity and return
这是因为MysqlError已经被定义之后,mysqlgem正在被加载。以下是一些测试控制台的乐趣:

Loading test environment (Rails 2.3.2)
>> MysqlError.object_id
=> 58446850
>> require 'mysql'
C:/Ruby/lib/ruby/gems/1.8/gems/mysql-2.7.3-x86-mswin32/ext/mysql.so: warning: already initialized constant MysqlError
=> true
>> MysqlError.object_id
=> 58886080
>> ObjectSpace._id2ref(MysqlError.object_id)
=> Mysql::Error
你可以在IRB中很容易地做到这一点,而无需任何要求;以下是一个有效的技巧,因为irb不会在每次声明哈希文本时按名称查找哈希:

irb(main):001:0> Hash = Class.new
(irb):1: warning: already initialized constant Hash
=> Hash
irb(main):002:0> hash = {:test => true}
=> {:test=>true}
irb(main):003:0> hash.class
=> Hash
irb(main):004:0> hash.is_a? Hash
=> false

我可以理解为什么您可能想要这样做,它可以像全局名称空间的alias_method_chain一样使用。例如,您可以将互斥体添加到非线程安全的类中,并且不需要更改旧代码来引用您的线程安全版本。但我真希望RSpec没有压制住这一警告。

是的,我也这么认为,而且祖先的想法很好,我在问题中提供了更多信息。但我怎么才能真正引用我想要的呢?哇,真讨厌。很棒的景点+1个迷人的东西。我不认为你可以在Ruby中复制这个。我只是想在irb中定义Blah类,奇怪的是,即使您退出并输入irb,新定义的Blah类的对象id仍然保持不变。。。但是,如果您在开始时以不同的方式定义Blah(例如,您添加了一个方法,但在第一个定义中),则对象id会发生变化。我不知道这意味着什么,但我认为这可能会有所帮助…?是不是在加载gem之前对MysqlError进行了猴子补丁?@Scott,猴子补丁不会更改对象的id。请在irb中尝试。当第一次定义类时,它会得到一个对象id(它与类的内容,包括它的名称,有某种关联),并且它会。。。除了迈克尔描述的情况。哇,那太邪恶了。很好的追踪工作。