1

This question is partly answered in How do I remove leading whitespace chars from Ruby HEREDOC?

In Rails 3 there is a method #strip_heredoc, that strips all whitespace. But when inserting lines in an existing file, that already has identation, this doesn't work too well. An example:

begin
  insert_into_file "#{app_name}/config/environments/production.rb", <<-DEVISE_MAILER_STUFF.strip_heredoc, :before => "end\n"
    # for devise
    config.action_mailer.default_url_options = { :protocol => 'https', :host => 'YOURHOSTNAME' }
    config.action_mailer.delivery_method = :smtp
    something.each do |x|
      do stuff
    end
  DEVISE_MAILER_STUFF
end

The 'begin' and 'end' are only added to show that the source code has indentation. The heredoc has 4 spaces before the line '# for devise'. @strip_heredoc will delete all these spaces, but it will maintain the two extra spaces in the line 'do stuff'.

In environments/production.rb this will look like this:

MyApp::Application.configure do
  # Settings specified here will take precedence over those in config/application.rb
  # *** Identation here is two spaces! (We are in a block.) ***

# for devise
config.action_mailer.default_url_options = { :protocol => 'https', :host => 'YOURHOSTNAME' }
config.action_mailer.delivery_method = :smtp
something.each do |x|
  do stuff
end

  # back to original identation of this file
end # MyApp::Application.configure block!

How to solve this? Maybe there are other ways instead of a heredoc? I thought maybe strip_heredoc(min) where min is the minimum of spaces to keep, but that doesn't work well with tabs I guess. Or have the first line of the heredoc determine the left margin, like this:

puts <<-HEREDOC
  FIRST_LINE
    Real code here
HEREDOC

That 'FIRST_LINE' would be deleted by strip_heredoc, but it would also set the number of spaces/whitespace that needs to be deleted. So the output would have 2 spaces in front of 'Real code here'.

Update: Maybe something like this:

String.class_eval do
  def strip_heredoc_with_indent(indent=0)
    new_indent = ( self.empty? ? 0 : ( scan(/^[ \t]*(?=\S)/).min.size - indent ) )
    gsub(/^[ \t]{#{new_indent}}/, '')
  end
end
Community
  • 1
  • 1
  • possible duplicate of [How do I remove leading whitespace chars from Ruby HEREDOC?](http://stackoverflow.com/questions/3772864/how-do-i-remove-leading-whitespace-chars-from-ruby-heredoc) – Simone Carletti Jan 03 '14 at 17:01

3 Answers3

2

I went with strip_heredoc_with_indent(indent) as described here.

Dr1Ku
  • 2,875
  • 3
  • 47
  • 56
  • [These techniques](http://melborne.github.io/2012/04/27/ruby-heredoc-without-leading-whitespace/) are also notable, nevermind the language barrier. I've had success with the last solution on the page (search for the Twitter mention). – Dr1Ku Feb 28 '14 at 12:34
0

strip it yourself.

insert_into_file "#{app_name}/config/environments/production.rb",
                 <<-DEVISE_MAILER_STUFF.gsub(/^  /, ''), :before => "end\n"
  ...
DEVISE_MAILER_STUFF

This says at the beginning of each line, replace two spaces with the empty string. It's simple, it's easy to understand, it's easy to fix or change.


Alternatively, you could do it in your template, but I can't tell if you're actually using a template or if you're parsing the file in some manner and replacing rows (hint, you should probably be using a template).


"I thought maybe strip_heredoc(min) where min is the minimum of spaces to keep, but that doesn't work wel with tabs, I guess."

The Ruby standard is two spaces. Even if it weren't, tabs always screw everything up. Use spaces.


Regarding your update

It's complicated. Do you need a generalized solution like this? Are you doing this in many places? Or in places where you can't just glance at it and see how much to gsub? Also, extending core classes is a dangerous game, I've seen codebases that are stuck on Rails 1 and customized Ruby 1.8 because they were too cavalier about changing built in behaviour. When it changed, they weren't able to change with it. I know Rails does it, but Rails is not a good reference for best practices.

It's also got some potential bugs:

  • When given this String, and passing no params, what should it do? " a\n\n b" I would expect nothing, because the shortest line is an empty line, so your new_indent should be zero, but it will be one.
  • What happens happens when the indent they pass in exceeds the scan(...).min.size?
  • What happens if they pass in something negative?

To me it ultimately seems like a complicated solution that can't ever truly be correct, which couples any code that uses it to this app, while all of this may not actually be necessary.

Joshua Cheek
  • 30,436
  • 16
  • 74
  • 83
  • Thanks for taking the time to answer this question. However, when I wrap my code in a module, press Alt-Shift-F in my IDE, then everything gets moved 2 spaced to the right. That means the gsub(/^ /, '') won't cut the cake anymore. You are right about if someone puts in something negative, or if the indent exceeds the calculated minimal indent. This would result in negative new_indent, which might do nothing in a regex. When giving no params, indent will be 0 by default. The new_indent will be 1, as per the shortest line, but empty lines dont' have whitespace so the regex doesn't do a thing. – Ace Suares user1266770 Jul 16 '12 at 01:01
  • Actually, to have the first line an 'empty' line, it needs to have the white space added. That is also a weirdness in the rails strip_heredoc, AFAIK. – Ace Suares user1266770 Jul 16 '12 at 01:01
0

You could use some kind of delimiter to indicate the left margin.

def heredoc_with_margin()
  doc = <<-EOT.gsub(/^\s*\|/, '')
    |top:
    |  sub-one:
    |    sub-sub-one
    |  sub-two:
    |    sub-sub-two
    EOT
  return doc
end
amacleod
  • 1,450
  • 2
  • 15
  • 23