Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

string

ruby

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 graphics

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 graphics

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 graphics

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. i.e. 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:

enter image description here

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

enter image description here

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

like image 937
Nasser Avatar asked Jun 27 '15 23:06

Nasser


People also ask

How do you create a multiline string in Ruby?

Use the + Operator for Writing Multi-Line String in Ruby The string concatenation operator is naive to write multi-line strings in Ruby. Unlike the above method, using + does not preserve whitespaces or newlines.

How do I write a multi line string literal in C?

We can use string literal concatenation. Multiple string literals in a row are joined together: char* my_str = "Here is the first line." "Here is the second line."; But wait!

What is a string literal in Ruby?

Strings are most often created with a String literal. A literal is a special syntax in the Ruby language that creates an object of a specific type. For example, 23 is a literal that creates a ​Fixnum object. As for String literals, there are several forms.

What is %q in Ruby?

Alternate double quotes The %Q operator (notice the case of Q in %Q ) allows you to create a string literal using double-quoting rules, but without using the double quote as a delimiter. It works much the same as the %q operator.


1 Answers

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.

like image 195
Synoli Avatar answered Nov 15 '22 08:11

Synoli