Преобразование строки из snake_case в CamelCase в Ruby


171

Я пытаюсь преобразовать имя из случая змеи в случай верблюда. Есть ли встроенные методы?

Например: "app_user"к"AppUser"

(У меня есть строка, которую "app_user"я хочу преобразовать в модель AppUser).

Ответы:


252

Если вы используете Rails, String # camelize - это то, что вы ищете.

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

Если вы хотите получить реальный класс, вы должны использовать String # constantize поверх этого.

"app_user".camelize.constantize

44
Вы должны добавить, что это дополнение Rails к String, оно не работает с чистым Ruby.
iGEL

2
Он помечен ruby-on-rails, так что, я думаю, это не проблема. Но спасибо за упоминание.
Серхио Туленцев,

6
Вам не нужно кататься на верблюдах, прежде чем констатировать. Используйте #classifyвместо этого. "some_namespace/module/class_name".classify => "SomeNamespace::Module::ClassName"
Крис Хилд

5
@chris #classify: не то же самое. #classify возвращает строку, тогда как #constantize ищет константу в контексте (и нуждается в верблюжьей). «active_record» .constantize выдает ошибку, «active_record». camelize.constantize возвращает константу ActiveRecord, «active_record» .classify возвращает строку «ActiveRecord». И если вы сделали 'no_class'.camelize.constantize, вы получите ошибку (нет такой константы NoClass), но' no_class '.classify с радостью вернет строку' NoClass '.
Канат Болазар

Для того чтобы использовать эти методы Rails из чистого Ruby, require "active_support/core_ext/string"достаточно, если уже установлен Rails.
Маса Сакано

121

Как насчет этого?

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

Найдено в комментариях здесь: классифицировать рубиновые строки

См. Комментарий Уэйна Конрада


11
Вы великолепны, спасибо. Я не хотел включать библиотеки rails только для такой маленькой задачи. Это прекрасно. :)
Джерри

11
Это один из немногих реальных ответов на вопрос. Не использовать библиотеки Rails.
Луис Ортега Аранеда

40

Если вы используете Rails, используйте classify. Хорошо обрабатывает крайние случаи.

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

Примечание:

Этот ответ является специфическим для описания, приведенного в вопросе (он не является специфичным для названия вопроса). Если кто-то пытается преобразовать строку в случай верблюда, он должен использовать ответ Серхио . Спрашивающий заявляет, что он хочет преобразовать app_userв AppUser(не App_user), следовательно, этот ответ ..


4
Для сред Rails это идеально.
Гэйс

Обратите внимание, что classifyвозвращает строку, которую вы должны constantizeвпоследствии вызвать, чтобы преобразовать ее в фактический класс.
Стефан

1
Важным предупреждением classifyявляется то, что множественные строки станут единичными ... 'age_in_years'.classifyстановитсяAgeInYear
br3nt

@ br3nt это не множественное число, так как activerecord4.2.11
Ulysse BN

23

Источник: http://rubydoc.info/gems/extlib/0.9.15/String#camel_case-instance_method

Для учебной цели:

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"

И для варианта в нижнем регистре:

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, если колесо называется ActiveSupport , пожалуйста, изобретите его заново.
Shime

Я думаю, что вариант в нижнем регистре неверен. Блок ввода не должен напрямую манипулировать буфером, а должен возвращать новое значение для буфера:self.split('_').inject([]){ |buffer,e| buffer + [buffer.empty? ? e : e.capitalize] }.join
Sven Koschnicke

19

Тест для чистых решений Ruby

Я использовал все возможности, которые имел в виду, чтобы сделать это с чистым кодом ruby, вот они:

  • использовать заглавные буквы и gsub

    'app_user'.capitalize.gsub(/_(\w)/){$1.upcase}
  • разделить и отобразить с помощью &стенографии (благодаря ответу пользователя 3869936)

    'app_user'.split('_').map(&:capitalize).join
  • раскол и карта (благодаря ответу мистера Блэка)

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

И вот эталон для всего этого, мы видим, что gsub довольно плох для этого. Я использовал 126 080 слов.

                              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

Я попал сюда в поисках обратной стороны вашего вопроса: от верблюжьей до змеиной. Для этого используйте подчеркивание (не декамелизуйте):

AppUser.name.underscore # => "app_user"

или, если у вас уже есть строка с верблюжьим регистром:

"AppUser".underscore # => "app_user"

или, если вы хотите получить имя таблицы, возможно, поэтому вам нужен случай со змеей:

AppUser.name.tableize # => "app_users"


Почему не пользуетесь AppUser.table_name? Вы также убедитесь, что у вас есть истинное имя таблицы, если это не app_users, а что-то определенное в другом месте.
Ulysse BN

3

Я чувствую себя немного неловко, чтобы добавить больше ответов здесь. Решил пойти на наиболее читабельный и минимальный метод чистого рубина, не обращая внимания на хороший тест @ ulysse-bn. Хотя :classmode является копией @ user3869936, этот :methodрежим я не вижу здесь ни в одном другом ответе.

  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

Результат:

[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
Дело верблюда на самом деле сначала ниже. В противном случае он называется PascalCase (или иногда верхним регистром). Хотя в этом вопросе это неоднозначно!
Ulysse BN

2
@ UlysseBN, tbh Я не в истории слов. Википедия утверждает, PascalCaseчто это подмножество CamelCase. И это то, что я знал - этот случай с верблюдом относился к обоим. Но я никогда не исследовал. Спасибо за упоминание PascalCase, хотя. en.wikipedia.org/wiki/Camel_case
akostadinov

2
Это лучший ответ на странице ИМО. Было бы неплохо, если бы :methodверсия делала downcaseпервый, чтобы ее можно было использовать как для, так lower_snake_caseи для UPPER_SNAKE_CASE.
skagedal

0

Большинство других методов, перечисленных здесь, специфичны для Rails. Если вы хотите сделать это с чистым Ruby, ниже приведен самый краткий способ, которым я придумал (спасибо @ ulysse-bn за предложенное улучшение)

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

Ваше определение «дела верблюда» слишком ограничено. Имена классов в Java и, например, Ruby, имеют верблюжий регистр MyFavoriteClass ... но они также не имеют строчной буквы. иногда у верблюжьего чехла есть начальные заглушки иногда это не так.
Масукоми

Использование 2 Regex, где вы можете использовать только один, является излишним. Вы можете использовать только группу без захвата:x.gsub(/(?:_|^)(\w)/){$1.upcase}
Ulysse BN

@ UlysseBN, и мы вернемся к вашему gsubрешению, которое кажется медленнее по сравнению с mapрешением.
Акостадинов

0

Расширить строку, чтобы добавить Camelize

В чистом Ruby вы можете расширить строковый класс, используя точно такой же код из 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
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.