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は、終了区切り文字の先頭の空白のみを無視します。
Ruby 2.3以降では、波線のあるヒアドキュメント(<<~
)コンテンツ行の先頭の空白を抑制するには:
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 から:
最もインデントされていない行のインデントは、コンテンツの各行から削除されます。空行とリテラルのタブとスペースのみで構成される行は、インデントを決定するために無視されますが、エスケープされたタブとスペースはインデントされていない文字と見なされます。
Rails 3.0以降を使用している場合は、#strip_heredoc
。 このドキュメントの例 は、最後の2行の2スペースのインデントを保持したまま、インデントなしで最初の3行を出力します。
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 にあります。
私が恐れていることを知っていることはあまりありません。私は通常:
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!"}
最初の行が他の行よりもインデントされている可能性があり、(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]+
をスキャンすると、先頭の空白の代わりにヒアドキュメントから改行を削除することに注意してください。望ましくない!
<<-
in 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
覚えやすい別のオプションは、インデントのないgemを使用することです
require 'unindent'
p <<-end.unindent
hello
world
end
# => "hello\n world\n"
注:@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
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スコープから外に出ません。