Рубі: Чи можна писати багаторядкові рядки без конкатенації?


397

Чи є спосіб зробити цей вигляд трохи кращим?

conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' +
          'from table1, table2, table3, etc, etc, etc, etc, etc, ' +
          'where etc etc etc etc etc etc etc etc etc etc etc etc etc'

Мовляв, чи є спосіб підказати конкатенацію?


28
Будьте обережні щодо атак на ін'єкції SQL. :)
Рой Тінкер

Відповіді:


595

Є відповіді на цю відповідь, які допомогли мені отримати те, що мені потрібно (легке багаторядкове з'єднання БЕЗ зайвого пробілу), але оскільки жодної з дійсних відповідей цього не було, я складаю їх тут:

str = 'this is a multi-line string'\
  ' using implicit concatenation'\
  ' to prevent spare \n\'s'

=> "this is a multi-line string using implicit concatenation to eliminate spare
\\n's"

Як бонус, ось версія з кумедним синтаксисом HEREDOC (за цим посиланням ):

p <<END_SQL.gsub(/\s+/, " ").strip
SELECT * FROM     users
         ORDER BY users.id DESC
END_SQL
# >> "SELECT * FROM users ORDER BY users.id DESC"

Останнє здебільшого стосується ситуацій, які потребують більшої гнучкості в обробці. Мені особисто це не подобається, він ставить обробку в дивне місце wrt рядок (тобто перед ним, але використовуючи методи екземпляра, які зазвичай надходять після цього), але він є. Зауважте, що якщо ви вводите відступ останнього END_SQLідентифікатора (що є загальним, оскільки це, мабуть, всередині функції чи модуля), вам знадобиться використовувати дефіс синтаксис (тобто p <<-END_SQLзамість p <<END_SQL). В іншому випадку пробіл відступу викликає інтерпретацію ідентифікатора як продовження рядка.

Це не економить на друці, але це виглядає приємніше, ніж використання знаків +.

Також (я кажу в редакції, кілька років пізніше), якщо ви використовуєте Ruby 2.3+, також доступний оператор << ~ , який видаляє зайвий відступ із заключного рядка. У цьому випадку ви зможете видалити .gsubвиклик (хоча це може залежати як від початкового відступу, так і від ваших кінцевих потреб).

EDIT: Додавання ще одного:

p %{
SELECT * FROM     users
         ORDER BY users.id DESC
}.gsub(/\s+/, " ").strip
# >> "SELECT * FROM users ORDER BY users.id DESC"

2
Це давнє запитання, АЛЕ у відповіді є помилка або відбулася зміна синтаксису. p <<END_SQLмає бути p <<-END_SQLінакше це відповідь. необов'язково, ви можете зняти провідну пробіл разом із оперативним оператором HEREDOC,<<~END_SQL
jaydel

Помилка лише в тому випадку, якщо ідентифікатор закінчення відведений (дефіс повідомляє інтерпретатору рубіну обрізати пробіл перед тим, як визначити кінцевий ідентифікатор). Я можу помітити примітку, згадуючи про це. Крім того, ~ не потрібен, gsub \ s + і смуга вже видаляють провідний пробіл.
А. Вілсон

Додавання <<~відповіді було б добре, і закінчилося дослідження, що звідти. Особисто я використовую <<~MSG.strip ... MSGякий також знімає останнє \n.
Qortex

1
Коли я написав цю відповідь (дев'ять років тому, так!), Рубі був на 1.9, і << ~ (очевидно) не був представлений до 2.3. У будь-якому випадку, давню історію, я відкладу, дякую, що виклали її.
А. Вілсон

Дякую, що ви одна з небагатьох відповідей, яка не додає зайвих нових рядків, чого я намагався уникнути, коли знайшов це запитання.
Джош

174

У рубіні 2.0 тепер ви можете просто використовувати %

Наприклад:

SQL = %{
SELECT user, name
FROM users
WHERE users.id = #{var}
LIMIT #{var2}
}

14
Працює і в Ruby 1.9.3.
Енді Стюарт

26
Рядок, створений за допомогою цього синтаксису, включає як нові рядки, так і будь-який відступ, доданий до наступних рядків.
Джеймс

Це навіть краще, ніж << EOT ...... EOT (тут документ)! вона також робить інтерполяцію, якщо це необхідно.
Насер

1
@Nasser Гередок також робить інтерполяцію.
Позов про позов Моніки

3
Якщо використовувати Rails, виклик squishна висновку повинен бути корисним.
Jignesh Gohel

167

Так, якщо ви не заперечуєте, щоб додаткові нові рядки вставлялися:

 conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7
            from table1, table2, table3, etc, etc, etc, etc, etc,
            where etc etc etc etc etc etc etc etc etc etc etc etc etc'

Можна також використовувати гередок :

conn.exec <<-eos
   select attr1, attr2, attr3, attr4, attr5, attr6, attr7
   from table1, table2, table3, etc, etc, etc, etc, etc,
   where etc etc etc etc etc etc etc etc etc etc etc etc etc
eos

87
Ви також можете скористатися%Q(...)
BaroqueBobcat

3
@ Zombies: Нові рядки зазвичай дозволені в операторах SQL і просто розглядаються як звичайні пробіли.
Марк Байєрс

2
див. мою відповідь нижче для прикладу, ви можете просто використовувати% зараз.
Роббі Гільфойл

4
Ви також можете скористатися%(...)
нульовим дільником

1
Що важливо пам’ятати, якщо ви навмисно додаєте пробіли пробілу та використовуєте одне з цих рішень, це те, що ваш редактор може автоматично видаляти простір під час збереження файлу. Хоча я зазвичай віддаю перевагу такій поведінці, це кілька разів викликало у мене несподівані проблеми. Рішення полягає в тому, щоб написати багаторядковий рядок, як, наприклад, ОП у запитанні.
Денніс

50

Існує кілька синтаксисів для багаторядкових рядків, як ви вже читали. Мій улюблений стиль Perl:

conn.exec %q{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
      from table1, table2, table3, etc, etc, etc, etc, etc,
      where etc etc etc etc etc etc etc etc etc etc etc etc etc}

Багаторядковий рядок починається з% q, а потім символом {, [або (і потім закінчується відповідним зворотним символом.% Q не дозволяє інтерполяцію;% Q робить так, що ви можете писати такі речі:

conn.exec %Q{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
      from #{table_names},
      where etc etc etc etc etc etc etc etc etc etc etc etc etc}

Я фактично не маю уявлення, як називаються такі різновиди багаторядкових рядків, так що давайте просто назвемо їх багатояйцевими Perl.

Зауважте, однак, що якщо ви використовуєте мультилінії Perl або гередоки, як запропонували Марк і Петро, ​​у вас виникнуть непотрібні пробіли. Як у моїх прикладах, так і в їхніх прикладах рядки "від" та "де" містять провідні пробіли через їх відступ у коді. Якщо цей пробіл не бажаний, ви повинні використовувати об'єднані рядки, як зараз.


4
from # {table_names} не працюватиме в цьому прикладі, як ви використовували% q {}, він би працював, якщо ви використовували% q [] або ()
MatthewFord

2
Мій улюблений у цій жилці - це просто% {супер багаторядковий рядок з підтримкою інтерполяції}
герцог

Рядки, створені з %qсімейства, включатимуть нові рядки, що не еквівалентно вихідному коду.
Джош

29

Іноді варто видалити нові символи рядка, \nтакі як:

conn.exec <<-eos.squish
 select attr1, attr2, attr3, attr4, attr5, attr6, attr7
 from table1, table2, table3, etc, etc, etc, etc, etc,
 where etc etc etc etc etc etc etc etc etc etc etc etc etc
eos

5
це рейки на основі не рубінові
a14m

23

Ви також можете використовувати подвійні лапки

x = """
this is 
a multiline
string
"""

2.3.3 :012 > x
 => "\nthis is\na multiline\nstring\n"

Якщо потрібно для усунення розривів рядків, \ n "використовуйте зворотний проріз" \ "в кінці кожного рядка


5
Такого ж результату можна досягти за допомогою подвійних лапок. У Рубі немає такого поняття, як потрійні подвійні цитати. Це просто інтерпретує їх як "" + "double quotes with some content" + "".
rakvium

Так, але `" "+" \ n привіт \ n "+" "Чи виглядає це дивно
juliangonzalez

1
Так, це виглядає дивно, і саме тому немає підстав додавати додаткові подвійні лапки, коли ви можете просто використовувати одинарні подвійні лапки з тим самим результатом.
rakvium

Так, я мав на увазі знак плюс. Подвійні лапки без нього виглядають просто чудово, їх можна прочитати і їх легше помітити замість однієї цитати, яку слід використовувати в рядках рядка.
juliangonzalez

1
Я маю на увазі, що просто "x"виглядає краще і працює швидше, ніж """x"""(що в основному те саме ""+"x"+"") або """""x"""""(що таке саме "" + "" + "x" + "" + ""). Це Ruby, а не Python, де ви використовуєте """замість того, "коли вам потрібна багаторядкова струна.
rakvium

15
conn.exec = <<eos
  select attr1, attr2, attr3, attr4, attr5, attr6, attr7
  from table1, table2, table3, etc, etc, etc, etc, etc,
  where etc etc etc etc etc etc etc etc etc etc etc etc etc
eos

1
використання heredoc без символу "-", як у "<< - eos", включатиме додаткові провідні простори. див. відповідь Марка Байєрса.
ives

heredoc буде включати нові рядки, що не еквівалентно вихідному коду.
Джош

15

Інші варіанти:

#multi line string
multiline_string = <<EOM
This is a very long string
that contains interpolation
like #{4 + 5} \n\n
EOM

puts multiline_string

#another option for multiline string
message = <<-EOF
asdfasdfsador #{2+2} this month.
asdfadsfasdfadsfad.
EOF

puts message

1
Повинен чи змінити <<EOMдо <<-EOM, ні?
kingPuppy

Можливо, здавалося, це працює для мого <<-EOFприкладу. Я здогадуюсь, що будь-який спосіб працює.
Алекс Коен

heredoc буде включати нові рядки, що не еквівалентно вихідному коду.
Джош

11

Нещодавно, завдяки новим функціям у Ruby 2.3, нове squiggly HEREDOCдозволить вам писати наші багаторядкові рядки красивим чином з мінімальними змінами, тому використання цього в поєднанні з .squish(якщо ви використовуєте рейки) дозволить вам писати багаторядкові в хороший спосіб! у випадку просто використання рубіну, ви можете зробити те, <<~SQL.split.join(" ")що майже те саме

[1] pry(main)> <<~SQL.squish
[1] pry(main)*   select attr1, attr2, attr3, attr4, attr5, attr6, attr7
[1] pry(main)*   from table1, table2, table3, etc, etc, etc, etc, etc,
[1] pry(main)*   where etc etc etc etc etc etc etc etc etc etc etc etc etc
[1] pry(main)* SQL
=> "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 from table1, table2, table3, etc, etc, etc, etc, etc, where etc etc etc etc etc etc etc etc etc etc etc etc etc"

посилання: https://infinum.co/the-capsized-eight/multiline-strings-ruby-2-3-0-the-squiggly-heredoc


кабачки - це рейки, а не рубіни
Джош

1
@Josh, так, ти маєш рацію, оновив відповідь, привіт.
Позначте Джад

6
conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' <<
        'from table1, table2, table3, etc, etc, etc, etc, etc, ' <<
        'where etc etc etc etc etc etc etc etc etc etc etc etc etc'

<< - оператор конкатенації рядків


2
+є регулярним оператором з'єднання, <<є оператором додавання на місці . Використання побічних ефектів у буквальному сенсі спрацьовує тут (перша рядок змінюється двічі та повертається), але IMHO це дивно і змушує мене зробити подвійний процес, де +було б абсолютно зрозуміло. Але, можливо, я просто новачок у Рубі ...
Бені Чернявський-Паскін

Це не працюватиме, якщо frozen_string_literalввімкнено
Raido

6

Якщо ви не заперечуєте додаткових пробілів та нових рядків, ви можете використовувати

conn.exec %w{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
  from table1, table2, table3, etc, etc, etc, etc, etc,
  where etc etc etc etc etc etc etc etc etc etc etc etc etc} * ' '

(використовуйте% W для інтерпольованих рядків)


Мені це дуже подобається, тому що він дозволяє набагато більше комбінацій використання.
schmijos

1
Це дозволить скріпити кілька сусідніх просторів в одне ціле. (Скріплення нового рядка + наступне відступ - це перемога, але в середині рядка це може бути дивно.)
Бені Чернявський-Паскін

5

Щоб уникнути закриття дужок для кожного рядка, ви можете просто скористатися подвійними лапками з зворотною косою рисою для виходу з нового рядка:

"select attr1, attr2, attr3, attr4, attr5, attr6, attr7 \
from table1, table2, table3, etc, etc, etc, etc, etc, \
where etc etc etc etc etc etc etc etc etc etc etc etc etc"

Це одна з небагатьох відповідей на цій сторінці, яка насправді відповідає на питання!
Джош

4
conn.exec [
  "select attr1, attr2, attr3, ...",
  "from table1, table2, table3, ...",
  "where ..."
].join(' ')

Ця пропозиція має перевагу перед тут документами та довгими рядками, завдяки чому автоматичні відступи можуть відступити кожну частину рядка відповідним чином. Але це коштує з ефективністю.


@Aidan, Ви можете замінити коми косою косою рисою (a la C), і жодне з'єднання (або масив) не знадобиться. . Одна з переваг приєднання до масиву рядків полягає в тому, що деякі автоіндентори роблять кращу роботу, ніж це, наприклад, тут-doc-рядки або з \.
Уейн Конрад

1
Одна примітка, синтаксис << гередок << - дозволить відповідне відступ.
А. Вілсон

2

Ruby-way (TM), починаючи з Ruby 2.3: щоб визначити багаторядковий рядок з новими рядками та належним ідентифікацією, використовуйте чітко HEREDOC <<~ :

conn.exec <<~EOS
            select attr1, attr2, attr3, attr4, attr5, attr6, attr7
            from table1, table2, table3, etc, etc, etc, etc, etc
            where etc etc etc etc etc etc etc etc etc etc etc etc etc
          EOS

# -> "select...\nfrom...\nwhere..."

Якщо правильне ідентифікація не викликає занепокоєння, то одинарні та подвійні лапки можуть охоплювати кілька рядків у Ruby:

conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 
           from table1, table2, table3, etc, etc, etc, etc, etc, 
           where etc etc etc etc etc etc etc etc etc etc etc etc etc"    

# -> "select...\n           from...\n           where..."

Якщо одинарні або подвійні лапки є громіздкими, оскільки для цього знадобиться багато пробігу, то буквальне позначення відсоткових рядків %є найбільш гнучким рішенням:

conn.exec %(select attr1, attr2, attr3, attr4, attr5, attr6, attr7
            from table1, table2, table3, etc, etc, etc, etc, etc
            where (ProductLine = 'R' OR ProductLine = "S") AND Country = "...")
# -> "select...\n            from...\n            where..."

Якщо мета - уникнути нових рядків (що спричинить як чіткі HEREDOC, цитати, так і відсотковий буквальний ряд), то зворотний проріз як останній символ, що не пробігає, продовжить рядок і змусить Рубі об'єднати рядки назад у спину (стежте за пробілами всередині цитованого рядка):

conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' \
          'from table1, table2, table3, etc, etc, etc, etc, etc, ' \
          'where etc etc etc etc etc etc etc etc etc etc etc etc etc'

# -> "select...from...where..."

Якщо ви використовуєте Rails, String.squishто викресліть рядок провідного та останнього простору та згорнете всі послідовні пробіли (нові рядки, вкладки та всі) в єдиний пробіл:

conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 
           from table1, table2, table3, etc, etc, etc, etc, etc, 
           where etc etc etc etc etc etc etc etc etc etc etc etc etc".squish

# -> "select...from...where..."

Детальніше:

Синтаксис Ruby HEREDOC

Позначення документа тут для рядків - це спосіб позначити довгі блоки тексту в коді. Запускається за <<ним, визначений користувачем String (термінатор End of String). Усі наступні рядки з'єднуються, поки на самому початку рядка не знайдеться термінатор End of String :

puts <<HEREDOC 
Text Text Text Text
Bla Bla
HEREDOC
# -> "Text Text Text Text\nBlaBla"

Термінатор End of String можна вибирати вільно, але звичайно використовувати щось на зразок "EOS" (End of String) або щось, що відповідає домену String, наприклад "SQL".

HEREDOC підтримує інтерполяцію за замовчуванням або тоді, коли термінатор EOS подвійно цитується:

price = 10
print <<"EOS"  # comments can be put here
1.) The price is #{price}.
EOS
# -> "1.) The price is 10."

Інтерполяцію можна відключити, якщо термінатор EOS одинарний:

print <<'EOS' # Disabled interpolation
3.) The price is #{price}.
EOS
# -> "3.) The price is #{price}."

Одне важливе обмеження <<HEREDOCполягає в тому, що термінатор End of String повинен бути на початку рядка:

  puts <<EOS 
    def foo
      print "foo"
    end
  EOS
EOS
#-> "....def foo\n......print "foo"\n....end\n..EOS

Щоб обійти це, <<-було створено синтаксис. Це дозволяє розширити термінатор EOS, щоб зробити вигляд кодом гарнішим. Рядки між <<-термінатором та терміналом EOS досі використовуються в повному обсязі, включаючи всі відступи:

puts <<-EOS # Use <<- to indent End of String terminator
  def foo
    print "foo"
  end
EOS
# -> "..def foo\n....print "foo"\n..end"

З Ruby 2.3 ми тепер чітко HEREDOC <<~видаляє провідні пробіли:

puts <<~EOS # Use the squiggly HEREDOC <<~ to remove leading whitespace (since Ruby 2.3!)
  def foo
    print "foo"
  end
EOS
# -> "def foo\n..print "foo"\nend"

Порожні рядки та рядки, які містять лише вкладки та пробіл, << ігноруються

puts <<~EOS.inspect 
  Hello

    World!
EOS
#-> "Hello\n..World!"

Якщо використовуються і вкладки, і пробіли, вкладки вважаються рівними 8 пробілам. Якщо рядок з найменшим відступом знаходиться в середині вкладки, ця вкладка не видаляється.

puts <<~EOS.inspect
<tab>One Tab
<space><space>Two Spaces
EOS
# -> "\tOne Tab\nTwoSpaces"

HEREDOC може виконувати деякі шалені речі, такі як виконання команд за допомогою зворотних посилань:

puts <<`EOC`            
echo #{price}
echo #{price * 2}
EOC

Визначення рядка HEREDOC можна "скласти", що означає, що перший термінатор EOS (EOSFOO нижче) закінчить перший рядок і запустить другий (EOSBAR нижче):

print <<EOSFOO, <<EOSBAR    # you can stack them
I said foo.
EOSFOO
I said bar.
EOSBAR

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

def func(a,b,c)
  puts a
  puts b
  puts c
end

func(<<THIS, 23, <<THAT) 
Here's a line
or two.
THIS
and here's another.
THAT

Якщо у вас немає Ruby 2.3, але Rails >=3.0, ви можете використовувати те, String.strip_heredocщо робить те ж саме<<~

# File activesupport/lib/active_support/core_ext/string/strip.rb, line 22
class String
  def strip_heredoc
    gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
  end
end

puts <<-USAGE.strip_heredoc # If no Ruby 2.3, but Rails >= 3.0
  This command does such and such.

  Supported options are:
    -h         This message
    ...
USAGE

Відсотні рядкові літерали

Див RubyDoc про те , як використовувати знак відсотка , за яким слідує рядок в парі дужок , таких як %(...), %[...],%{...} і т.д. , або пару будь-якого НЕ алфавітно - цифровий символ , такі як%+...+

Останні слова

Нарешті, щоб отримати відповідь на початкове запитання "Чи є спосіб припускати конкатекацію?" відповів: Ruby завжди має на увазі конкатенацію, якщо два рядки (одна і подвійна цитата) знайдені назад до спини:

puts "select..." 'from table...' "where..."
# -> "select...from table...where..."

Застереження полягає в тому, що це не працює при перервах рядків, тому що Ruby інтерпретує закінчення твердження, а послідовний рядок лише рядків у рядку нічого не робить.


1

Елегантний відповідь сьогодні:

<<~TEXT
Hi #{user.name}, 

Thanks for raising the flag, we're always happy to help you.
Your issue will be resolved within 2 hours.
Please be patient!

Thanks again,
Team #{user.organization.name}
TEXT

Існує відмінність у, <<-TEXTі <<~TEXTколишній зберігає відстань всередині блоку, а другий - ні.

Є й інші варіанти. Як конкатенація тощо, але це має більше сенсу в цілому.

Якщо я тут помиляюся, дайте мені знати, як ...


heredoc буде включати нові рядки, що не еквівалентно вихідному коду.
Джош

1

Як і ви, я також шукав рішення, яке не включає нові рядки . (Хоча вони можуть бути безпечними в SQL, в моєму випадку вони не безпечні, і у мене є великий блок тексту для вирішення)

Це, мабуть, так само некрасиво, але ви можете відхилити нові рядки в гередоку, щоб опустити їх із отриманого рядка:

conn.exec <<~END_OF_INPUT
    select attr1, attr2, attr3, attr4, attr5, attr6, attr7 \
    from table1, table2, table3, etc, etc, etc, etc, etc, \
    where etc etc etc etc etc etc etc etc etc etc etc etc etc
  END_OF_INPUT

Зауважте, що ви не можете цього <<~'END_OF_INPUT'пояснити без інтерполяції (IE ), тому будьте обережні. #{expressions}буде оцінено тут, тоді як вони не будуть у вашому початковому коді. Відповідь А. Вілсона може бути кращою з цієї причини.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.