Перетворення рядка з snake_case в CamelCase в Ruby


171

Я намагаюсь перетворити ім’я з корпусу змії на футляр верблюда. Чи є вбудовані методи?

Напр .: "app_user"до"AppUser"

(У мене є рядок, який "app_user"я хочу перетворити на модель AppUser).

Відповіді:


251

Якщо ви використовуєте 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 шукає константу в контексті (і це потрібно comelize). '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 вже встановлений.
Маса Сакано

120

Як щодо цього?

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

Знайдено в коментарях тут: Класифікуйте рядок Ruby

Дивіться коментар Уейна Конрада


10
Ви дивовижні, дякую. Мені не хотілося включати бібліотеки рейок лише для цього невеликого завдання. Це прекрасно. :)
Геррі

11
Це одна з єдино реальних відповідей на питання. Не використовується бібліотеки Rails.
Луїс Ортега Аранеда

40

Якщо ви використовуєте Rails, використовуйте classify. Він добре обробляє крайові корпуси.

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

Примітка:

Ця відповідь є специфічною для опису, даного у запитанні (він не специфічний для назви питання). Якщо хтось намагається перетворити рядок у верблюд, він повинен відповісти Серхіо . Допитуючий заявляє, що хоче перейти app_userна AppUser(не App_user), звідси ця відповідь.


4
Для середовищ Rails це ідеально.
ghayes

Зверніть увагу, що classifyповертаючи рядок, вам доведеться зателефонувати constantizeпотім, щоб перетворити її у фактичний клас.
Стефан

1
Важливим застереженням classifyє те, що 'age_in_years'.classifyAgeInYear
множинні

@ br3nt це не плюралізується, оскільки activerecord4.2.11
Ulysse BN

23

Джерело: http://rubydoc.info/gems/extlib/0.9.15/String#camel_case-instan_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"

А для нижнього варіантуCase:

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

Я скористався всіма можливостями, які мав на увазі, щоб зробити це за допомогою чистого рубінового коду, ось вони:

  • капіталізувати і 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, а щось визначене в іншому місці.
Уліссе БН

3

Мені трохи неприємно додавати сюди більше відповідей. Вирішив піти на найбільш читаний і мінімально чистий рубіновий підхід, не зважаючи на приємний орієнтир від @ ulysse-bn. Хоча :classрежим є копією @ 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 (або іноді верхній корпус верблюда). Хоча в цьому питанні це неоднозначно!
Уліссе БН

2
@UlysseBN, tbh Я не в історії слів. Вікіпедія стверджує PascalCase, що це підмножина CamelCase. І це те, що я знав, - той випадок верблюда стосувався обох. Але я ніколи не досліджував. Дякуємо, що згадали про PascalCase. en.wikipedia.org/wiki/Camel_case
akostadinov

2
Це найкраща відповідь на сторінці imo. Було б добре, якби :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 ..., але вони також не мають нижньої літери початкової літери. іноді у верблюжої футляра є початкові кришки. іноді це не так.
masukomi

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

@UlysseBN, і ми повернулися до вашого gsubрішення, схоже, це повільніше порівняно з mapрішенням.
акостадінов

0

Розширити рядок, щоб додати камеру

У чистому 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.