Mam problem z heredocem Ruby, który próbuję zrobić. Zwraca początkowe białe znaki z każdego wiersza, mimo że włączam operator -, który ma blokować wszystkie wiodące białe znaki. moja metoda wygląda następująco:
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
a moje wyjście wygląda następująco:
=> " \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"
to oczywiście jest poprawne w tym konkretnym przypadku, z wyjątkiem wszystkich spacji między pierwszym „a \ t. Czy ktoś wie, co tu robię źle?
źródło
Jeśli używasz Rails 3.0 lub nowszych, spróbuj
#strip_heredoc
. Ten przykład z dokumentacji wypisuje pierwsze trzy wiersze bez wcięć, zachowując wcięcie z dwoma spacjami w ostatnich dwóch wierszach:if options[:usage] puts <<-USAGE.strip_heredoc This command does such and such. Supported options are: -h This message ... USAGE end
Dokumentacja zauważa również: „Technicznie, szuka najmniej wciętej linii w całym ciągu i usuwa tę ilość wiodących białych spacji”.
Oto implementacja z 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
Testy można znaleźć w test / core_ext / string_ext_test.rb .
źródło
require "active_support/core_ext/string"
pierwszytry
nie jest zdefiniowany dla String. W rzeczywistości wydaje się, że jest to konstrukcja specyficzna dla szynNie mam wiele do zrobienia, bo boję się. Zwyklę robię:
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
To działa, ale to trochę hack.
EDYCJA: Czerpiąc inspirację z Rene Saarsoo poniżej, sugerowałbym coś takiego:
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
Ta wersja powinna obsługiwać, gdy pierwsza linia nie jest również najbardziej wysunięta na lewo.
źródło
EOF
samego siebie, a nie tylkoString
?\s
obejmuje to nowe wiersze.Oto znacznie prostsza wersja skryptu bez wcięcia, którego używam:
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
Użyj go w ten sposób:
foo = { bar: <<-ENDBAR.unindent My multiline and indented content here Yay! ENDBAR } #=> {:bar=>"My multiline\n and indented\n content here\nYay!"}
Jeśli pierwsza linia może być wcięta bardziej niż inne i chcesz (tak jak Rails), aby cofnąć wcięcie na podstawie linii z najmniejszym wcięciem, możesz zamiast tego użyć:
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
Zauważ, że jeśli zaczniesz szukać
\s+
zamiast[ \t]+
ciebie, może skończyć się usunięciem nowych linii z twojego heredoc zamiast początkowych białych spacji. Niepożądane!źródło
<<-
w Rubim zignoruje tylko początkową spację dla separatora końcowego, umożliwiając odpowiednie wcięcie. Nie usuwa wiodących spacji w liniach wewnątrz łańcucha, pomimo tego, co może powiedzieć część dokumentacji online.Możesz samodzielnie usunąć wiodące białe znaki, używając
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
Lub jeśli chcesz po prostu usunąć spacje, pozostawiając karty:
<<-EOF.gsub /^ */, '' \tSELECT \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT \tFROM #{table.call} EOF
źródło
Niektóre inne odpowiedzi określają poziom wcięcia najmniej wciętego wiersza i usuwają go ze wszystkich wierszy, ale biorąc pod uwagę naturę wcięcia w programowaniu (pierwsza linia jest najmniej wcięta), myślę, że powinieneś poszukać poziomu wcięcia w pierwsza linia .
class String def unindent; gsub(/^#{match(/^\s+/)}/, "") end end
źródło
Podobnie jak w przypadku oryginalnego plakatu, ja również odkryłem
<<-HEREDOC
składnię i byłem cholernie rozczarowany, że nie zachowywał się tak, jak myślałem, że powinien.Ale zamiast zaśmiecać mój kod gsub-s, rozszerzyłem klasę 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
źródło
Uwaga: jak wskazał @radiospiel,
String#squish
jest dostępny tylko wActiveSupport
kontekście.wierzę
rubinówString#squish
jest bliżej tego, czego naprawdę szukasz:Oto jak poradziłbym sobie z twoim przykładem:
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
źródło
Inną łatwą do zapamiętania opcją jest użycie niewciętego klejnotu
require 'unindent' p <<-end.unindent hello world end # => "hello\n world\n"
źródło
Musiałem użyć czegoś, dzięki
system
czemu mógłbym podzielić długiesed
polecenia na linie, a następnie usunąć wcięcia ORAZ nowe linie ...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
Więc wymyśliłem to:
class ::String def strip_heredoc(compress = false) stripped = gsub(/^#{scan(/^\s*/).min_by(&:length)}/, "") compress ? stripped.gsub(/\n/," ").chop : stripped end end
Domyślnym zachowaniem jest brak usuwania znaków nowej linii, tak jak we wszystkich innych przykładach.
źródło
Zbieram odpowiedzi i otrzymuję to:
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
Generuje doskonały SQL i nie wychodzi poza zakres AR.
źródło