106 votes

Comment supprimer les caractères d'espacement de tête du HEREDOC de Ruby ?

J'ai un problème avec un heredoc Ruby que j'essaie de faire. Il renvoie l'espacement de tête de chaque ligne même si j'inclus l'opérateur -, qui est censé supprimer tous les caractères d'espacement de tête. ma méthode ressemble à ceci :

    def distinct_count
    <<-EOF
        \tSELECT
        \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
        \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
        \tFROM #{table.call}
    EOF
end

et mon résultat ressemble à ceci :

    => "            \tSELECT\n            \t CAST('SRC_ACCT_NUM' AS VARCHAR(30)) as
COLUMN_NAME\n            \t,COUNT(DISTINCT SRC_ACCT_NUM) AS DISTINCT_COUNT\n
        \tFROM UD461.MGMT_REPORT_HNB\n"

ce qui, bien sûr, est correct dans ce cas précis, à l'exception de tous les espaces entre le premier " et ". \t. Quelqu'un sait-il ce que je fais mal ici ?

124voto

chrisk Points 926

Si vous utilisez Rails 3.0 ou plus récent, essayez #strip_heredoc . Cet exemple tiré de la documentation imprime les trois premières lignes sans indentation, tout en conservant l'indentation à deux espaces des deux dernières lignes :

if options[:usage]
  puts <<-USAGE.strip_heredoc
    This command does such and such.
 
    Supported options are:
      -h         This message
      ...
  USAGE
end

La documentation note également : "Techniquement, il recherche la ligne la moins indentée de toute la chaîne, et supprime cette quantité d'espace blanc en tête."

Voici l'implémentation de active_support/core_ext/string/strip.rb :

class String
  def strip_heredoc
    indent = scan(/^[ \t]*(?=\S)/).min.try(:size) || 0
    gsub(/^[ \t]{#{indent}}/, '')
  end
end

Et vous pouvez trouver les tests dans test/core_ext/string_ext_test.rb .

44voto

einarmagnus Points 1716

Il n'y a pas grand-chose à faire, à ce que je sache, j'en ai peur. Je le fais habituellement :

def distinct_count
    <<-EOF.gsub /^\s+/, ""
        \tSELECT
        \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
        \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
        \tFROM #{table.call}
    EOF
end

Cela fonctionne, mais c'est un peu un bricolage.

EDIT : En m'inspirant de Rene Saarsoo ci-dessous, je suggérerais plutôt quelque chose comme ceci :

class String
  def unindent 
    gsub(/^#{scan(/^\s*/).min_by{|l|l.length}}/, "")
  end
end

def distinct_count
    <<-EOF.unindent
        \tSELECT
        \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
        \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
        \tFROM #{table.call}
    EOF
end

Cette version devrait également gérer les cas où la première ligne n'est pas celle qui est la plus à gauche.

23voto

Phrogz Points 112337

Voici une version beaucoup plus simple de l'unindent script que j'utilise :

class String
  # Strip leading whitespace from each line that is the same as the 
  # amount of whitespace on the first line of the string.
  # Leaves _additional_ indentation on later lines intact.
  def unindent
    gsub /^#{self[/\A[ \t]*/]}/, ''
  end
end

Utilisez-le comme ça :

foo = {
  bar: <<-ENDBAR.unindent
    My multiline
      and indented
        content here
    Yay!
  ENDBAR
}
#=> {:bar=>"My multiline\n  and indented\n    content here\nYay!"}

Si la première ligne peut être plus indentée que les autres, et que vous souhaitez (comme Rails) désindenter en fonction de la ligne la moins indentée, vous pouvez plutôt utiliser :

class String
  # Strip leading whitespace from each line that is the same as the 
  # amount of whitespace on the least-indented line of the string.
  def strip_indent
    if mindent=scan(/^[ \t]+/).min_by(&:length)
      gsub /^#{mindent}/, ''
    end
  end
end

Notez que si vous scannez pour \s+ au lieu de [ \t]+ vous pouvez finir par supprimer les nouvelles lignes de votre heredoc au lieu des espaces blancs. Ce n'est pas souhaitable !

8voto

Brian Campbell Points 101107

<<- en Ruby ignorera seulement l'espace de tête pour le délimiteur de fin, ce qui lui permettra d'être correctement indenté. Il ne supprime pas l'espace de tête sur les lignes à l'intérieur de la chaîne, malgré ce que peut dire la documentation en ligne.

Vous pouvez supprimer vous-même les espaces blancs en tête en utilisant gsub :

<<-EOF.gsub /^\s*/, ''
    \tSELECT
    \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
    \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
    \tFROM #{table.call}
EOF

Ou si vous voulez juste supprimer les espaces, en laissant les tabulations :

<<-EOF.gsub /^ */, ''
    \tSELECT
    \t CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME
    \t,COUNT(DISTINCT #{name}) AS DISTINCT_COUNT
    \tFROM #{table.call}
EOF

6voto

sawa Points 62592

D'autres réponses trouvent le niveau d'indentation de la ligne la moins indentée et le supprimer de toutes les lignes, mais compte tenu de la nature de l'indentation en programmation (la première ligne est la moins indentée), je pense que vous devriez rechercher le niveau d'indentation de la première ligne .

class String
  def unindent; gsub(/^#{match(/^\s+/)}/, "") end
end

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X