How do I remove leading whitespace chars from Ruby HEREDOC?

后端 未结 11 685
误落风尘
误落风尘 2020-11-28 22:09

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

相关标签:
11条回答
  • 2020-11-28 22:49

    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
    
    0 讨论(0)
  • 2020-11-28 22:50

    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
    
    0 讨论(0)
  • 2020-11-28 22:50

    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.

    0 讨论(0)
  • 2020-11-28 22:52

    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.

    0 讨论(0)
  • 2020-11-28 22:56

    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
    
    0 讨论(0)
提交回复
热议问题