Quebrando seqüências longas em várias linhas no Ruby sem descartar novas linhas

221

Recentemente, decidimos no meu trabalho um guia de estilo rubi. Um dos editais é que nenhuma linha deve ter mais de 80 caracteres. Como este é um projeto do Rails, geralmente temos sequências um pouco mais longas - ou seja, "O usuário X queria enviar uma mensagem sobre o Coisa Y " que nem sempre se encaixa dentro do limite de 80 caracteres.

Entendo que há três maneiras de ter uma longa cadeia de caracteres abrangendo várias linhas:

  • HEREDOC
  • % Q {}
  • Concatenação de string real.

No entanto, todos esses casos acabam tendo mais ciclos de computação, o que parece bobo. Concatenação de strings obviamente, mas para HEREDOCe %Qeu tenho que retirar as novas linhas, através de algo como .gsub(/\n$/, '').

Existe uma maneira pura de sintaxe para fazer isso, que é equivalente a apenas ter toda a cadeia em uma linha? O objetivo é, obviamente, não gastar ciclos extras apenas porque quero que meu código seja um pouco mais legível. (Sim, eu sei que você precisa fazer muito isso ... mas, para o comprimento da corda, isso parece bobagem.)

Atualização: as barras invertidas não são exatamente o que eu quero, porque você perde o recuo, o que realmente afeta o estilo / legibilidade.

Exemplo:

if foo
  string = "this is a \  
string that spans lines"  
end

Acho o exposto um pouco difícil de ler.

EDIT : eu adicionei uma resposta abaixo; três anos depois, agora temos o heredoc irritadiço.

chug2k
fonte
Pode valer a pena deduzir essas strings.
Cheezmeister
👎🏼 Não está claro se você deseja manter os \ncaracteres de nova linha ou não. A resposta principal não os mantém - mas sua resposta sim. A pergunta diz "sem remover novas linhas" - mas a descrição diz "[...] eu tenho que retirar as novas linhas".
21717 Bryan Dimas

Respostas:

432

Talvez seja isso que você está procurando?

string = "line #1"\
         "line #2"\
         "line #3"

p string # => "line #1line #2line #3"
Jessehz
fonte
4
Hã. Sim, isso é muito perfeito. isso é realmente fazer uma concatenação sob o capô? (ocultando um +?)
chug2k
13
Tenho quase certeza de que não é; Eu tentei indefinir e redefinir a String # + e parece que nada está sendo enviado para lá.
Jessehz
Além disso, colocar as strings próximas umas das outras na mesma linha também funciona, mas isso não é muito útil.
Jessehz
3
Eu também gosto da solução de Emily. Mas, por alguma razão, estou mais à vontade com essa, porque os espaços em branco à esquerda nas linhas a seguir podem ser recuados sem adivinhar.
Amin Ariana
5
Para concatenação, confira a resposta de Amadan a esta pergunta relacionada . Ele usa o bytecode para provar que não está concatenando cadeias, mas tratando-as como uma única.
21717 Tom Hundt
55

Você pode usar \para indicar que qualquer linha do Ruby continua na próxima linha. Isso funciona com strings também:

string = "this is a \
string that spans lines"

puts string.inspect

irá produzir "this is a string that spans lines"

Emily
fonte
11
Hã. isso é bem simples. bem, provavelmente é pedir demais, mas não posso recuar a próxima linha. seria ideal se eu pudesse combinar com o sinal de igual.
Chug2k
5
Nota: você não pode ter nenhum espaço em branco depois dessa barra invertida. Deve ser o último caractere.
Julian Mann
3
Veja a resposta de @Jessehz. A maneira de ter um código bem formatado com recuo adequado é colocar cada linha entre aspas.
orrd 5/06
36

Três anos depois, agora existe uma solução no Ruby 2.3: O heredoc irritadiço.

class Subscription
  def warning_message
    <<~HEREDOC
      Subscription expiring soon!
      Your free trial will expire in #{days_until_expiration} days.
      Please update your billing information.
    HEREDOC
  end
end

Link para postagem no blog: https://infinum.co/the-capsized-eight/articles/multiline-strings-ruby-2-3-0-the-squiggly-heredoc

O recuo da linha menos recuada será removido de cada linha do conteúdo.

chug2k
fonte
29
Para quem mais se pergunta: <<~HEREDOCmantém os caracteres da nova linha, isso apenas livra o recuo. Por exemploline 1\nline 2\nline 3\n
Bryan Dimas
1
A remoção de novas linhas ainda exigirá computação extra, o que o tipo de OP implícito não era desejável.
henrebotha
1
@henrebotha Sim, mas o OP disse de forma mais explícita que eles querem o "equivalente a apenas ter toda a string em uma linha", o que não exigirá novas linhas.
Nick
Então, você declarou explicitamente na sua pergunta que não queria usar um heredoc e precisava remover as novas linhas. Sua resposta aqui usa um heredoc e requer o "pós-processamento" que você deseja evitar.
lamont
17

Eu tive esse problema ao tentar escrever um URL muito longo, o seguinte funciona.

image_url = %w(
    http://minio.127.0.0.1.xip.io:9000/
    bucket29/docs/b7cfab0e-0119-452c-b262-1b78e3fccf38/
    28ed3774-b234-4de2-9a11-7d657707f79c?
    X-Amz-Algorithm=AWS4-HMAC-SHA256&
    X-Amz-Credential=ABABABABABABABABA
    %2Fus-east-1%2Fs3%2Faws4_request&
    X-Amz-Date=20170702T000940Z&
    X-Amz-Expires=3600&X-Amz-SignedHeaders=host&
    X-Amz-Signature=ABABABABABABABABABABAB
    ABABABABABABABABABABABABABABABABABABA
).join

Observe que não deve haver novas linhas, espaços em branco quando a sequência de URLs é formada. Se você deseja novas linhas, use HEREDOC.

Aqui você tem recuo para facilitar a leitura, sem modificar aspas e barras invertidas em todas as linhas. O custo de união das strings deve ser insignificante.

Zack Xu
fonte
10

Modifiquei a resposta de Zack, pois queria espaços e interpolação, mas não novas linhas, e usei:

%W[
  It's a nice day "#{name}"
  for a walk!
].join(' ')

onde name = 'fred'isso produzIt's a nice day "fred" for a walk!

iheggie
fonte