如何从Ruby 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
和我的输出如下所示:
=> " \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”之外,谁知道我在这里做错了什么?
heredoc的forms只是忽略了最后一个分隔符的主要空格。
在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"
从Ruby 文档文档 :
最小缩进行的缩进将从内容的每一行中删除。 请注意,为了确定缩进的目的,仅仅包含文本标签和空格的空行和行将被忽略,但是转义的制表符和空格被视为非缩进字符。
如果您使用的是Rails 3.0或更高版本,请尝试#strip_heredoc
。 这个来自docs的例子打印了前三行,没有缩进,同时保留了最后两行的双空格缩进:
if options[:usage] puts <<-USAGE.strip_heredoc This command does such and such. Supported options are: -h This message ... USAGE end
该文档还指出:“从技术上讲,它在整个string中查找最less的缩进行,并删除这些数量的前导空格。”
以下是来自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中findtesting。
没有太多的事情要做,我知道我害怕。 我通常做:
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
这工作,但是有点破解。
编辑:从下面的Rene Saarsoo获取灵感,我会build议像这样的东西:
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!"}
如果第一行可能比其他的缩进更多,并且希望(像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中只会忽略结尾分隔符的前导空格,允许它正确缩进。 它不会去掉string内部的空格,尽pipe网上有些文档可能会这样说。
您可以使用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
其他一些答案find最小缩进行的缩进级别,并从所有行中删除,但考虑到编程中缩进的性质(即第一行缩进最less),我认为你应该寻找缩进级别第一行 。
class String def unindent; gsub(/^#{match(/^\s+/)}/, "") end end
就像原来的海报一样,我也发现了<<-HEREDOC
语法”,并且非常失望,它没有像我认为应该performance的那样行事。
但是,而不是用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
另一个容易记住的select是使用unindentgem
require 'unindent' p <<-end.unindent hello world end # => "hello\n world\n"
注意: @radiospiel指出, String#squish
ActiveSupport
仅在ActiveSupport
上下文中可用。
我相信 ruby的 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
我收集答案,并得到这个:
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
东西,我可以跨行分割长期的命令,然后删除缩进和换行符…
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
默认行为是不去除换行符,就像所有其他的例子。