95 votes

ruby convertit le nom de la classe dans la chaîne en classe réelle

Comment appeler une classe à partir d'une chaîne de caractères contenant le nom de cette classe ? (Je suppose que je pourrais faire case/when mais cela semble laid).

La raison de ma question est que j'utilise l'option acts_as_commentable entre autres, qui stockent le commentable_type comme une colonne. Je veux pouvoir appeler n'importe quelle classe commentable particulière pour faire un find(commentable_id) sur elle.

Merci.

142voto

Jamie Wong Points 10858

Je pense que ce que vous voulez est constantize

C'est une construction RdR. Je ne sais pas s'il y en a une pour Ruby Core.

0 votes

Parfait, c'est exactement ce que je cherchais.

42 votes

Pour un simple Ruby, vous utiliserez Module.const_get . L'avantage de constantize est qu'il fonctionne même avec des espaces de noms profondément imbriqués, de sorte que vous pourriez faire 'Functional::Collections::LazyList'.constantize et obtenir la classe LazyList du module Collections dans le module Functional, alors qu'avec const_get vous devriez faire quelque chose comme 'Functional::Collections::LazyList'.split('::').reduce(Modul‌​e, :const_get) .

47voto

jtbandes Points 39804
"Object".constantize # => Object

0 votes

Veuillez noter que .constantize est une méthode Rails, vous ne la trouverez pas dans le langage Ruby.

37voto

user664833 Points 4597

Cela dépend de la corde...

S'il a déjà la forme appropriée (boîtier, pluralisation, etc.) et qu'il correspondrait directement à un objet, alors.. :

Rails :

'User'.constantize # => User

Ruby :

Module.const_get 'User' # => User

Mais sinon (notez la différence de boîtier) :

'user'.constantize # => NameError: wrong constant name user

Module.const_get 'user' # => NameError: wrong constant name user

Par conséquent, vous devez vous demander... la chaîne source est-elle au singulier ou au pluriel (fait-elle référence à une table ou non ?), est-elle composée de plusieurs mots et AlreadyCamelCased ou is_it_underscored ?

Avec Rails, vous avez ces outils à votre disposition :

Utilisez cameliser pour convertir les chaînes de caractères en chaînes de caractères en majuscules, même en traitant les caractères de soulignement et les barres obliques :

'object'.constantize # => NameError: wrong constant name object
'object'.camelize # => "Object"
'object'.camelize.constantize # => Object
'active_model/errors'.camelize # => "ActiveModel::Errors"
'active_model/errors'.camelize.constantize # => ActiveModel::Errors

Utilisez classer pour convertir une chaîne de caractères, qui peut même être plurielle (c'est peut-être une référence de table), pour créer un nom de classe (toujours une chaîne de caractères), puis appeler constance pour essayer de trouver et de retourner la constante du nom de la classe (notez qu'en Ruby les noms de classe sont des constantes ) :

'users'.classify => "User" # a string
'users'.classify.constantize # => User

'user'.classify => "User" # a string
'user'.classify.constantize # => User

'ham_and_eggs'.classify # => "HamAndEgg"

En POR (Plain Old Ruby), vous avez capitaliser mais cela ne fonctionne que pour le premier mot :

Module.const_get 'user'.capitalize => User

...sinon vous devez utiliser des outils fondamentaux tels que strip, split, map, join, etc. pour réaliser la manipulation appropriée :

class HamAndEgg end # => nil
Module.const_get ' ham and eggs '.strip.gsub(/s$/,'').split(' ').map{|w| w.capitalize}.join # => HamAndEgg

12 votes

Vous devriez utiliser camelize au lieu de classify, car classify est destiné aux noms de tables et ne gère pas très bien la pluralisation.

2 votes

Votre réponse est vraiment précieuse, mais vous devriez utiliser "titleize" pour le nom de la table qui comprend un espace, et supprimer les espaces blancs de la chaîne pour donner un sens complet au nom de la classe.

0 votes

@SSR oui, vous pourriez : ' ham and eggs '.titleize.gsub(/ /,'').constantize # => HamAndEggs

23voto

Je sais que c'est une vieille question mais je veux juste laisser cette note, elle peut être utile pour d'autres.

En clair, en Ruby, Module.const_get peut trouver des constantes imbriquées. Par exemple, avec la structure suivante :

module MyModule
  module MySubmodule
    class MyModel
    end
  end
end

Vous pouvez l'utiliser comme suit :

Module.const_get("MyModule::MySubmodule::MyModel")
MyModule.const_get("MySubmodule")
MyModule::MySubmodule.const_get("MyModel")

7voto

skalee Points 3227

Lorsque ActiveSupport est disponible (par exemple, dans Rails) : String#constantize o String#safe_constantize c'est-à-dire "ClassName".constantize .

En pur Ruby : Module#const_get en général Object.const_get("ClassName") .

Dans les rubis récents, les deux fonctionnent avec des constantes imbriquées dans des modules, comme dans Object.const_get("Outer::Inner") .

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X