Question

J'ai ce code dans mon contrôleur pour une application 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

Quand je lance mon spec pour vous assurer que mes clés étrangères fonctionnent, je reçois ceci:

not a MysqlError, instead it was a MysqlError

Que pourrait-on se passe ici?


Quelques informations ancêtre: Quand je change la rescousse pour me donner ceci:

puts MysqlError.ancestors
puts "****"
puts Mysql::Error.ancestors
puts "****"
puts e.class.ancestors

Voici ce que je reçois:

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

Y aurait-il un alias dans l'espace de noms global qui fait la classe MysqlError injoignable?

Était-ce utile?

La solution 2

Ce fut un bug simple redéfinition de la classe. Ruby vous permet de redéfinir un haut niveau constant, mais il ne détruit pas la constante d'origine lorsque vous le faites. Les objets qui contiennent toujours des références à cette constante peut encore l'utiliser, il pourrait encore être utilisé pour générer des exceptions, comme dans la question que je faisais.

Depuis ma redéfinition se passait dans les dépendances, je résolu ce problème en recherchant la classe d'origine dans l'espace d'objets, et accroché à une référence à elle à utiliser lors de la capture des exceptions. J'ai ajouté cette ligne à mon contrôleur:

ObjectSpace.each_object(Class){|k| @@mysql_error = k if k.name == 'MysqlError'}

Cela devient une référence à la version originale de MysqlError. Ensuite, j'ai pu faire ceci:

  rescue @@mysql_error => e
    render :xml => e.message, :status => :unprocessable_entity and return

Cela se produit parce que le bijou mysql est chargé après se MysqlError a déjà été défini. Voici un peu de joie de la console de test:

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

Vous pouvez le faire dans la CISR sans exiger assez facilement; voici une astuce qui fonctionne parce que RIR ne regarde pas Hash par nom chaque fois que vous déclarez un Hash littéral:

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

Je peux comprendre pourquoi vous pourriez vouloir ce faire, il pourrait être utilisé comme alias_method_chain pour l'espace de noms global. Vous pouvez ajouter un mutex à une classe qui ne sont pas threadsafe, par exemple, et pas besoin de changer l'ancien code pour référencer votre version threadsafe. Mais je ne veux pas RSpec avait réduit au silence cet avertissement.

Autres conseils

les classes Ruby ne sont que des objets, donc la comparaison est basée sur l'identité de l'objet (par exemple, le même pointeur sous le capot).

Je ne sais pas ce qui se passe dans votre cas, mais je vais essayer de débogage dans quelques endroits et de voir ce que l'objet et ids ancêtres que vous obtenez pour MysqlError. Je pense qu'il ya deux objets dans ces différents modules et votre clause catch fait référence à la mauvaise.

Edit:

C'est tout à fait étrange. Je suppose maintenant que MysqlError ou l'un de ses ancêtres a été inclus en deux points différents le long de vos contrôleurs propre chaîne de classe, et qui est en quelque sorte trébucher l'exception attraper.

Théorie # 2 serait que, puisque les rails REDEFINES const_missing faire auto-demande où vous vous attendez à obtenir une exception UndefinedConstant dans les clauses de gestion des exceptions est au lieu de trouver quelque chose par ce dieu nom sait où dans l'arbre source. Vous devriez être en mesure de voir si tel est le cas en testant avec l'auto nécessitant de (c.-à-faire quelques debugs aussi bien en mode dev et prod).

Il y a une syntaxe pour forcer votre référence à partir de la racine qui peut être d'une certaine aide si vous pouvez le droit d'être un référencement:

::Foo::Bar

Rant:

Ce genre de chose est là où je pense que certains des défauts de spectacle rubis. Sous le capot du modèle d'objet et portée de Ruby est toutes les structures d'objets pointant vers l'autre, d'une manière qui est tout à fait semblable à javascript ou d'autres langages à base de prototypes. Mais cela fait surface incohérente dans la classe / syntaxe du module que vous utilisez dans la langue. Il semble que certains refactoring attention, vous pouvez faire ce genre de choses plus claires, ainsi que de simplifier le langage, bien que cela serait bien sûr très incompatible avec le code existant.

Conseil:

Lors de l'utilisation met pour le débogage, essayez de faire met foo.inspect comme cela affichera dans la façon dont vous avez l'habitude de partir RIR.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top