8

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.

Nasser
  • 12,849
  • 6
  • 52
  • 104
  • From your examples and your latest comment, I now assume: **1.** you wish to encode your strings as raw as possible; **2.** your strings frequently contain series of literal backslashes, in particular series of **exactly three** backslashes; **3.** you don’t want interpolation (→ double quotes are not an option); **4.** you don’t want syntactical line breaks or indentation (→ heredocs are not an option). Is that what your requirements are? – Synoli Jun 28 '15 at 16:57

4 Answers4

1

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.

Synoli
  • 1,123
  • 1
  • 13
  • 17
  • thanks for the answer. I know about the undent from this [answer](http://stackoverflow.com/questions/3772864/how-do-i-remove-leading-whitespace-chars-from-ruby-heredoc) and used it. But I fail to see how this answers the question I have? I am trying to avoid using EOT all together and I explained why. The issue is that EOT forces one to indent the strings in the Ruby source code itself in certain way. I am not talking about indentation of the Latex code after it was written to outside file. – Nasser Jun 28 '15 at 02:15
  • I don’t see how heredocs force you to (horizontally) indent the strings in a certain way when you use them together with `String#undent`. So if I understand your examples correctly, you’re not only trying to avoid restrictions in (horizontal) indentation, but you also don’t want (vertical) line breaks leading or trailing your literals? – Synoli Jun 28 '15 at 16:56
0

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
Synoli
  • 1,123
  • 1
  • 13
  • 17
  • Thanks again. I've been testing it. But I am afraid it does not work. 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. Your parser there did not handle all cases. I'll update my question and add an example input for you to try. The output in the file after it is written to, should be exactly the same as the raw string in the code. – Nasser Jun 28 '15 at 17:56
  • Thanks for your latest update and test case, but given your set of requirements I’m going to have to pass that one. – Synoli Jun 29 '15 at 15:59
0

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?

Chris Heald
  • 61,439
  • 10
  • 123
  • 137
  • thanks for the answer. This however is not what I am looking for. I need to be able to have small pieces of raw strings generated on the fly in the code, since I can more easily insert strings in between with different values based on some other logic to generate code on the fly. It is easier to do this and it also makes the code easier to read when I can have the raw strings flow with the code itself than it being in different places or having to break the code indentation to be able to use it. I can do this with Python's `r"""` now, and just wanted to so if I can do the same with Ruby. – Nasser Jun 29 '15 at 02:46
0

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'"""
Gary H
  • 1
  • 1