How to remove leading white characters from Ruby HEREDOC?

I have a problem with the Ruby heredoc I'm trying to do. It returns leading spaces from each line, although I include the - operator, which should suppress all leading spaces. my method is as follows:

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 

and my conclusion is as follows:

  => " \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" 

this, of course, is correct in this particular instance, with the exception of all spaces between the first "and \ t". Does anyone know what I'm doing wrong here?

+66
ruby heredoc whitespace
Sep 22 '10 at 19:23
source share
11 answers

The <<- heredoc form only ignores leading spaces for the trailing delimiter.

With Ruby 2.3 and later, you can use squiggly heredoc ( <<~ ) to suppress leading spaces of content lines:

 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" 

From the Ruby documentation:

The indentation of the line with the least indentation will be removed from each line of content. Note that blank lines and lines consisting solely of literal tabs and spaces will be ignored for indentation purposes, but avoided tabs and spaces are considered un-displayed characters.

+91
Jan 19 '16 at 22:33
source share

If you are using Rails 3.0 or later, try #strip_heredoc . This document example prints the first three lines without indentation, preserving the two indentation of the last two two lines:

 if options[:usage]  puts <<-USAGE.strip_heredoc    This command does such and such.    Supported options are:      -h        This message      ...  USAGE end 

The documentation also notes: "Technically, it searches for the smallest line indented in the entire line and removes this number of leading spaces."

Here's the implementation from 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 

And you can find the tests in test / core_ext / string_ext_test.rb .

+115
Mar 11 '12 at 10:45
source share

Not so much that I know, I'm afraid. Usually I do:

 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 

It works, but a little hacked.

EDIT: Taking inspiration from Rene Saarsoo below, I would suggest something like this:

 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 

This version should be processed if the first line is not one of the farthest to the left.

+44
Sep 22 '10 at 19:30
source share

Here's a much simpler version of the unindent script I'm using:

 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 

Use it like this:

 foo = { bar: <<-ENDBAR.unindent My multiline and indented content here Yay! ENDBAR } #=> {:bar=>"My multiline\n and indented\n content here\nYay!"} 



If the first line can be indented more than others, and you want (for example, Rails) to make unindent based on the line with the least indentation, you can use instead:

 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 

Note that if you scan \s+ instead of [ \t]+ , you can remove newline descriptors from your heredoc instead of leading spaces. Not advisable!

+21
Apr 12 2018-11-11T00:
source share

<<- In Ruby, it will ignore the leading space for the trailing delimiter, which allows it to be indented correctly. It does not divide the leading space into lines within the line, despite the fact that some documentation on the Internet can say.

With gsub you can remove leading spaces:

 <<-EOF.gsub /^\s*/, '' \tSELECT \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT \tFROM #{table.call} EOF 

Or, if you just want to break the spaces by leaving tabs:

 <<-EOF.gsub /^ */, '' \tSELECT \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT \tFROM #{table.call} EOF 
+7
Sep 22 '10 at 19:35
source share

In some other answers, find the indent level of the smallest indent line and remove it from all lines, but given the nature of the indent in programming (the first line is the least indent), I think you should look for the indent level in the first line .

 class String def unindent; gsub(/^#{match(/^\s+/)}/, "") end end 
+6
Jul 15 '13 at 2:36
source share

Like the original poster, I also discovered the <<-HEREDOC and was rather disappointed that it did not behave the way I thought it should behave.

But instead of clogging my gsub-s code, I extended the String class:

 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 
+2
Dec 16 '10 at 21:41
source share

another easy-to-remember option is to use unindent gem

 require 'unindent' p <<-end.unindent hello world end # => "hello\n world\n" 
+1
Jul 29 '14 at 3:36 on
source share

Note. . As @radiospiel noted, String#squish is only available in the ActiveSupport context.




I believe ruby's String#squish closer to what you are really looking for:

Here is how I could handle your example:

 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 
0
Apr 19 '13 at 21:58
source share

I collect the answers and get the following:

 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 

It generates excellent SQL and does not leave areas of AR.

0
Mar 02 '17 at 6:38
source share

I needed to use something with system , so I could separate long sed commands into lines, and then remove indentation and newlines ...

 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 

So, I came up with this:

 class ::String def strip_heredoc(compress = false) stripped = gsub(/^#{scan(/^\s*/).min_by(&:length)}/, "") compress ? stripped.gsub(/\n/," ").chop : stripped end end 

The default behavior is not to break newlines, like all other examples.

0
May 18 '17 at 19:25
source share



All Articles