¿Cómo elimino los caracteres principales de espacio en blanco de Ruby HEREDOC?
whitespace (11)
Al igual que el póster original, también descubrí la sintaxis <<-HEREDOC
y me decepcionó bastante que no se comportara como pensé que debería comportarse.
Pero en lugar de ensuciar mi código con gsub-s extendí la clase String:
class String
# Removes beginning-whitespace from each line of a string.
# But only as many whitespace as the first line has.
#
# Ment to be used with heredoc strings like so:
#
# text = <<-EOS.unindent
# This line has no indentation
# This line has 2 spaces of indentation
# This line is also not indented
# EOS
#
def unindent
lines = []
each_line {|ln| lines << ln }
first_line_ws = lines[0].match(/^/s+/)[0]
re = Regexp.new(''^/s{0,'' + first_line_ws.length.to_s + ''}'')
lines.collect {|line| line.sub(re, "") }.join
end
end
Estoy teniendo un problema con un Ruby heredoc que estoy tratando de hacer. Devuelve el espacio en blanco inicial de cada línea a pesar de que incluyo el operador -, que se supone que suprime todos los caracteres principales en blanco. mi método se ve así:
def distinct_count
<<-EOF
/tSELECT
/t CAST(''#{name}'' AS VARCHAR(30)) as COLUMN_NAME
/t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
/tFROM #{table.call}
EOF
end
y mi salida se ve así:
=> " /tSELECT/n /t CAST(''SRC_ACCT_NUM'' AS VARCHAR(30)) as
COLUMN_NAME/n /t,COUNT(DISTINCT SRC_ACCT_NUM) AS DISTINCT_COUNT/n
/tFROM UD461.MGMT_REPORT_HNB/n"
esto, por supuesto, es correcto en esta instancia específica, a excepción de todos los espacios entre el primero "y" ¿alguien sabe lo que estoy haciendo mal aquí?
Algunas otras respuestas encuentran el nivel de sangría de la línea con la sangría mínima y la eliminan de todas las líneas, pero considerando la naturaleza de la sangría en la programación (que la primera línea es la menos sangrada), creo que debería buscar el nivel de sangría del primera línea
class String
def unindent; gsub(/^#{match(/^/s+/)}/, "") end
end
Aquí hay una versión mucho más simple de la secuencia de comandos sin incluir que uso:
class String
# Strip leading whitespace from each line that is the same as the
# amount of whitespace on the first line of the string.
# Leaves _additional_ indentation on later lines intact.
def unindent
gsub /^#{self[//A[ /t]*/]}/, ''''
end
end
Úselo así:
foo = {
bar: <<-ENDBAR.unindent
My multiline
and indented
content here
Yay!
ENDBAR
}
#=> {:bar=>"My multiline/n and indented/n content here/nYay!"}
Si la primera línea se puede sangrar más que otras, y desea (como Rieles) desencriptar en función de la línea con sangrado mínimo, en su lugar puede desear usar:
class String
# Strip leading whitespace from each line that is the same as the
# amount of whitespace on the least-indented line of the string.
def strip_indent
if mindent=scan(/^[ /t]+/).min_by(&:length)
gsub /^#{mindent}/, ''''
end
end
end
Tenga en cuenta que si explora por /s+
lugar de [ /t]+
, puede acabar eliminando nuevas líneas de su heredoc en lugar de marcar espacios en blanco. ¡No deseable!
La forma <<-
de heredoc solo ignora los espacios en blanco iniciales para el delimitador final.
Con Ruby 2.3 y posterior puede usar un squiggly heredoc ( <<~
) para suprimir el espacio en blanco inicial de las líneas de contenido:
def test
<<~END
First content line.
Two spaces here.
No space here.
END
end
test
# => "First content line./n Two spaces here./nNo space here./n"
De la documentación de Ruby literales :
La sangría de la línea menos sangrada se eliminará de cada línea del contenido. Tenga en cuenta que las líneas y líneas vacías que consisten únicamente en pestañas y espacios literales se ignorarán a los efectos de determinar la sangría, pero las pestañas y espacios vacíos se consideran caracteres sin sangría.
Necesitaba usar algo con el system
por el cual pudiera dividir los comandos long sed
entre las líneas y luego eliminar la sangría Y las líneas nuevas ...
def update_makefile(build_path, version, sha1)
system <<-CMD.strip_heredoc(true)
//sed -i".bak"
-e "s/GIT_VERSION[/ ]*:=.*/GIT_VERSION := 20171-2342/g"
-e "s/GIT_VERSION_SHA1[/ ]:=.*/GIT_VERSION_SHA1 := 2342/g"
"/tmp/Makefile"
CMD
end
Así que se me ocurrió esto:
class ::String
def strip_heredoc(compress = false)
stripped = gsub(/^#{scan(/^/s*/).min_by(&:length)}/, "")
compress ? stripped.gsub(//n/," ").chop : stripped
end
end
El comportamiento predeterminado es no quitar nuevas líneas, al igual que todos los demás ejemplos.
No hay mucho que hacer que sé que tengo miedo. Usualmente hago:
def distinct_count
<<-EOF.gsub /^/s+/, ""
/tSELECT
/t CAST(''#{name}'' AS VARCHAR(30)) as COLUMN_NAME
/t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
/tFROM #{table.call}
EOF
end
Eso funciona, pero es un poco complicado.
EDITAR: Tomando inspiración de Rene Saarsoo a continuación, sugeriría algo como esto en su lugar:
class String
def unindent
gsub(/^#{scan(/^/s*/).min_by{|l|l.length}}/, "")
end
end
def distinct_count
<<-EOF.unindent
/tSELECT
/t CAST(''#{name}'' AS VARCHAR(30)) as COLUMN_NAME
/t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
/tFROM #{table.call}
EOF
end
Esta versión debe manejar cuando la primera línea no es la que está más a la izquierda también.
Otra opción fácil de recordar es usar la gema de unindent
require ''unindent''
p <<-end.unindent
hello
world
end
# => "hello/n world/n"
Recopilo respuestas y obtuve esto:
class Match < ActiveRecord::Base
has_one :invitation
scope :upcoming, -> do
joins(:invitation)
.where(<<-SQL_QUERY.strip_heredoc, Date.current, Date.current).order(''invitations.date ASC'')
CASE WHEN invitations.autogenerated_for_round IS NULL THEN invitations.date >= ?
ELSE (invitations.round_end_time >= ? AND match_plays.winner_id IS NULL) END
SQL_QUERY
end
end
Genera SQL excelente y no sale de los ámbitos AR.
Si está utilizando Rails 3.0 o posterior, intente con #strip_heredoc
. Este ejemplo de los documentos imprime las primeras tres líneas sin sangría, conservando la sangría de dos espacios de las dos últimas líneas:
if options[:usage]
puts <<-USAGE.strip_heredoc
This command does such and such.
Supported options are:
-h This message
...
USAGE
end
La documentación también señala: "Técnicamente, busca la línea menos sangrada en toda la cadena y elimina esa cantidad de espacio en blanco inicial".
Aquí está la implementación de active_support/core_ext/string/strip.rb :
class String
def strip_heredoc
indent = scan(/^[ /t]*(?=/S)/).min.try(:size) || 0
gsub(/^[ /t]{#{indent}}/, '''')
end
end
Y puede encontrar las pruebas en test/core_ext/string_ext_test.rb .
<<-
en Ruby solo ignorará el espacio inicial para el delimitador final, lo que permite que tenga una sangría adecuada. No quita el espacio que lleva en las líneas dentro de la cadena, a pesar de lo que pueda decir alguna documentación en línea.
Puede quitar el espacio en blanco líder utilizando gsub
:
<<-EOF.gsub /^/s*/, ''''
/tSELECT
/t CAST(''#{name}'' AS VARCHAR(30)) as COLUMN_NAME
/t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
/tFROM #{table.call}
EOF
O si solo quieres quitar espacios, dejando las pestañas:
<<-EOF.gsub /^ */, ''''
/tSELECT
/t CAST(''#{name}'' AS VARCHAR(30)) as COLUMN_NAME
/t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
/tFROM #{table.call}
EOF
Nota: Como señaló @radiospiel, String#squish
solo está disponible en el contexto de ActiveSupport
.
Creo Ruby String#squish
está más cerca de lo que realmente estás buscando:
Así es como manejaría su ejemplo:
def distinct_count
<<-SQL.squish
SELECT
CAST(''#{name}'' AS VARCHAR(30)) as COLUMN_NAME,
COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
FROM #{table.call}
SQL
end