简体   繁体   中英

How to make multi-line string literals in Ruby without using HERE-DOCUMENT syntax?

Summary of the problem

I'd like to try Ruby for something I did in Python. In Python it has the r""" syntax to support raw strings , which is nice as it allows one to have raw strings in-line with the code and to concatenate them in more natural way and with no special indentation needed. In Ruby, when using raw strings, one has to use <<'EOT' followed by EOT in separate line which breaks the code layout.

You might ask, why not then use Ruby's %q{} ? Well, because %q{} has limitations compared to Python's r""" as it does not escape multiple \\\\\\ and only handles single \\ .

I am generating Latex code on the fly and write to a file, which later is compiled with pdflatex. The Latex code contain things like \\\\\\ in many places. If I use Ruby's %q{} syntax, then it will not work. So I have to use Ruby's <<'EOT' but I do not want to do this, as it makes the code harder to read in the Ruby source file due to having to break it for indentation to make EOT happy.

I am asking if there is a way to make syntax similar to %q{} , or some function that take string and return same result as if one used EOT , that handles raw strings without the limitation of EOT .

I do not need interpolation. So single quoted strings only. No double quoted. double quotes causes interpolation, which I do not want.

Small working examples to illustrate

Here is a small example in Python, and then I show what I have to do in Ruby to generate the same output.

my_file = open("py_latex.tex", 'w')
x = r"""\\\hline is a raw string"""+r""" another one \\\hline and so on"""
my_file.write(x)

When I open the Latex text file written to in the above, I see the correct result

Mathematica图形

Now to do the same thing in Ruby. I can't write the following (even though I'd like to)

file = File.open('rb_latex.tex','w')
x=%q{\\\hline is a raw string}+%q{ another one \\\hline and so on}
file.write(x)

The above ofcourse is not what I want. When it is written to latex file, it shows up as

Mathematica图形

Using EOT works, as follows

file = File.open('rb_latex.tex','w')
x=<<-'EOT1'+<<-'EOT2'
\\\hline is a raw string
EOT1
 another one \\\hline and so on
EOT2
file.write(x)

And the file now is

Mathematica图形

ps. it makes the second string on new line, this is a side-issue for me now, and will try to find solution for this after I solve the main problem at hand.

Short summary of the question

How to make %q{} like syntax for Ruby that works similar to Python r""" ?

If someone wants to try the above code in Ruby, make sure there is no space after EOT . I also include below the full source code.

Python full source

import os
os.chdir(" direct to change to here ")    
my_file = open("py_latex.tex", 'w')
x = r"""\\\hline is a raw string"""+r""" another one \\\hline and so on"""
my_file.write(x)
my_file.close()

Ruby source code

#!/usr/local/bin/ruby -w
Dir.chdir("/home/....")
file = File.open('rb_latex.tex','w')
#x=%q{\\\hline is a raw string}+%q{ another one \\\hline and so on}
x=<<-'EOT1'+<<-'EOT2'
\\\hline is a raw string
EOT1
 another one \\\hline and so on
EOT2
file.write(x)
file.close

Update

To answer comment below:

The idea is that it is supposed to act exactly like HERE-DOCUMENT, but with the nice syntax of %q{} to allow one to more easier format the string in the ruby source code. ie anything inside should be written to the file as is, no matter what it is.

I tested the solution provided below, but it does not work for all cases. Here is a test case:

#!/usr/local/bin/ruby -w    
class String
  def raw
    gsub('\\'*2) { '\\'*3 }
  end
end    
class Array
  def raw(separator = $,)
    map(&:raw).join(separator)
  end
end

