ときにRubyのクラスは、そのRubyのクラスではないでしょうか?

StackOverflow https://stackoverflow.com/questions/1134752

  •  16-09-2019
  •  | 
  •  

質問

私は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クラスが到達不能になりグローバル名前空間内の別名があるのだろうか。

役に立ちましたか?

解決 2

これは、単純なクラス再定義のバグでした。 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がすでに定義された後にMySQLの宝石がロードされてきているので、

これが起こります。ここではいくつかのテストコンソールの喜びがあります:

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のは、その警告を黙らないでよかったんます。

他のヒント

比較は、オブジェクトアイデンティティ(ボンネットの下、すなわち、同一のポインタ)に基づいているように、

Rubyのクラスは単に、オブジェクトである。

未あなたのケースで何が起こっているかを確認しますが、私はいくつかの場所でのデバッグとあなたがMysqlErrorのために何を得るのオブジェクトIDと先祖見て試してみました。私はそこに別のモジュールに2つのそのようなオブジェクトをだとあなたのcatch節が間違ったものを参照していると思われる。

編集ます:

これは非常に奇妙です。私の推測では、現在お使いのコントローラ自身のクラスのチェーンに沿って2つの異なる点に含まれていることMysqlErrorかの祖先の一つであり、それは何とかキャッチ例外をトリップいます。

論#2は、レールがするconst_missing再定義するので、あなたが例外処理句でUndefinedConstant例外ではなく、その名前の神で何かを見つけることです得ることを期待したいところどこソースツリーの知っている、自動-必要があることでしょう。あなたは(つまり、DEVとPRODモードの両方でいくつかのデバッグを行う)その場合はオフに必要と自動でテストすることによって見ることができる必要があります。

あなたが参照されるように正しいものを見つけ出すことができれば、いくつかの助けになることがあり、ルートから開始するようにあなたの参照を強制するための構文があります:

::Foo::Bar

暴言ます:

私はルビーのショーの欠陥のいくつかを考える。ここで、

この種のものです。ボンネットの下にRubyのオブジェクトモデルとスコープはjavascriptや他のプロトタイプベースの言語と非常によく似ていた方法で、お互いを指しているすべてのオブジェクト構造です。しかし、これはあなたが言語の使用クラス/モジュール構文で一貫性なく浮上しています。これはもちろん、既存のコードと非常に互換性がないだろうけれども、それは、あなたがこのようなもの明確にするだけでなく、言語を簡素化することができ、いくつかの慎重なリファクタリングを持つように思えるます。

ヒント:

デバッグのためのプットを使用している場合、これはあなたがIRBからに慣れている方法でそれを表示するようfoo.inspectプットをやってみます。

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top