[ruby] 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 의 형식은 끝 구분 기호의 선행 공백 만 무시합니다.

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. 문서의이 예제는 들여 쓰기없이 처음 세 줄을 인쇄하고 마지막 두 줄의 두 칸 들여 쓰기를 유지합니다.

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

작동하지만 약간의 해킹입니다.

편집 : 아래의 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

이 버전은 첫 번째 줄이 가장 왼쪽에있는 줄이 아닐 때 처리해야합니다.


답변

내가 사용하는 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에서 줄 바꿈이 제거 될 수 있습니다. 바람직하지 않습니다!


답변

<<-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