Ruby: Posso escrever uma sequência de linhas múltiplas sem concatenação?

397

Existe uma maneira de fazer isso parecer um pouco melhor?

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'

Tipo, existe uma maneira de implicar concatenação?

Zumbis
fonte
28
Tenha cuidado com os ataques de injeção de SQL. :)
Roy Tinker

Respostas:

595

Há algumas respostas para essa resposta que me ajudaram a obter o que eu precisava (concatenação fácil de várias linhas sem espaço em branco extra), mas como nenhuma das respostas reais tinha isso, estou compilando-as aqui:

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"

Como bônus, aqui está uma versão usando a sintaxe HEREDOC engraçada (através deste link ):

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

O último seria principalmente para situações que exigiam mais flexibilidade no processamento. Eu pessoalmente não gosto, ele coloca o processamento em um lugar estranho, usando a string (ou seja, na frente dela, mas usando métodos de instância que geralmente vêm depois), mas está lá. Observe que, se você estiver recuando o último END_SQLidentificador (o que é comum, já que provavelmente isso está dentro de uma função ou módulo), você precisará usar a sintaxe hifenizada (ou seja, em p <<-END_SQLvez de p <<END_SQL). Caso contrário, o espaço em branco recuado fará com que o identificador seja interpretado como uma continuação da sequência.

Isso não economiza muita digitação, mas parece melhor do que usar sinais de + para mim.

Além disso (digo em uma edição, vários anos depois), se você estiver usando Ruby 2.3+, o operador << ~ também estará disponível , o que removerá o recuo extra da string final. .gsubNesse caso, você poderá remover a chamada (embora possa depender do recuo inicial e das suas necessidades finais).

EDIT: Adicionando mais um:

p %{
SELECT * FROM     users
         ORDER BY users.id DESC
}.gsub(/\s+/, " ").strip
# >> "SELECT * FROM users ORDER BY users.id DESC"
A. Wilson
fonte
2
Esta é uma pergunta antiga, MAS ou há um erro na resposta ou houve uma alteração na sintaxe desde então. p <<END_SQLdeve ser p <<-END_SQLCaso contrário, esta é a resposta. opcionalmente pode retirar os espaços em branco com o operador heredoc ondulada,<<~END_SQL
jaydel
É apenas um erro se o identificador final estiver recuado (o hífen diz ao intérprete ruby ​​para aparar o espaço em branco antes de fazer a determinação do identificador final). Eu posso colocar uma nota mencionando isso, no entanto. Além disso, o ~ é desnecessário, gsub \ s + e strip já estão removendo os espaços em branco à esquerda.
A. Wilson
Acrescentar <<~à resposta seria bom, acabou pesquisando a partir daí. Pessoalmente, eu uso o <<~MSG.strip ... MSGque também tira o último \n.
Qortex 24/04
11
Quando escrevi esta resposta (nove anos atrás, sheesh!), Ruby estava no 1.9, e << ~ (evidentemente) não foi introduzido até o 2.3. De qualquer forma, a história antiga de lado, eu vou colocar, obrigado por trazer isso à tona.
A. Wilson
Obrigado por ser uma das poucas respostas que não adiciona novas linhas extras, o que eu estava tentando evitar ao encontrar essa pergunta.
Josh
174

No ruby ​​2.0, agora você pode apenas usar %

Por exemplo:

SQL = %{
SELECT user, name
FROM users
WHERE users.id = #{var}
LIMIT #{var2}
}
Robbie Guilfoyle
fonte
14
Também funciona no Ruby 1.9.3.
Andy Stewart
26
Uma sequência criada com esta sintaxe incluirá novas linhas e qualquer recuo adicionado às linhas subseqüentes.
James
Isso é ainda melhor que << EOT ...... EOT (aqui documento)! também interpola, se necessário.
Nasser
11
@Nasser Um heredoc também interpola.
Fund Monica's Lawsuit
3
Se usar o Rails, chamar squishna saída deve ser útil.
Jignesh Gohel
167

Sim, se você não se importa com as novas linhas extras sendo inseridas:

 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'

