Ruby: Can I write multi-line string with no concatenation?

后端 未结 16 782
礼貌的吻别
礼貌的吻别 2020-12-04 04:42

Is there a way to make this look a little better?

conn.exec \'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 \' +
          \'from table1, table2, ta         


        
相关标签:
16条回答
  • 2020-12-04 05:19

    The Ruby-way (TM) since Ruby 2.3: Use the squiggly HEREDOC <<~ to define a multi-line string with newlines and proper indentation:

    conn.exec <<~EOS
                select attr1, attr2, attr3, attr4, attr5, attr6, attr7
                from table1, table2, table3, etc, etc, etc, etc, etc
                where etc etc etc etc etc etc etc etc etc etc etc etc etc
              EOS
    
    # -> "select...\nfrom...\nwhere..."
    

    If proper indentation is not a concern, then single and double quotes can span multiple lines in Ruby:

    conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 
               from table1, table2, table3, etc, etc, etc, etc, etc, 
               where etc etc etc etc etc etc etc etc etc etc etc etc etc"    
    
    # -> "select...\n           from...\n           where..."
          
    

    If single or double quotes are cumbersome because that would need lots of escaping, then the percent string literal notation % is the most flexible solution:

    conn.exec %(select attr1, attr2, attr3, attr4, attr5, attr6, attr7
                from table1, table2, table3, etc, etc, etc, etc, etc
                where (ProductLine = 'R' OR ProductLine = "S") AND Country = "...")
    # -> "select...\n            from...\n            where..."
    

    If the aim is to avoid the newlines (which both the squiggly HEREDOC, quotes and the percent string literal will cause), then a line continuation can be used by putting a backslash \ as the last non-whitespace character in a line. This will continue the line and will cause Ruby to concatenate the Strings back to back (watch out for those spaces inside the quoted string):

    conn.exec 'select attr1, attr2, attr3, attr4, attr5, attr6, attr7 ' \
              'from table1, table2, table3, etc, etc, etc, etc, etc, ' \
              'where etc etc etc etc etc etc etc etc etc etc etc etc etc'
    
    # -> "select...from...where..."
    

    If you use Rails String.squish will strip the string of leading and trailing space and collapse all consecutive whitespaces (newlines, tabs, and all) into a single space:

    conn.exec "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 
               from table1, table2, table3, etc, etc, etc, etc, etc, 
               where etc etc etc etc etc etc etc etc etc etc etc etc etc".squish
    
    # -> "select...attr7 from...etc, where..."
    

    More details:

    Ruby HEREDOC Syntax

    The Here Document Notation for Strings works is a way to designate long blocks of text inline in code. It is started by << followed by a user-defined String (the End of String terminator). All following lines are concatenated until the End of String terminator is found at the very beginning of a line:

    puts <<HEREDOC 
    Text Text Text Text
    Bla Bla
    HEREDOC
    # -> "Text Text Text Text\nBlaBla"
    

    The End of String terminator can be chosen freely, but it is common to use something like "EOS" (End of String) or something that matches the domain of the String such as "SQL".

    HEREDOC supports interpolation by default or when the EOS terminator is double quoted:

    price = 10
    print <<"EOS"  # comments can be put here
    1.) The price is #{price}.
    EOS
    # -> "1.) The price is 10."
    

    Interpolation can be disabled if the EOS terminator is single quoted:

    print <<'EOS' # Disabled interpolation
    3.) The price is #{price}.
    EOS
    # -> "3.) The price is #{price}."
    

    One important restriction of the <<HEREDOC is that the End of String terminator needs to be at the beginning of the line:

      puts <<EOS 
        def foo
          print "foo"
        end
      EOS
    EOS
    #-> "....def foo\n......print "foo"\n....end\n..EOS
    

    To get around this, the <<- syntax was created. It allows the EOS terminator to be indented to make the code look nicer. The lines between the <<- and EOS terminator are still used in their full extend including all indentation:

    puts <<-EOS # Use <<- to indent End of String terminator
      def foo
        print "foo"
      end
    EOS
    # -> "..def foo\n....print "foo"\n..end"
    

    Since Ruby 2.3, we now have the squiggly HEREDOC <<~ removes leading whitespace:

    puts <<~EOS # Use the squiggly HEREDOC <<~ to remove leading whitespace (since Ruby 2.3!)
      def foo
        print "foo"
      end
    EOS
    # -> "def foo\n..print "foo"\nend"
    

    Empty lines and lines which only contains tabs and space are ignored by <<~

    puts <<~EOS.inspect 
      Hello
    
        World!
    EOS
    #-> "Hello\n..World!"
    

    If both tabs and spaces are used, tabs are considered as equal to 8 spaces. If the least-indented line is in the middle of a tab, this tab is not removed.

    puts <<~EOS.inspect
    <tab>One Tab
    <space><space>Two Spaces
    EOS
    # -> "\tOne Tab\nTwoSpaces"
    

    HEREDOC can do some crazy stuff such as executing commands using backticks:

    puts <<`EOC`            
    echo #{price}
    echo #{price * 2}
    EOC
    

    HEREDOC String definitions can be "stacked", which means that the first EOS terminator (EOSFOO below) will end the first string and start the second (EOSBAR below):

    print <<EOSFOO, <<EOSBAR    # you can stack them
    I said foo.
    EOSFOO
    I said bar.
    EOSBAR
    

    I don't think anybody would ever use it as such, but the <<EOS is really just a string literal and can be put whereever a string can normally be put:

    def func(a,b,c)
      puts a
      puts b
      puts c
    end
    
    func(<<THIS, 23, <<THAT) 
    Here's a line
    or two.
    THIS
    and here's another.
    THAT
    

    If you don't have Ruby 2.3, but Rails >= 3.0 then you can use String.strip_heredoc which does the same as <<~

    # File activesupport/lib/active_support/core_ext/string/strip.rb, line 22
    class String
      def strip_heredoc
        gsub(/^#{scan(/^[ \t]*(?=\S)/).min}/, "".freeze)
      end
    end
    
    puts <<-USAGE.strip_heredoc # If no Ruby 2.3, but Rails >= 3.0
      This command does such and such.
    
      Supported options are:
        -h         This message
        ...
    USAGE
    

    Troubleshooting

    If you see errors when Ruby parses your file, then it is most likely that you either have extra leading or trailing spaces with a HEREDOC or extra trailing spaces with a squiggly HEREDOC. For example:

    What you see:

    database_yml = <<~EOS
      production:
        database: #{fetch(:user)}
        adapter: postgresql
        pool: 5
        timeout: 5000
    EOS  
    

    What Ruby tells you:

    SyntaxError: .../sample.rb:xx: can't find string "EOS" anywhere before EOF
    ...sample.rb:xx: syntax error, unexpected end-of-input, expecting `end'
    

    What is at fault:

    Spot the extra spaces after the terminating EOS.

    Percent String Literals

    See RubyDoc for how to use the percentage sign followed by a string in a parentheses pair such as a %(...), %[...], %{...}, etc. or a pair of any non-alphanumeric character such as %+...+

    Last Words

    Last, to get the answer to the original question "Is there a way to imply concatenation?" answered: Ruby always implies concatenation if two strings (single and double quoted) are found back to back:

    puts "select..." 'from table...' "where..."
    # -> "select...from table...where..."
    

    The caveat is that this does not work across line-breaks, because Ruby is interpreting an end of statement and the consequitive line of just strings alone on a line doesn't do anything.

    0 讨论(0)
  • 2020-12-04 05:21
    conn.exec [
      "select attr1, attr2, attr3, ...",
      "from table1, table2, table3, ...",
      "where ..."
    ].join(' ')
    

    This suggestion has the advantage over here-documents and long strings that auto-indenters can indent each part of the string appropriately. But it comes at an efficiency cost.

    0 讨论(0)
  • 2020-12-04 05:23

    Recently with the new features in Ruby 2.3 the new squiggly HEREDOC will let you write our multiline strings in a nice manner with a minimal change so using this combined with the .squish (if you are using rails) will let you write multiline in a nice way! in case of just using ruby, you can do a <<~SQL.split.join(" ") which is almost the same

    [1] pry(main)> <<~SQL.squish
    [1] pry(main)*   select attr1, attr2, attr3, attr4, attr5, attr6, attr7
    [1] pry(main)*   from table1, table2, table3, etc, etc, etc, etc, etc,
    [1] pry(main)*   where etc etc etc etc etc etc etc etc etc etc etc etc etc
    [1] pry(main)* SQL
    => "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 from table1, table2, table3, etc, etc, etc, etc, etc, where etc etc etc etc etc etc etc etc etc etc etc etc etc"
    

    ref: https://infinum.co/the-capsized-eight/multiline-strings-ruby-2-3-0-the-squiggly-heredoc

    0 讨论(0)
  • 2020-12-04 05:24

    Elegant Answer Today:

    <<~TEXT
    Hi #{user.name}, 
    
    Thanks for raising the flag, we're always happy to help you.
    Your issue will be resolved within 2 hours.
    Please be patient!
    
    Thanks again,
    Team #{user.organization.name}
    TEXT
    

    Theres a difference in <<-TEXT and <<~TEXT, former retains the spacing inside block and latter doesn't.

    There are other options as well. Like concatenation etc. but this one makes more sense in general.

    If I am wrong here, let me know how...

    0 讨论(0)
  • 2020-12-04 05:25

    To avoid closing the parentheses for each line you can simply use double quotes with a backslash to escape the newline:

    "select attr1, attr2, attr3, attr4, attr5, attr6, attr7 \
    from table1, table2, table3, etc, etc, etc, etc, etc, \
    where etc etc etc etc etc etc etc etc etc etc etc etc etc"
    
    0 讨论(0)
  • 2020-12-04 05:28

    There are multiple syntaxes for multi-line strings as you've already read. My favorite is Perl-style:

    conn.exec %q{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
          from table1, table2, table3, etc, etc, etc, etc, etc,
          where etc etc etc etc etc etc etc etc etc etc etc etc etc}
    

    The multi-line string starts with %q, followed by a {, [ or (, and then terminated by the corresponding reversed character. %q does not allow interpolation; %Q does so you can write things like this:

    conn.exec %Q{select attr1, attr2, attr3, attr4, attr5, attr6, attr7
          from #{table_names},
          where etc etc etc etc etc etc etc etc etc etc etc etc etc}
    

    I actually have no idea how these kinds of multi-line strings are called so let's just call them Perl multilines.

    Note however that whether you use Perl multilines or heredocs as Mark and Peter have suggested, you'll end up with potentially unnecessary whitespaces. Both in my examples and their examples, the "from" and "where" lines contain leading whitespaces because of their indentation in the code. If this whitespace is not desired then you must use concatenated strings as you are doing now.

    0 讨论(0)
提交回复
热议问题