How do I remove leading whitespace chars from Ruby HEREDOC?

后端 未结 11 684
误落风尘
误落风尘 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:40

    another easy to remember option is to use unindent gem

    require 'unindent'
    
    p <<-end.unindent
        hello
          world
      end
    # => "hello\n  world\n"  
    
    0 讨论(0)
  • 2020-11-28 22:43

    The <<- form of heredoc only ignores leading whitespace for the end delimiter.

    With Ruby 2.3 and later you can use a squiggly heredoc (<<~) to suppress the leading whitespace 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 literals documentation:

    The indentation of the least-indented line will be removed from each line of the content. Note that empty lines and lines consisting solely of literal tabs and spaces will be ignored for the purposes of determining indentation, but escaped tabs and spaces are considered non-indentation characters.

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

    If you're using Rails 3.0 or newer, try #strip_heredoc. This example from the docs prints the first three lines with no indentation, while retaining the last two lines' two-space indentation:

    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 looks for the least indented line in the whole string, and removes that amount of leading whitespace."

    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.

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

    I needed to use something with system whereby I could split long sed commands across 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
    

    Default behavior is to not strip newlines, just like all the other examples.

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

    Here's a far simpler version of the unindent script that I use:

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

    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 may be indented more than others, and want (like Rails) to unindent based on the least-indented line, you may instead wish to use:

    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 for \s+ instead of [ \t]+ you may end up stripping newlines from your heredoc instead of leading whitespace. Not desirable!

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

    <<- in Ruby will only ignore leading space for the ending delimiter, allowing it to be properly indented. It does not strip leading space on lines inside the string, despite what some documentation online might say.

    You can strip leading whitespace yourself by using 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
    

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

    <<-EOF.gsub /^ */, ''
        \tSELECT
        \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
        \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
        \tFROM #{table.call}
    EOF
    
    0 讨论(0)
提交回复
热议问题