我正在尝试使用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之间的所有空格。有没有人知道我在这里做错了什么?
答案 0 :(得分:126)
<<-
形式的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"
来自Ruby literals documentation:
最小缩进行的缩进将从每个缩进行中删除 内容的一行。请注意,空行和仅包含的行 为了达到目的,将忽略文字制表符和空格 确定缩进,但考虑转义标签和空格 非缩进字符。
答案 1 :(得分:122)
如果您使用的是Rails 3.0或更高版本,请尝试使用#strip_heredoc
。 This example from the docs打印前三行没有缩进,同时保留最后两行&#39;双空缩进:
if options[:usage]
puts <<-USAGE.strip_heredoc
This command does such and such.
Supported options are:
-h This message
...
USAGE
end
文档还指出:&#34;从技术上讲,它在整个字符串中查找最少的缩进行,并删除前导空格的数量。&#34;
以下是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找到测试。
答案 2 :(得分:44)
没那么多,我知道我害怕。我通常会这样做:
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中汲取灵感,我建议改为:
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
此版本应该处理第一行不是最左边的行。
答案 3 :(得分:22)
这是我使用的一个更简单的unindent脚本版本:
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中删除换行符而不是前导空格。不可取!
答案 4 :(得分:7)
<<-
只会忽略结束分隔符的前导空格,允许它正确缩进。尽管网上有些文档可能会说,但它并没有删除字符串内部行的前导空格。
您可以使用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
答案 5 :(得分:6)
其他一些答案找到最小缩进行的缩进级别,并从所有行中删除它,但考虑到编程中缩进的性质(第一行是最少缩进的),我认为你应该寻找第一行的缩进级别。
class String
def unindent; gsub(/^#{match(/^\s+/)}/, "") end
end
答案 6 :(得分:2)
就像原版海报一样,我也发现了<<-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
答案 7 :(得分:1)
注意:正如@radiospiel所指出的那样,String#squish
仅在ActiveSupport
上下文中可用。
我相信 ruby&#39> 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
答案 8 :(得分:1)
另一个容易记住的选择是使用unindent gem
require 'unindent'
p <<-end.unindent
hello
world
end
# => "hello\n world\n"
答案 9 :(得分:0)
我收集答案并得到了这个:
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范围。
答案 10 :(得分:0)
我需要使用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
默认行为是不剥离换行符,就像所有其他示例一样。