Ich habe ein Problem mit einem Ruby heredoc, das ich zu erstellen versuche. Es gibt das führende Leerzeichen aus jeder Zeile zurück, obwohl ich den Operator - einbeziehe, der alle unterdrücken soll führende Leerzeichen. meine Methode sieht so aus:
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
und meine Ausgabe sieht so aus:
=> " \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"
das ist natürlich in diesem speziellen Fall richtig, mit Ausnahme aller Leerzeichen zwischen dem ersten "und\t. Weiß jemand, was ich hier falsch mache?
Das <<-
form of heredoc ignoriert nur führende Leerzeichen für den Endbegrenzer.
Mit Ruby 2.3 und höher können Sie einen schnörkellosen Heredoc (<<~
) um das führende Leerzeichen von Inhaltszeilen zu unterdrücken:
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"
Aus der Ruby Literale Dokumentation :
Der Einzug der am wenigsten eingerückten Zeile wird aus jeder Zeile des Inhalts entfernt. Beachten Sie, dass leere Zeilen und Zeilen, die nur aus wörtlichen Tabulatoren und Leerzeichen bestehen, zum Ermitteln des Einzugs ignoriert werden. Escape-Tabulatoren und Leerzeichen gelten jedoch als Nicht-Einzugszeichen.
Wenn Sie Rails 3.0 oder neuer verwenden, versuchen Sie #strip_heredoc
. In diesem Beispiel aus den Dokumenten druckt die ersten drei Zeilen ohne Einrückung, wobei die zwei Einrückungen der letzten beiden Zeilen beibehalten werden:
if options[:usage]
puts <<-USAGE.strip_heredoc
This command does such and such.
Supported options are:
-h This message
...
USAGE
end
In der Dokumentation wird außerdem Folgendes angegeben: "Technisch wird in der gesamten Zeichenfolge nach der am wenigsten eingerückten Zeile gesucht und das führende Leerzeichen entfernt."
Hier ist die Implementierung von 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
Die Tests finden Sie in test/core_ext/string_ext_test.rb .
Nicht viel zu tun, von dem ich weiß, dass ich Angst habe. Normalerweise mache ich:
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
Das funktioniert, ist aber ein bisschen hacken.
EDIT: Inspiriert von Rene Saarsoo, würde ich stattdessen Folgendes vorschlagen:
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
Diese Version sollte funktionieren, wenn die erste Zeile nicht auch die am weitesten links stehende ist.
Hier ist eine viel einfachere Version des nicht einbezogenen Skripts, das ich verwende:
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
Benutze es so:
foo = {
bar: <<-ENDBAR.unindent
My multiline
and indented
content here
Yay!
ENDBAR
}
#=> {:bar=>"My multiline\n and indented\n content here\nYay!"}
Wenn die erste Zeile stärker eingerückt sein kann als andere und (wie Rails) die Einrückung auf der Grundlage der am wenigsten eingerückten Zeile aufheben soll, möchten Sie möglicherweise stattdessen Folgendes verwenden:
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
Beachten Sie, dass beim Scannen nach \s+
Anstatt von [ \t]+
Möglicherweise werden Zeilenumbrüche von Ihrem Heredoc entfernt, anstatt Leerzeichen zu setzen. Nicht wünschenswert!
<<-
in Ruby ignoriert nur das führende Leerzeichen für das abschließende Trennzeichen, sodass es ordnungsgemäß eingerückt wird. Es werden keine führenden Leerzeichen in Zeilen innerhalb der Zeichenfolge entfernt, obwohl dies in einigen Online-Dokumentationen angegeben ist.
Sie können führende Leerzeichen selbst entfernen, indem Sie gsub
verwenden:
<<-EOF.gsub /^\s*/, ''
\tSELECT
\t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
\t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
\tFROM #{table.call}
EOF
Oder wenn Sie nur Leerzeichen entfernen möchten und die Registerkarten belassen:
<<-EOF.gsub /^ */, ''
\tSELECT
\t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
\t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
\tFROM #{table.call}
EOF
Einige andere Antworten finden die Einrückungsstufe von die am wenigsten eingerückte Zeile und löschen diese aus allen Zeilen. Unter Berücksichtigung der Art der Einrückung bei der Programmierung (die erste Zeile ist die am wenigsten eingerückte Zeile) sollten Sie dies jedoch tun Suchen Sie nach der Einrückungsstufe von die erste Zeile.
class String
def unindent; gsub(/^#{match(/^\s+/)}/, "") end
end
Wie das Originalplakat entdeckte auch ich das <<-HEREDOC
syntax und war ziemlich enttäuscht, dass es sich nicht so verhalten hat, wie ich es mir vorgestellt habe.
Aber anstatt meinen Code mit gsub-s zu verunreinigen, habe ich die String-Klasse erweitert:
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
eine andere, leicht zu merkende Option ist die Verwendung von Edelsteinen
require 'unindent'
p <<-end.unindent
hello
world
end
# => "hello\n world\n"
Hinweis: Wie @radiospiel hervorhob, String#squish
ist nur im Kontext ActiveSupport
verfügbar.
Ich glaube Ruby's String#squish
ist näher an dem, wonach Sie wirklich suchen:
So würde ich mit Ihrem Beispiel umgehen:
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
Ich musste etwas mit system
verwenden, wobei ich lange sed
Befehle über Zeilen aufteilen und dann Einrückungen UND Zeilenumbrüche entfernen konnte ...
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
Also habe ich mir Folgendes ausgedacht:
class ::String
def strip_heredoc(compress = false)
stripped = gsub(/^#{scan(/^\s*/).min_by(&:length)}/, "")
compress ? stripped.gsub(/\n/," ").chop : stripped
end
end
Standardmäßig werden keine Zeilenumbrüche entfernt, wie in allen anderen Beispielen.
Ich sammle Antworten und bekam diese:
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
Es generiert exzellentes SQL und verlässt nicht den AR-Bereich.