Como alternativa, você pode usar um 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
Mark Byers
fonte
87
Você também pode usar%Q(...)
BaroqueBobcat 25/02
3
@ Zombies: as novas linhas geralmente são permitidas nas instruções SQL e são tratadas apenas como espaços em branco comuns.
22810 Mark Orers
2
veja minha resposta abaixo para um exemplo, você pode usar% agora.
Robbie Guilfoyle
4
Você também pode usar%(...)
zero divisor
11
Algo importante a ter em mente se você adicionar intencionalmente espaços em branco à direita e usar uma dessas soluções é que seu editor pode remover automaticamente o espaço à direita ao salvar o arquivo. Embora eu normalmente prefira esse comportamento, ele causou problemas inesperados para mim algumas vezes. Uma solução é escrever sua string de várias linhas, como o OP fez na pergunta.
Dennis
50

Existem várias sintaxes para cadeias de caracteres de várias linhas, como você já leu. O meu favorito é o estilo 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}

A sequência de linhas múltiplas começa com% q, seguida por {, [ou (, e depois finalizada pelo caractere invertido correspondente.% Q não permite interpolação;% Q faz para que você possa escrever coisas como estas:

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}

Na verdade, eu não tenho idéia de como esses tipos de cadeias de linhas múltiplas são chamados, então vamos chamá-los de multilinhas Perl.

Observe, no entanto, que, se você usa multilinhas Perl ou heredocs, como Mark e Peter sugeriram, você terá espaços em branco potencialmente desnecessários. Nos meus exemplos e nos exemplos deles, as linhas "de" e "onde" contêm espaços em branco à esquerda devido ao recuo no código. Se esse espaço em branco não for desejado, você deve usar seqüências de caracteres concatenadas como está fazendo agora.

Hongli
fonte
4
de # {table_names} não iria funcionar neste exemplo, como você costumava% q {}, ele iria trabalhar se você% q usado [] ou ()
MatthewFord
2
O meu favorito nesta veia é apenas% {string Super multilinha com o apoio de interpolação}
Duke
strings produzidas a partir da %qfamília incluirão as novas linhas que não são equivalentes ao código original.
Josh
29

Às vezes, vale a pena remover novos caracteres de linha, \ncomo:

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
Kamil Lelonek
fonte
5
este é trilhos não baseado rubi
a14m
23

Você também pode usar aspas duplas

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

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

Se necessário, para remover quebras de linha "\ n", use barra invertida "\" no final de cada linha

juliangonzalez
fonte
5
Você pode obter o mesmo resultado com aspas duplas no singular. Não existe aspas duplas triplas em Ruby. Apenas os interpreta como "" + "double quotes with some content" + "".
Rakvium
Sim, mas ` "" + "\ n Olá \ n "+"" parece estranho
juliangonzalez
11
Sim, parece estranho, e é por isso que não há razão para adicionar aspas duplas extras quando você pode apenas usar aspas duplas no singular com o mesmo resultado.
Rakvium 12/11/2018
Sim, eu quis dizer o sinal de mais. As aspas duplas sem ela ficam bem, são legíveis e mais fáceis de localizar, em vez de uma aspas simples, que devem ser usadas em strings de linha única.
juliangonzalez
11
Quero dizer que apenas "x"parece melhor e funciona mais rápido que """x"""(que é basicamente o mesmo que ""+"x"+"") ou """""x"""""(que é o mesmo que "" + "" + "x" + "" + ""). É Ruby, não Python, onde você usa, e """não "quando precisa de uma string de várias linhas.
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
Pedro
fonte
11
o uso do heredoc sem o '-', como em '<< - eos', incluirá os espaços de líder adicionais. veja a resposta de Mark Byers.
Ives 02/02
O heredoc incluirá as novas linhas que não são equivalentes ao código original.
Josh
15

Outras opções:

#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
Alex Cohen
fonte
11
Deve mudar <<EOMpara <<-EOM, não?
kingPuppy
Talvez parecesse funcionar para o meu <<-EOFexemplo. Meu palpite é que de qualquer maneira funciona.
Alex Cohen #
O heredoc incluirá as novas linhas que não são equivalentes ao código original.
Josh
11

Recentemente, com os novos recursos do Ruby 2.3, o novo squiggly HEREDOCpermitirá que você escreva nossas seqüências multilinhas de uma maneira agradável com uma alteração mínima; portanto, usando isso combinado com o .squish(se você estiver usando trilhos) permitirá que você escreva multilinhas de uma maneira agradável! no caso de apenas usar ruby, você pode fazer um <<~SQL.split.join(" ")que é quase o mesmo

[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"

ref: https://infinum.co/the-capsized-eight/multiline-strings-ruby-2-3-0-the-squiggly-heredoc

Mark Jad
fonte
squish é trilhos, não rubi
Josh
11
@ Josh, sim, você está certo, atualizou a resposta, felicidades.
Mark Jad
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'

<< é o operador de concatenação para seqüências de caracteres

Dom Brezinski
fonte
2
+é o operador de concatenação regular, <<é o operador de acréscimo no local . O uso de efeitos colaterais em um literal funciona aqui (a primeira string é modificada duas vezes e retornada), mas IMHO é estranho e me faz dar uma olhada dupla, onde +seria perfeitamente claro. Mas talvez eu sou apenas novo para Ruby ...
Beni Cherniavsky-Paskin
Isso não funcionará se frozen_string_literalestiver ativado
Raido
6

Se você fazer mente espaços extras e novas linhas, você pode usar

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} * ' '

(use% W para seqüências de caracteres interpoladas)

UncleGene
fonte
Eu gosto muito deste, porque permite muito mais combinações de uso.
26415 schmijos
11
Isso esmagará vários espaços adjacentes em um. (Sua squishing de newline + seguinte recuo é uma vitória aqui, mas no meio da linha pode ser surpreendente.)
Beni Cherniavsky-Paskin
5

Para evitar fechar os parênteses para cada linha, você pode simplesmente usar aspas duplas com uma barra invertida para escapar da nova linha:

"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
fonte
Esta é uma das poucas respostas nesta página que realmente responde à pergunta!
Josh
4
conn.exec [
  "select attr1, attr2, attr3, ...",
  "from table1, table2, table3, ...",
  "where ..."
].join(' ')

Essa sugestão tem a vantagem sobre os documentos aqui e as cadeias longas que os identificadores automáticos podem indentar cada parte da cadeia de maneira apropriada. Mas tem um custo de eficiência.

Aidan Cully
fonte
@Aidan, você pode substituir as vírgulas por barras invertidas (a la C) e nenhuma junção (ou matriz) será necessária: o intérprete concatenará as strings no (eu acho) tempo de análise, tornando-o bastante rápido comparado à maioria das alternativas . Uma vantagem, porém, de unir uma matriz de strings é que alguns auto-indentadores funcionam melhor do que com, por exemplo, strings aqui-doc ou com \.
Wayne Conrad
11
Uma observação, a sintaxe heredoc << - permitirá o recuo apropriado.
A. Wilson
3

O caminho do Ruby (TM) desde o Ruby 2.3: use o HEREDOC ondulado <<~ para definir uma sequência de linhas múltiplas com novas linhas e recuo adequado:

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..."

Se o recuo adequado não for uma preocupação, as aspas simples e duplas podem abranger várias linhas no 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..."

Se aspas simples ou duplas são complicadas porque isso exigiria muito escape, a notação literal da string de porcentagem % é a solução mais flexível:

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..."

Se o objetivo é evitar as novas linhas (causadas por HEREDOC, aspas e literal de seqüência de caracteres percentual), uma continuação de linha pode ser usada colocando uma barra invertida \como o último caractere que não é um espaço em branco em uma linha. Isso continuará a linha e fará com que Ruby concatene as Strings consecutivas (cuidado com os espaços dentro da string citada):

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..."

Se você usar o Rails String.squish, a seqüência de espaços à esquerda e à direita será reduzida e reduzirá todos os espaços em branco consecutivos (novas linhas, guias e tudo) em um único espaço:

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..."

Mais detalhes:

Sintaxe Ruby HEREDOC

A notação de documento aqui para seqüências de caracteres funciona é uma maneira de designar longos blocos de texto embutidos no código. É iniciado <<seguido por uma String definida pelo usuário (o terminador End of String). Todas as linhas a seguir são concatenadas até o final do final da string ser encontrado no início de uma linha:

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

O terminador End of String pode ser escolhido livremente, mas é comum usar algo como "EOS" (End of String) ou algo que corresponda ao domínio da String, como "SQL".

O HEREDOC suporta interpolação por padrão ou quando o terminador EOS é duplo entre aspas:

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

A interpolação pode ser desativada se o terminador EOS estiver entre aspas simples:

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

Uma restrição importante do <<HEREDOCé que o terminador End of String precisa estar no início da linha:

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

Para contornar isso, a <<-sintaxe foi criada. Ele permite que o terminador EOS seja recuado para tornar o código mais agradável. As linhas entre o <<-terminador EOS ainda são usadas em toda a extensão, incluindo todo o recuo:

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

Desde o Ruby 2.3, agora temos o HEREDOC que <<~remove os espaços em branco principais:

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"

Linhas vazias e linhas que contêm apenas tabulações e espaço são ignoradas por << ~

puts <<~EOS.inspect 
  Hello

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

Se forem usadas guias e espaços, as guias serão consideradas iguais a 8 espaços. Se a linha menos recuada estiver no meio de uma guia, essa guia não será removida.

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

O HEREDOC pode fazer coisas malucas, como executar comandos usando backticks:

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

As definições de string HEREDOC podem ser "empilhadas", o que significa que o primeiro terminador EOS (EOSFOO abaixo) encerrará a primeira string e iniciará o segundo (EOSBAR abaixo):

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

Eu não acho que alguém possa usá-lo como tal, mas o <<EOSé realmente apenas uma string literal e pode ser colocada onde quer que uma string possa ser normalmente colocada:

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

Se você não possui o Ruby 2.3, mas o Rails >=3.0, pode usar o String.strip_heredocque faz o mesmo que<<~

# 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

Literais de seqüência de caracteres percentuais

Veja RubyDoc para saber como usar o sinal de porcentagem seguido por uma corda em um parênteses emparelhar tais como %(...), %[...], %{...}, etc. ou um par de qualquer caractere não-alfanumérico, como%+...+

Últimas palavras

Por fim, para obter a resposta para a pergunta original "Existe uma maneira de implicar concatenação?" respondeu: Ruby sempre implica concatenação se duas strings (simples e dupla entre aspas) forem encontradas consecutivas:

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

A ressalva é que isso não funciona entre quebras de linha, porque Ruby está interpretando um final de declaração e a consequente linha de apenas strings em uma linha não faz nada.

Christopher Oezbek
fonte
1

Resposta elegante hoje:

<<~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

Há uma diferença em <<-TEXTe <<~TEXT, o primeiro mantém o espaçamento dentro do bloco e o último não.

Existem outras opções também. Como concatenação, etc., mas essa faz mais sentido em geral.

Se eu estiver errado aqui, deixe-me saber como ...

Sandip Mane
fonte
O heredoc incluirá as novas linhas que não são equivalentes ao código original.
Josh
1

Como você, eu também estava procurando uma solução que não inclua novas linhas . (Embora eles possam estar seguros no SQL, eles não estão seguros no meu caso e eu tenho um grande bloco de texto para lidar)

É indiscutivelmente tão feio, mas você pode fazer uma barra invertida de escape com novas linhas em um heredoc para omiti-las da sequência resultante:

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

Observe que você não pode fazer isso sem interpolação (IE <<~'END_OF_INPUT'), portanto, tenha cuidado. #{expressions}serão avaliados aqui, enquanto eles não aparecerão no seu código original. R. A resposta de Wilson pode ser melhor por esse motivo.

Josh
fonte