Dir.chdir("/home/me")
file = File.open('rb_latex.tex','w')
x=%q{'\\'\hline \\\\\\ (6 of them) // some stuff follows. All should be raw string
            <!DOCTYPE html>
            \[ stuff \]
            <html>
            <head>
            <title>title</title>
            <style>
            video {
              width: 100%    !important;
              eight: auto   !important;
            }
            </html> \"quotes\"    (did you see this?)
            \\\hline $\sin(x)$
            </style>'  //notice this ' is in the raw string!, ok!
            \begin{tabular}{c}\\\hline  '''''' (6 of them)
            x\\\hline
            \end{tabular}}.raw+%q{another '''one \\\hline and so on'}.raw

file.write(x)
file.close

Looking at the file written, it is not the same as the raw string:

在此输入图像描述

Now compare with Python r"""

import os
os.chdir("/home/me")    
my_file = open("py_latex.tex", 'w')
x =r"""\\'\hline \\\\\\ (6 of them) // some stuff follows. All should be raw string
            <!DOCTYPE html>
            \[ stuff \]
            <html>
            <head>
            <title>title</title>
            <style>
            video {
              width: 100%    !important;
              eight: auto   !important;
            }
            </html> \"quotes\"    (did you see this?)
            \\\hline $\sin(x)$
            </style>'  //notice this ' is in the raw string!, ok!
            \begin{tabular}{c}\\\hline  '''''' (6 of them)
            x\\\hline
            \end{tabular}}"""+r"""{another '''one \\\hline and so on'"""
my_file.write(x)
my_file.close()

Here is the output

在此输入图像描述

And the above is what I want to obtain from Ruby as well.

Cleaning up indentation in here documents

To deal with indentation issues in here documents, one approach is to monkey-patch the core String class, adding an instance method String#undent :

class String
  def undent
    indentation = slice(/^\s+/).length
    gsub(/^.{#{ indentation }}/, '')
  end
end

Then you could rewrite your code like this:

x = <<-'EOT'.undent
  \\\hline is a raw string
   another one \\\hline and so on
EOT

Note: Patching core classes is generally considered bad style and might compromise stability and maintainability. That said, I feel that patching String to add #undent is a sensible exception. That method just has so many benefits and it is not really that invasive.
Ultimately, it's up to you to weigh the pros and cons.

Undoing Ruby's escaping syntax

If you wish to avoid heredocs altogether, and assuming your common case is a series of exactly three \\ s, how about something like the following?

class String
  def raw
    gsub('\\'*2) { '\\'*3 }
  end
end

class Array
  def raw(separator = $,)
    map(&:raw).join(separator)
  end
end

This introduces a set of #raw instance methods to compensate for Ruby's treating the leading backslash as an escape character.

Examples

Example a)

x = '\\\hline is a raw string'.raw + ' another one \\\hline and so on'.raw

Example b)

x = %q{\\\hline is a raw string}.raw + %q{ another one \\\hline and so on}.raw

Example c)

x = ['\\\hline is a raw string', ' another one \\\hline and so on'].raw

or, if you set $, = ' ' beforehand, you can even do away with the leading space:

Example d)

x = ['\\\hline is a raw string', 'another one \\\hline and so on'].raw

Example e)

x = [%q{\\\hline is a raw string}, %q{ another one \\\hline and so on}].raw

or alternatively, given you have set $, = ' ' beforehand:

Example f)

x = [%q{\\\hline is a raw string}, %q{another one \\\hline and so on}].raw

Result

For each of the six examples a) through f), the result is:

\\\hline is a raw string another one \\\hline and so on

If this is a single one-off chunk of data, then Ruby has the little known DATA file handle:

#!/bin/env ruby
puts DATA.read
__END__
Hi there, this is data
\\\\quad backslashes are no problem!\\\\

After the magic __END__ , anything left in the file is treated as unescaped string data, which can be read from a file handle named DATA .

So, your script could read like:

#!/usr/local/bin/ruby -w
File.open('/home/me/rb_latex.tex','w') {|fp| fp.print DATA.read }
__END__
'\\'\hline \\\\\\ (6 of them) // some stuff follows. All should be raw string
<!DOCTYPE html>
\[ stuff \]
<html>
<head>
<title>title</title>
<style>
video {
  width: 100%    !important;
  eight: auto   !important;
}
</html> \"quotes\"    (did you see this?)
\\\hline $\sin(x)$
</style>'  //notice this ' is in the raw string!, ok!
\begin{tabular}{c}\\\hline  '''''' (6 of them)
x\\\hline
\end{tabular}another '''one \\\hline and so on'

Though, I am curious: why are you bothering to generate this as an intermediate Ruby script, whose only job is to write it out to another file? Wouldn't it be more expedient to simply write the output directly to the target file?

Using <<'EOT' should get you what you want (note the single quotes on the end-tag):

my_file = File.open("/tmp/rb_latex.tex", 'w')
x = <<'EOT'
\\'\hline \\\\\\ (6 of them) // some stuff follows. All should be raw string
            <!DOCTYPE html>
            \[ stuff \]
            <html>
            <head>
            <title>title</title>
            <style>
            video {
              width: 100%    !important;
              eight: auto   !important;
            }
            </html> \"quotes\"    (did you see this?)
            \\\hline $\sin(x)$
            </style>'  //notice this ' is in the raw string!, ok!
            \begin{tabular}{c}\\\hline  '''''' (6 of them)
            x\\\hline
            \end{tabular}}"""+r"""{another '''one \\\hline and so on'"""
EOT

my_file.write(x)
my_file.close()

That produces this file:

cat /tmp/rb_latex.tex
\\'\hline \\\\\\ (6 of them) // some stuff follows. All should be raw string
            <!DOCTYPE html>
            \[ stuff \]
            <html>
            <head>
            <title>title</title>
            <style>
            video {
              width: 100%    !important;
              eight: auto   !important;
            }
            </html> \"quotes\"    (did you see this?)
            \\\hline $\sin(x)$
            </style>'  //notice this ' is in the raw string!, ok!
            \begin{tabular}{c}\\\hline  '''''' (6 of them)
            x\\\hline
            \end{tabular}}"""+r"""{another '''one \\\hline and so on'"""

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM