Ruby: Могу ли я написать многострочную строку без конкатенации?

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'

Мол, есть ли способ подразумевать конкатенацию?

Zombies
источник
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"

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

Это не сильно экономит время при наборе текста, но для меня это выглядит лучше, чем использование знаков +.

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

РЕДАКТИРОВАТЬ: Добавление еще одного:

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
Ошибка только в том случае, если конечный идентификатор имеет отступ (дефис указывает интерпретатору ruby ​​обрезать пробелы перед определением конечного идентификатора). Я могу поставить записку с упоминанием этого, хотя. Кроме того, ~ не требуется, gsub \ s + и strip уже удаляют начальные пробелы.
А. Уилсон
Добавление <<~к ответу было бы неплохо, в конечном итоге исследовать это оттуда. Лично я пользуюсь тем, <<~MSG.strip ... MSGчто также лишает последнего \n.
Qortex
1
Когда я писал этот ответ (девять лет назад, боже!), Ruby был на 1.9, а << ~ (очевидно) не был представлен до 2.3. В любом случае, если оставить в стороне древнюю историю, я добавлю ее, спасибо за то, что подняли ее.
А. Уилсон
Спасибо за то, что вы один из немногих ответов, который не добавляет лишних строк, чего я и пытался избежать, когда нашел этот вопрос.
Джош
174

В ruby ​​2.0 теперь можно просто использовать %

Например:

SQL = %{
SELECT user, name
FROM users
WHERE users.id = #{var}
LIMIT #{var2}
}
Робби Гилфойл
источник
14
Работает в Ruby 1.9.3 тоже.
Энди Стюарт
26
Строка, созданная с этим синтаксисом, будет содержать как новые строки, так и любые отступы, добавленные в последующие строки.
Джеймс
Это даже лучше, чем << EOT ...... EOT (здесь документ)! он также выполняет интерполяцию, если это необходимо.
Насер
1
@Nasser Heredoc также выполняет интерполяцию.
Фонд Моника иск
3
Если вы используете Rails, вызов squishдолжен быть полезен.
Джинеш Гоэль
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'

В качестве альтернативы вы можете использовать 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
Марк Байерс
источник
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 multilines.

Однако обратите внимание, что независимо от того, используете ли вы Perl multilines или heredocs, как предлагали Марк и Питер, вы получите потенциально ненужные пробелы. И в моих примерах, и в их примерах строки «from» и «where» содержат начальные пробелы из-за их отступа в коде. Если этот пробел не нужен, вы должны использовать объединенные строки, как вы делаете сейчас.

Hongli
источник
4
from # {table_names} не будет работать в этом примере, так как вы использовали% q {}, он будет работать, если вы использовали% q [] или ()
MatthewFord
2
Мой фаворит в этом смысле просто% {супер многострочная строка с поддержкой интерполяции}
Duke
строки, полученные из %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", используйте обратную косую черту "\" в конце каждой строки.

juliangonzalez
источник
5
Вы можете достичь того же результата с двойными кавычками. В Ruby нет такой вещи, как тройные двойные кавычки. Это просто интерпретирует их как "" + "double quotes with some content" + "".
Раквиум
Да, но `" "+" \ n hello \ n "+" "
Выглядит
1
Да, это выглядит странно, и поэтому нет необходимости добавлять дополнительные двойные кавычки, когда вы можете просто использовать одинарные двойные кавычки с одинаковым результатом.
Раквиум
Да, я имел в виду знак плюс. Двойные кавычки без него выглядят просто отлично, их читабельно и легче найти вместо одинарных кавычек, которые должны использоваться в однострочных строках.
Juliangonzalez
1
Я имею в виду, что это "x"выглядит лучше и работает быстрее, чем """x"""(что в основном так же, как ""+"x"+"") или """""x"""""(что так же, как "" + "" + "x" + "" + ""). Это Ruby, а не Python, где вы используете """вместо того, "когда вам нужна многострочная строка.
Раквиум
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(если вы используете rails) позволит вам писать многострочные записи хорошим способом! в случае просто использования ruby, вы можете сделать, <<~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-heroc

Марк Жад
источник
хлам это рельсы, а не рубин
Джош
1
@ Джош, да, ты прав, обновил ответ, ура.
Марк Джейд
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
+является регулярным оператором конкатенации, <<является на месте оператора Append. Использование побочных эффектов для литерала здесь работает (первая строка дважды модифицируется и возвращается), но ИМХО это странно и заставляет меня сделать двойной дубль, где +было бы совершенно ясно. Но, может быть, я просто новичок в Ruby ...
Бени Чернявский-Паскин
Это не будет работать, если frozen_string_literalвключено
Райдо
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 для интерполированных строк)

UncleGene
источник
Мне очень нравится этот, потому что он позволяет гораздо больше комбинаций использования.
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"
Pwnrar
источник
Это один из немногих ответов на этой странице, который действительно отвечает на вопрос!
Джош
4
conn.exec [
  "select attr1, attr2, attr3, ...",
  "from table1, table2, table3, ...",
  "where ..."
].join(' ')

Это предложение имеет преимущество перед документами и длинными строками, заключающееся в том, что авто-отступы могут соответствующим образом создавать отступ для каждой части строки. Но это происходит за счет эффективности.

Эйдан Калли
источник
@Aidan, Вы можете заменить запятые на обратную косую черту (а-ля C), и соединение не потребуется (или массив): интерпретатор объединит строки во время (я думаю) анализа, что делает его довольно быстрым по сравнению с большинством альтернатив , Однако одним из преимуществ присоединения массива строк является то, что некоторые автоиндентификаторы работают лучше, чем, например, со строками here-doc или с \.
Уэйн Конрад
1
Одно замечание, синтаксис heredoc << - позволит сделать соответствующие отступы.
А. Уилсон
3

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, так и кавычки и строковый литерал процента), то можно использовать продолжение строки , поместив обратную косую черту \в качестве последнего непробельного символа в строке. Это продолжит строку и заставит 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...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...attr7 from...etc, where..."

Больше деталей:

Рубиновый синтаксис HEREDOC

Работы Here Document Notation for Strings - это способ обозначить длинные блоки текста, встроенные в код. За ним <<следует пользовательская строка (терминатор конца строки). Все последующие строки объединяются до тех пор, пока терминатор конца строки не будет найден в самом начале строки:

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

Терминатор конца строки может быть выбран свободно, но обычно используется что-то вроде «EOS» (конец строки) или что-то, что соответствует домену строки, например «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является то, что терминатор конца строки должен находиться в начале строки:

  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, в моем случае они небезопасны, и у меня есть большой блок текста, чтобы иметь дело с ним)

Возможно, это так же уродливо, но вы можете использовать обратную черту-обратную черту в heredoc, чтобы исключить их из полученной строки:

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

Обратите внимание, что вы не можете сделать это без интерполяции (IE <<~'END_OF_INPUT'), поэтому будьте осторожны. #{expressions}будет оцениваться здесь, тогда как они не будут в вашем исходном коде. Ответ А. Уилсона может быть лучше по этой причине.

мистифицировать
источник