Ruby: ¿Puedo escribir cadenas de varias líneas sin concatenación?
¿Hay alguna manera de hacer que esto se vea un poco mejor?
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'
¿Hay alguna manera de implicar concatenación?
Hay partes de esta respuesta que me ayudaron a obtener lo que necesitaba (concatenación fácil de varias líneas SIN espacios en blanco adicionales), pero como ninguna de las respuestas reales lo tenía, las estoy compilando aquí:
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 beneficio adicional, aquí hay una versión que usa una sintaxis HEREDOC divertida (a través de este enlace ):
p <<END_SQL.gsub(/\s+/, " ").strip
SELECT * FROM users
ORDER BY users.id DESC
END_SQL
# >> "SELECT * FROM users ORDER BY users.id DESC"
Esto último sería principalmente para situaciones que requirieran más flexibilidad en el procesamiento. A mí personalmente no me gusta, coloca el procesamiento en un lugar extraño con respecto a la cadena (es decir, delante de ella, pero usando métodos de instancia que generalmente vienen después), pero está ahí. Tenga en cuenta que si está sangrando el último END_SQL
identificador (lo cual es común, ya que probablemente esté dentro de una función o módulo), deberá usar la sintaxis con guiones (es decir, p <<-END_SQL
en lugar de p <<END_SQL
). De lo contrario, el espacio en blanco de sangría hace que el identificador se interprete como una continuación de la cadena.
Esto no ahorra mucho tipeo, pero a mí me parece mejor que usar los signos +.
Además (lo digo en una edición, varios años después), si estás usando Ruby 2.3+, el operador <<~ también está disponible , lo que elimina la sangría adicional de la cadena final. En ese caso, debería poder eliminar la .gsub
invocación (aunque podría depender tanto de la sangría inicial como de sus necesidades finales).
EDITAR: Añadiendo uno más:
p %{
SELECT * FROM users
ORDER BY users.id DESC
}.gsub(/\s+/, " ").strip
# >> "SELECT * FROM users ORDER BY users.id DESC"
En Ruby 2.0 ahora puedes usar%
Por ejemplo:
SQL = %{
SELECT user, name
FROM users
WHERE users.id = #{var}
LIMIT #{var2}
}
Sí, si no le importa que se inserten nuevas líneas adicionales:
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'
Alternativamente, puedes usar un 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
Esta pregunta me hizo enloquecer para entender cómo funciona HEREDOC. Disculpe si la respuesta fue demasiado larga.
El ondulado HEREDOC <<~
es lo que busca cuando desea definir una cadena de varias líneas con nuevas líneas y sangría adecuada (disponible desde Ruby 2.3):
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..."
Si la sangría adecuada no es una preocupación, entonces las comillas simples y dobles pueden abarcar varias líneas en 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..."
Si las comillas simples o dobles son engorrosas porque requerirían muchos escapes, entonces la notación literal de cadena de porcentaje %
es la solución más flexible:
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..."
Si el objetivo es evitar las nuevas líneas (que causarán tanto el garabato HEREDOC, las comillas y el literal de cadena de porcentaje), entonces se puede usar una continuación de línea poniendo una barra invertida \
como último carácter que no sea un espacio en blanco en una línea. Esto continuará la línea y hará que Ruby concatene las cadenas una detrás de otra (tenga cuidado con los espacios dentro de la cadena 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..."
Si usa Rails, String.squish
eliminará la cadena de espacios iniciales y finales y colapsará todos los espacios en blanco consecutivos (nuevas líneas, tabulaciones y todo) en un solo espacio:
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..."
Más detalles:
Sintaxis de Ruby HEREDOC
La notación de documentos Here para cadenas es una forma de designar largos bloques de texto en línea en el código. Se inicia <<
seguido de una cadena definida por el usuario (el terminador de fin de cadena). Todas las líneas siguientes se concatenan hasta que se encuentra el terminador de fin de cadena al principio de una línea:
puts <<HEREDOC
Text Text Text Text
Bla Bla
HEREDOC
# -> "Text Text Text Text\nBlaBla"
El terminador End of String se puede elegir libremente, pero es común usar algo como "EOS" (End of String) o algo que coincida con el dominio de la String como "SQL".
HEREDOC admite la interpolación de forma predeterminada o cuando el terminador EOS está entre comillas dobles:
price = 10
print <<"EOS" # comments can be put here
1.) The price is #{price}.
EOS
# -> "1.) The price is 10."
La interpolación se puede desactivar si el terminador EOS está entre comillas simples:
print <<'EOS' # Disabled interpolation
3.) The price is #{price}.
EOS
# -> "3.) The price is #{price}."
Una restricción importante <<HEREDOC
es que el terminador de fin de cadena debe estar al principio de la línea:
puts <<EOS
def foo
print "foo"
end
EOS
EOS
#-> "....def foo\n......print "foo"\n....end\n..EOS"
Para solucionar esto, <<-
se creó la sintaxis. Permite sangrar el terminador EOS para que el código se vea mejor. Las líneas entre el <<-
terminador y EOS todavía se utilizan en toda su extensión, incluida toda la sangría:
def printExample
puts <<-EOS # Use <<- to indent End of String terminator
def foo
print "foo"
end
EOS
end
# -> "....def foo\n......print "foo"\n....end"
Desde Ruby 2.3, ahora tenemos el HEREDOC ondulado <<~
que elimina los espacios en blanco iniciales:
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"
Las líneas vacías y las líneas que solo contienen tabulaciones y espacios son ignoradas por <<~
puts <<~EOS.inspect
Hello
World!
EOS
#-> "Hello\n..World!"
Si se utilizan tabulaciones y espacios, las tabulaciones se consideran iguales a 8 espacios. Si la línea con menor sangría está en el medio de una pestaña, esta pestaña no se elimina.
puts <<~EOS.inspect
<tab>One Tab
<space><space>Two Spaces
EOS
# -> "\tOne Tab\nTwoSpaces"
HEREDOC puede hacer algunas cosas locas, como ejecutar comandos usando comillas invertidas:
puts <<`EOC`
echo #{price}
echo #{price * 2}
EOC
Las definiciones de cadenas HEREDOC se pueden "apilar", lo que significa que el primer terminador EOS (EOSFOO a continuación) finalizará la primera cadena y comenzará la segunda (EOSBAR a continuación):
print <<EOSFOO, <<EOSBAR # you can stack them
I said foo.
EOSFOO
I said bar.
EOSBAR
No creo que nadie lo use como tal, pero <<EOS
en realidad es solo un literal de cadena y se puede colocar donde normalmente se puede colocar una cadena:
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
Si no tienes Ruby 2.3, pero sí Rails >=
3.0, entonces puedes usar String.strip_heredoc
el cual hace lo mismo 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
Solución de problemas
Si ve errores cuando Ruby analiza su archivo, lo más probable es que tenga espacios iniciales o finales adicionales con un HEREDOC o espacios finales adicionales con un HEREDOC ondulado. Por ejemplo:
Lo que ves:
database_yml = <<~EOS
production:
database: #{fetch(:user)}
adapter: postgresql
pool: 5
timeout: 5000
EOS
Lo que Ruby te dice:
SyntaxError: .../sample.rb:xx: can't find string "EOS" anywhere before EOF
...sample.rb:xx: syntax error, unexpected end-of-input, expecting `end'
Qué tiene la culpa:
Localice los espacios adicionales después del EOS final.
Porcentaje de literales de cadena
Consulte RubyDoc para saber cómo utilizar el signo de porcentaje seguido de una cadena entre paréntesis como %(...)
, %[...]
, %{...}
etc. o un par de caracteres no alfanuméricos como%+...+
Ultimas palabras
Por último, para obtener la respuesta a la pregunta original "¿Existe alguna forma de implicar concatenación?" Respondido: Ruby siempre implica concatenación si se encuentran dos cadenas (comillas simples y dobles) una detrás de otra:
puts "select..." 'from table...' "where..."
# -> "select...from table...where..."
La advertencia es que esto no funciona a través de saltos de línea, porque Ruby está interpretando el final de una declaración y la línea consiguiente de solo cadenas en una línea no hace nada.