I\'m having a problem with a Ruby heredoc i\'m trying to make. It\'s returning the leading whitespace from each line even though i\'m including the - operator, which is supp
Some other answers find the indentation level of the least indented line, and delete that from all lines, but considering the nature of indentation in programming (that the first line is the least indented), I think you should look for the indentation level of the first line.
class String
def unindent; gsub(/^#{match(/^\s+/)}/, "") end
end
Like the original poster, I too discovered the <<-HEREDOC
syntax and was pretty damn disappointed that it didn't behave as I thought it should behave.
But instead of littering my code with gsub-s 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
I collect answers and got this:
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 do not go out of AR scopes.
Not much to do that I know of I'm afraid. I usually 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
That works but is a bit of a hack.
EDIT: Taking inspiration from Rene Saarsoo below, I'd suggest something like this instead:
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 handle when the first line is not the one farthest to the left too.
Note: As @radiospiel pointed out, String#squish
is only available in the ActiveSupport
context.
I believe ruby's String#squish is closer to what you're really looking for:
Here is how I would 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