Convertir cadenas de snake_case a CamelCase en Ruby

171

Estoy tratando de convertir un nombre de serpiente a camello. ¿Hay algún método incorporado?

Por ejemplo: "app_user"a"AppUser"

(Tengo una cadena "app_user"que quiero convertir eso a modelo AppUser).

Lohith MV
fuente

Respuestas:

252

Si está utilizando Rails, String # camelize es lo que está buscando.

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

Si desea obtener una clase real, debe usar String # constantize además de eso.

"app_user".camelize.constantize
Sergio Tulentsev
fuente
44
Debe agregar que esta es una adición de Rails a String, no funciona con Ruby puro.
iGEL
2
Está etiquetado ruby-on-rails, así que supongo que no es un problema. Pero gracias por mencionarlo.
Sergio Tulentsev
66
No es necesario camelizar antes de constante. Usar en su #classifylugar. "some_namespace/module/class_name".classify => "SomeNamespace::Module::ClassName"
Chris Heald
55
@chris #classify: No es lo mismo. #classify devuelve una cadena, mientras que #constantize busca constante en contexto (y necesita camelize). 'active_record'.constantize da error,' active_record'.camelize.constantize devuelve la constante ActiveRecord, 'active_record'.classify devuelve la cadena' ActiveRecord '. Y si hicieras 'no_class'.camelize.constantize obtendrías un error (no hay una NoClass constante), pero' no_class'.classify devuelve felizmente la cadena 'NoClass'.
Kanat Bolazar
Para utilizar estos métodos de Rails de Ruby puro, require "active_support/core_ext/string"es suficiente, siempre que Rails ya esté instalado.
Masa Sakano
121

¿Que tal este?

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

Encontrado en los comentarios aquí: clasificar una cadena de Ruby

Ver comentario de Wayne Conrad

usuario3869936
fuente
11
Eres genial, gracias. No quería tener que incluir bibliotecas de rieles solo para una tarea tan pequeña. Esto es hermoso. :)
Gerry
11
Esta es una de las únicas respuestas reales a la pregunta. No usar las bibliotecas Rails.
Luis Ortega Araneda
40

Si usa rieles, use classify. Maneja bien los casos de borde.

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

Nota:

Esta respuesta es específica de la descripción dada en la pregunta (no es específica del título de la pregunta). Si uno está tratando de convertir una cadena a camello, debe usar la respuesta de Sergio . El interrogador afirma que quiere convertirse app_usera AppUser(no App_user), de ahí esta respuesta.

Harish Shetty
fuente
44
Para entornos Rails, esto es perfecto.
ghayes
Tenga en cuenta que classifydevuelve una cadena, debe llamar constantizedespués para convertirla en una clase real.
Stefan
1
Una advertencia importante con classifyes que las cadenas Pluralizado se convertirán en singular ... 'age_in_years'.classifyse convierte enAgeInYear
br3nt
@ br3nt no se pluraliza desde activerecord4.2.11
Ulysse BN
23

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

Para fines de aprendizaje:

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"

Y para 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"
El Sr. Black
fuente
66
@pguardiario si la rueda se llama ActiveSupport , reinventala.
shime
Creo que la variante lowerCase está mal. El bloque de inyección no debe manipular directamente el búfer sino devolver el nuevo valor para el búfer:self.split('_').inject([]){ |buffer,e| buffer + [buffer.empty? ? e : e.capitalize] }.join
Sven Koschnicke
19

Punto de referencia para soluciones puras de Ruby

Aproveché todas las posibilidades que tenía en mente para hacerlo con código ruby ​​puro, aquí están:

  • capitalizar y gsub

    'app_user'.capitalize.gsub(/_(\w)/){$1.upcase}
  • dividir y mapear usando &taquigrafía (gracias a la respuesta del usuario 3869936)

    'app_user'.split('_').map(&:capitalize).join
  • división y mapa (gracias a la respuesta del Sr. Black)

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

Y aquí está el punto de referencia para todos estos, podemos ver que gsub es bastante malo para esto. Usé 126 080 palabras.

                              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
fuente
11

Llegué buscando el inverso de tu pregunta, pasando de un caso de camellos a un caso de serpientes. Use guión bajo para eso (no decamelizar):

AppUser.name.underscore # => "app_user"

o, si ya tiene una cadena de camello:

"AppUser".underscore # => "app_user"

o, si desea obtener el nombre de la tabla, probablemente es por eso que desea el caso de la serpiente:

AppUser.name.tableize # => "app_users"

Miguel
fuente
¿Por qué no usar AppUser.table_name? También se asegurará de tener el verdadero nombre de la tabla si no es app_users, sino algo definido en otra parte.
Ulysse BN
3

Me siento un poco incómodo para agregar más respuestas aquí. Decidí optar por el enfoque de rubí puro más legible y mínimo, sin tener en cuenta el buen punto de referencia de @ ulysse-bn. Si bien el :classmodo es una copia de @ user3869936, el :methodmodo que no veo en ninguna otra respuesta aquí.

  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

El resultado es:

[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
fuente
1
El caso del camello es, de hecho, primero más bajo. De lo contrario, se llama PascalCase (o, a veces, mayúscula de camello). ¡Aunque en esta pregunta es ambiguo!
Ulysse BN
2
@UlysseBN, tbh, no estoy en la historia de las palabras. Wikipedia afirma que PascalCasees un subconjunto de CamelCase. También esto es lo que sabía: ese caso de camello se aplicaba a ambos. Pero nunca he investigado. Sin embargo, gracias por mencionar PascalCase. en.wikipedia.org/wiki/Camel_case
akostadinov
2
Esta es la mejor respuesta en la página de la OMI. Sería bueno si la :methodversión fuera la downcaseprimera, de modo que pueda usarse en ambos lower_snake_casey UPPER_SNAKE_CASE.
skagedal
0

La mayoría de los otros métodos enumerados aquí son específicos de Rails. Si quieres hacer esto con Ruby puro, la siguiente es la forma más concisa que se me ocurrió (gracias a @ ulysse-bn por la mejora sugerida)

x="this_should_be_camel_case"
x.gsub(/(?:_|^)(\w)/){$1.upcase}
    #=> "ThisShouldBeCamelCase"
revs masukomi
fuente
Su definición de "caso camello" es demasiado limitada. Los nombres de clase en Java y Ruby, por ejemplo, son MyFavoriteClass en camello ... pero tampoco tienen una letra inicial en minúscula. a veces el caso del camello tiene tapas iniciales. a veces no lo hace.
masukomi
Usar 2 Regex donde puedes usar solo uno es exagerado. Solo puede usar un grupo no capturador:x.gsub(/(?:_|^)(\w)/){$1.upcase}
Ulysse BN
@UlysseBN, y volvemos a su gsubsolución, parece que es más lenta en comparación con la mapsolución.
akostadinov
0

Extienda la cadena para agregar Camelize

En Ruby puro, podría extender la clase de cadena utilizando exactamente el mismo código 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
fuente