Question

Pour générer des simulations pour Omniauth, j'ai ajouté cette méthode à config/environments/development.rb

  def provides_mocks_for(*providers)
    providers.each do |provider|
      class_eval %Q{
        OmniAuth.config.add_mock(provider, {
          :uid => '123456',
          :provider => provider,
          :nickname => 'nickname',
          :info => {
            'email' => "#{provider}@webs.com",
            'name' => 'full_name_' + provider
          }
        })
      }
    end
  end

alors j'appelle dans le même fichier:

provides_mocks_for :facebook, :twitter, :github, :meetup

Mais j'obtiens:

3.1.3/lib/active_support/core_ext/kernel/singleton_class.rb:11:in `class_eval': can't create instance of singleton class (TypeError)
Était-ce utile?

La solution

class_evalan et module_eval (qui sont équivalents) sont utilisés pour évaluer et exécuter immédiatement des chaînes en tant que code Ruby. En tant que tels, ils peuvent être utilisés comme une installation de méta-programmation pour créer dynamiquement des méthodes. Un exemple est

class Foo
  %w[foo bar].each do |name|
    self.class_eval <<-RUBY, __FILE__, __LINE__ + 1
      def #{name}
        puts '#{name}'
      end
    RUBY
  end
end

Il créera deux méthodes foo et bar qui afficheront leurs valeurs respectives. Comme vous pouvez le voir, je crée une chaîne contenant le code source réel de la fonction et je le passe dans class_eval.

Bien que ce soit un instrument très capable d'exécuter du code créé dynamiquement, il doit être utilisé avec beaucoup de précaution. Si vous faites des erreurs ici, de mauvaises choses arriveront ™. Par exemple. si vous utilisez des valeurs fournies par l'utilisateur lors de la génération de code, assurez-vous vraiment que les variables ne contiennent que les valeurs que vous attendez. La fonction basée sur l'évaluation doit généralement être utilisée en dernier recours.

Une variante plus propre et généralement préférée consiste à utiliser define_method comme ceci:

class Foo
  %w[foo bar].each do |name|
    define_method name do
      puts name
    end
  end
end

(Notez que l'IRM est un peu plus rapide avec la variante eval. Mais cela n'a pas d'importance la plupart du temps par rapport à la sécurité et à la clarté accrues.)

Maintenant, dans votre code donné, vous écrivez effectivement du code dans une chaîne qui peut être exécutée directement. L'utilisation de class_eval ici conduit à l'exécution de la chaîne dans le contexte de l'objet le plus haut (Kernel dans ce cas). Comme il s'agit d'un objet singleton spécial qui ne peut pas être instancié (similaire à nil, true et false), vous obtenez cette erreur.

Cependant, comme vous créez du code directement exécutable, vous n'avez pas du tout besoin d'utiliser class_eval (ou toute forme d'eval). Exécutez simplement votre code en boucle tel quel. Et rappelez-vous toujours: les variantes de méta-programmation (dont les méthodes eval sont parmi les plus mauvaises) ne doivent être utilisées qu'en dernier recours.

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