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).

Lohith MV
la source

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
Sergio Tulentsev
la source
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

user3869936
la source
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.

Harish Shetty
la source
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"
Mr Noir
la source
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)
Ulysse BN
la source
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"

Mike
la source
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"
Akostadinov
la source
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"
masukomi
la source
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
Cameron Lowell Palmer
la source