У меня проблема с рубиновым 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
и мой результат выглядит так:
=> " \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"
это, конечно, правильно в данном конкретном случае, за исключением всех пробелов между первым "и \ t. Кто-нибудь знает, что я здесь делаю неправильно?"
источник
Если вы используете 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.rb :
class String def strip_heredoc indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0 gsub(/^[ \t]{#{indent}}/, '') end end
И вы можете найти тесты в test / core_ext / string_ext_test.rb .
источник
require "active_support/core_ext/string"
первыйtry
не определено для String. На самом деле, похоже, что это конструкция, специфичная для рельсов,Боюсь, что делать нечего. Я обычно делаю:
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
Эта версия должна обрабатывать, когда первая строка тоже не самая крайняя слева.
источник
EOF
, а не простоString
?\s
включает новые строки.Вот гораздо более простая версия скрипта undent, который я использую:
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!"}
Если первая строка может иметь больший отступ, чем другие, и вы хотите (как в Rails) удалить отступ на основе строки с наименьшим отступом, вы можете вместо этого использовать:
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
источник
еще один вариант, который легко запомнить, - это использовать неадекватный драгоценный камень
require 'unindent' p <<-end.unindent hello world end # => "hello\n world\n"
источник
Мне нужно было что-то использовать, с помощью
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
По умолчанию символы новой строки не удаляются, как и во всех других примерах.
источник
Собираю ответы и получаю вот что:
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.
источник