Как удалить ведущие пробелы, символы с Рубином помощи heredoc?
у меня проблема с Ruby heredoc, который я пытаюсь сделать. Он возвращает ведущие пробелы из каждой строки, хотя я включаю оператор -, который должен подавлять все ведущие символы пробела. мой метод выглядит так:
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
и мой вывод выглядит следующим образом:
=> " tSELECTn t CAST('SRC_ACCT_NUM' AS VARCHAR(30)) as
COLUMN_NAMEn t,COUNT(DISTINCT SRC_ACCT_NUM) AS DISTINCT_COUNTn
tFROM UD461.MGMT_REPORT_HNBn"
это, конечно, правильно в этом конкретном случае, за исключением всех пробелов между первым " и t. кто-нибудь знает, что я делаю неправильно здесь?
11 ответов:
The
<<-
форма heredoc игнорирует только начальные пробелы для конечного разделителя.С Ruby 2.3 и позже вы можете использовать волнистый heredoc (
<<~
) для подавления ведущих пробелов строк содержимого: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"
от рубинового литералы документации:
отступ строки с наименьшим отступом будет удален из каждого строка содержания. Обратите внимание, что пустые строки и строки, состоящие исключительно из буквального табы и пробелы будут игнорироваться для целей определение отступа, но экранированные вкладки и пробелы считаются символы без отступов.
Если вы используете Rails 3.0 или новее, попробуйте
#strip_heredoc
. этот пример из документации печатает первые три строки без отступа, сохраняя при этом два отступа последних двух строк:if options[:usage] puts <<-USAGE.strip_heredoc This command does such and such. Supported options are: -h This message ... USAGE end
в документации также отмечается: "технически он ищет наименее отступленную строку во всей строке и удаляет это количество ведущих пробелов."
вот реализация от active_support/core_ext/string / strip.РБ:
class String def strip_heredoc indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0 gsub(/^[ \t]{#{indent}}/, '') end end
и вы можете найти тесты test/core_ext / string_ext_test.РБ.
не так много сделать, что я знаю, я боюсь. Я обычно делаю:
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
это работает, но это немного взломать.
изменить: Вдохновляясь Рене Саарсу ниже, я бы предложил что-то вроде этого:
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
эта версия должна обрабатывать, когда первая строка не является самой дальней слева тоже.
здесь гораздо проще версия отступы скрипт, который я использую:
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
используйте его так:
foo = { bar: <<-ENDBAR.unindent My multiline and indented content here Yay! ENDBAR } #=> {:bar=>"My multiline\n and indented\n content here\nYay!"}
если первая строка может быть отступом больше, чем другие, и хотите (например, рельсы), чтобы unindent на основе наименее отступом линии, вы можете вместо этого использовать:
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
обратите внимание, что если вы сканируете для
\s+
вместо[ \t]+
вы можете в конечном итоге удалить новые строки из вашего heredoc вместо ведущих пробелов. Не желательно!
<<-
в Ruby будет игнорировать только начальное пространство для конечного разделителя,позволяя ему быть правильно отступ. Она не оставляет места на строки внутри строки, несмотря на то, что некоторые онлайн может сказать.вы можете удалить ведущие пробелы самостоятельно с помощью
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
или если вы просто хотите убрать пробелы, оставив вкладки:
<<-EOF.gsub /^ */, '' \tSELECT \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT \tFROM #{table.call} EOF
некоторые другие ответы находят уровень отступа строка с наименьшим отступом, и удалите это из всех строк, но учитывая характер отступа в программировании (что первая строка является наименее отступом), я думаю, что вы должны искать уровень отступа первая строка.
class String def unindent; gsub(/^#{match(/^\s+/)}/, "") end end
Как и оригинальный плакат, я тоже обнаружил
<<-HEREDOC
синтаксис и был чертовски разочарован тем, что он не вел себя так, как я думал, что он должен вести себя.но вместо того, чтобы засорять мой код gsub-s я расширил класс 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
Примечание: как указал @radiospiel,
String#squish
доступно только вActiveSupport
контексте.
Я считаю
РубинString#squish
ближе к тому, что ты ищешь:вот как я бы справился с вашим примером:
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
еще один простой для запоминания вариант-использовать unindent gem
require 'unindent' p <<-end.unindent hello world end # => "hello\n world\n"
Я собираю ответы и получил это:
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
Он генерирует отличный SQL и не выходит из областей AR.
мне нужно использовать что-то с
system
в результате чего я мог бы разделить долгоsed
команды по строкам, а затем удалить отступ и новые строки...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
вот я и придумал это:
class ::String def strip_heredoc(compress = false) stripped = gsub(/^#{scan(/^\s*/).min_by(&:length)}/, "") compress ? stripped.gsub(/\n/," ").chop : stripped end end
поведение по умолчанию заключается в том, чтобы не удалять новые строки, как и все другие примеры.