Conversion d'une chaîne de snake_case en CamelCase dans Ruby


171

J'essaye de convertir un nom de cas de serpent en cas de chameau. Existe-t-il des méthodes intégrées?

Par exemple: "app_user"à"AppUser"

(J'ai une chaîne que "app_user"je veux convertir en modèle AppUser).

Réponses:


252

Si vous utilisez Rails, String # camelize est ce que vous recherchez.

  "active_record".camelize                # => "ActiveRecord"
  "active_record".camelize(:lower)        # => "activeRecord"

Si vous voulez obtenir une classe réelle, vous devez utiliser String # constantize en plus de cela.

"app_user".camelize.constantize

44
Vous devriez ajouter qu'il s'agit d'un ajout de Rails à String, cela ne fonctionne pas avec Ruby pur.
iGEL

2
Il est étiqueté ruby-on-rails, donc, je suppose, ce n'est pas un problème. Mais merci de l'avoir mentionné.
Sergio Tulentsev

6
Vous n'avez pas besoin de caméliser avant de continuer. Utilisez #classifyplutôt. "some_namespace/module/class_name".classify => "SomeNamespace::Module::ClassName"
Chris Heald

5
@chris #classify: Pas la même chose. #classify renvoie une chaîne, tandis que #constantize recherche la constante dans le contexte (et nécessite camelize). 'active_record'.constantize donne une erreur,' active_record'.camelize.constantize renvoie la constante ActiveRecord, 'active_record'.classify renvoie la chaîne' ActiveRecord '. Et si vous avez fait 'no_class'.camelize.constantize, vous obtiendrez une erreur (pas de constante NoClass), mais' no_class'.classify renvoie heureusement la chaîne 'NoClass'.
Kanat Bolazar

Pour utiliser ces méthodes de Rails à partir de Ruby pur, il require "active_support/core_ext/string"suffit, à condition que Rails soit déjà installé.
Masa Sakano

121

Celui-ci, ça va?

"hello_world".split('_').collect(&:capitalize).join #=> "HelloWorld"

Trouvé dans les commentaires ici: Classifier une chaîne Ruby

Voir le commentaire de Wayne Conrad


11
Vous êtes génial, merci. Je ne voulais pas avoir à inclure des bibliothèques de rails juste pour une tâche aussi petite. C'est beau. :)
Gerry

11
C'est l'une des seules vraies réponses à la question. Ne pas utiliser de bibliothèques Rails.
Luis Ortega Araneda

40

Si vous utilisez des rails, utilisez classify. Il gère bien les boîtiers de bord.

"app_user".classify # => AppUser
"user_links".classify   # => UserLink

Remarque:

Cette réponse est spécifique à la description donnée dans la question (elle n'est pas spécifique au titre de la question). Si quelqu'un essaie de convertir une chaîne en cas de chameau, il doit utiliser la réponse de Sergio . Le questionneur déclare qu'il veut se convertir app_useren AppUser(non App_user), d'où cette réponse.


4
Pour les environnements Rails, c'est parfait.
ghayes

Notez que classifyrenvoie une chaîne, vous devez l'appeler constantizepar la suite pour la convertir en classe réelle.
Stefan

1
Une mise en garde importante avec classifyest que les chaînes au pluriel deviendront singulières ... 'age_in_years'.classifydevientAgeInYear
br3nt

@ br3nt il ne se pluralise pas depuis activerecord4.2.11
Ulysse BN

23

Source: http://rubydoc.info/gems/extlib/0.9.15/String#camel_case-instance_method

À des fins d'apprentissage:

class String
  def camel_case
    return self if self !~ /_/ && self =~ /[A-Z]+.*/
    split('_').map{|e| e.capitalize}.join
  end
end

"foo_bar".camel_case          #=> "FooBar"

Et pour la variante lowerCase:

class String
  def camel_case_lower
    self.split('_').inject([]){ |buffer,e| buffer.push(buffer.empty? ? e : e.capitalize) }.join
  end
end

"foo_bar".camel_case_lower          #=> "fooBar"

6
@pguardiario si la roue s'appelle ActiveSupport , réinventez-la.
shime le

Je pense que la variante lowerCase est fausse. Le bloc d'injection ne doit pas manipuler directement le tampon mais renvoyer la nouvelle valeur du tampon:self.split('_').inject([]){ |buffer,e| buffer + [buffer.empty? ? e : e.capitalize] }.join
Sven Koschnicke

19

Benchmark pour les solutions Ruby pures

J'ai pris toutes les possibilités que j'avais en tête pour le faire avec du code ruby ​​pur, les voici:

  • capitaliser et gsub

    'app_user'.capitalize.gsub(/_(\w)/){$1.upcase}
  • diviser et cartographier en utilisant la &sténographie (grâce à la réponse de user3869936)

    'app_user'.split('_').map(&:capitalize).join
  • diviser et cartographier (grâce à la réponse de M. Black)

    'app_user'.split('_').map{|e| e.capitalize}.join

Et voici le Benchmark pour tout cela, nous pouvons voir que gsub est assez mauvais pour cela. J'ai utilisé 126080 mots.

                              user     system      total        real
capitalize and gsub  :      0.360000   0.000000   0.360000 (  0.357472)
split and map, with &:      0.190000   0.000000   0.190000 (  0.189493)
split and map        :      0.170000   0.000000   0.170000 (  0.171859)

11

Je suis arrivé ici à la recherche de l'inverse de votre question, passant de l'étui de chameau à l'étui de serpent. Utilisez un trait de soulignement pour cela (pas décaméliser):

AppUser.name.underscore # => "app_user"

ou, si vous avez déjà une chaîne de casse camel:

"AppUser".underscore # => "app_user"

ou, si vous voulez obtenir le nom de la table, c'est probablement pourquoi vous voudriez le cas du serpent:

AppUser.name.tableize # => "app_users"


Pourquoi ne pas utiliser AppUser.table_name? Vous vous assurerez également d'avoir le vrai nom de la table si ce n'est pas app_users, mais quelque chose de défini ailleurs.
Ulysse BN

3

Je me sens un peu mal à l'aise d'ajouter plus de réponses ici. Décidé d'opter pour l'approche de rubis pur la plus lisible et la plus minimale, sans tenir compte de la belle référence de @ ulysse-bn. Alors que le :classmode est une copie de @ user3869936, le :methodmode que je ne vois dans aucune autre réponse ici.

  def snake_to_camel_case(str, mode: :class)
    case mode
    when :class
      str.split('_').map(&:capitalize).join
    when :method
      str.split('_').inject { |m, p| m + p.capitalize }
    else
      raise "unknown mode #{mode.inspect}"
    end
  end

Le résultat est:

[28] pry(main)> snake_to_camel_case("asd_dsa_fds", mode: :class)
=> "AsdDsaFds"
[29] pry(main)> snake_to_camel_case("asd_dsa_fds", mode: :method)
=> "asdDsaFds"

1
Le cas de chameau est en fait le premier inférieur. Sinon, il s'appelle PascalCase (ou parfois en majuscule). Même si dans cette question, c'est ambigu!
Ulysse BN

2
@UlysseBN, tbh je ne suis pas dans l'histoire des mots. Les affirmations de Wikipédia PascalCasesont un sous-ensemble de CamelCase. C'est aussi ce que je savais - cette affaire de chameau s'appliquait aux deux. Mais je n'ai jamais enquêté. Merci d'avoir mentionné PascalCase. en.wikipedia.org/wiki/Camel_case
akostadinov

2
C'est la meilleure réponse sur la page imo. Ce serait bien si la :methodversion faisait une downcasepremière pour pouvoir être utilisée à la fois sur lower_snake_caseet UPPER_SNAKE_CASE.
skagedal le

0

La plupart des autres méthodes répertoriées ici sont spécifiques à Rails. Si vous voulez faire cela avec pur Ruby, voici la méthode la plus concise que j'ai trouvée (merci à @ ulysse-bn pour l'amélioration suggérée)

x="this_should_be_camel_case"
x.gsub(/(?:_|^)(\w)/){$1.upcase}
    #=> "ThisShouldBeCamelCase"

Votre définition de «cas de chameau» est trop limitée. Les noms de classe en Java, et Ruby, par exemple, sont MyFavoriteClass en cas de camel ... mais ils n'ont pas non plus de lettre initiale en minuscule. parfois le cas de chameau a des casquettes initiales. parfois non.
masukomi

Utiliser 2 Regex où vous ne pouvez en utiliser qu'un seul est excessif. Vous ne pouvez utiliser que des groupes non capturants:x.gsub(/(?:_|^)(\w)/){$1.upcase}
Ulysse BN

@UlysseBN, et nous revenons à votre gsubsolution il semble qui est plus lente que la mapsolution.
akostadinov

0

Étendre la chaîne pour ajouter du camelize

En Ruby pur, vous pouvez étendre la classe de chaînes en utilisant exactement le même code de Rails .camelize

class String
  def camelize(uppercase_first_letter = true)
    string = self
    if uppercase_first_letter
      string = string.sub(/^[a-z\d]*/) { |match| match.capitalize }
    else
      string = string.sub(/^(?:(?=\b|[A-Z_])|\w)/) { |match| match.downcase }
    end
    string.gsub(/(?:_|(\/))([a-z\d]*)/) { "#{$1}#{$2.capitalize}" }.gsub("/", "::")
  end
end
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.