Comment supprimer les principaux caractères blancs de Ruby HEREDOC?

J’ai un problème avec un heredoc Ruby que j’essaie de faire. Il renvoie les espaces blancs de chaque ligne même si j’inclus l’opérateur -, qui est supposé supprimer tous les caractères d’espacement. 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 ma sortie 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" 

ceci, bien sûr, est juste dans cette instance spécifique, sauf pour tous les espaces entre le premier “et \ t. est-ce que quelqu’un sait ce que je fais mal ici?

La forme < <- de heredoc ignore uniquement les espaces blancs pour le délimiteur de fin.

Avec Ruby 2.3 et versions ultérieures, vous pouvez utiliser un squedly heredoc ( < <~ ) pour supprimer les espaces blancs des lignes de contenu:

 def test < <~END First content line. Two spaces here. No space here. END end test # => "First content line.\n Two spaces here.\nNo space here.\n" 

De la documentation des littéraux Ruby:

L'indentation de la ligne la moins en retrait sera supprimée de chaque ligne du contenu. Notez que les lignes vides et les lignes composées uniquement d'tabs littéraux et d'espaces seront ignorées aux fins de la détermination de l'indentation, mais les tabulations et les espaces échappés sont considérés comme des caractères non indentés.

Si vous utilisez Rails 3.0 ou plus récent, essayez #ssortingp_heredoc . Cet exemple des documents 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, elle recherche la ligne la moins en retrait dans la chaîne entière et supprime cette quantité d'espaces blancs."

Voici l'implémentation de active_support / core_ext / ssortingng / ssortingp.rb :

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

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

Pas grand chose à faire que je sache j’ai peur. Je 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 bidouillage.

EDIT: En prenant l'inspiration de René Saarsoo ci-dessous, je suggère plutôt quelque chose comme ça:

 class Ssortingng 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 prendre en compte lorsque la première ligne n'est pas la plus éloignée à gauche également.

Voici une version beaucoup plus simple du script non lié que j’utilise:

 class Ssortingng # Ssortingp leading whitespace from each line that is the same as the # amount of whitespace on the first line of the ssortingng. # 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 en retrait que d’autres, et que vous voulez (comme Rails) se désindenter en fonction de la ligne la moins en retrait, vous pouvez plutôt utiliser:

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

Notez que si vous recherchez \s+ place de [ \t]+ vous risquez de retirer des nouvelles lignes de votre heredoc au lieu de créer des espaces. Pas souhaitable!

< <- dans Ruby ignorera uniquement l’espace de tête pour le délimiteur de fin, ce qui lui permettra d’être correctement mis en retrait. Il ne supprime pas l’espace de tête sur les lignes à l’intérieur de la chaîne, malgré ce que certains documents en ligne pourraient dire.

Vous pouvez gsub espaces blancs 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 simplement supprimer des espaces, en laissant les tabs:

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

Certaines autres réponses trouvent le niveau d’indentation de la ligne la moins en retrait et le suppriment de toutes les lignes, mais en considérant la nature de l’indentation en programmation (que la première ligne est la moins en retrait), vous devriez chercher le niveau d’indentation du première ligne .

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

Comme l’affiche originale, j’ai aussi découvert la syntaxe < <-HEREDOC et < <-HEREDOC été très déçue qu'elle ne se soit pas comscope comme je le pensais.

Mais au lieu de répandre mon code avec gsub-s, j'ai étendu la classe Ssortingng:

 class Ssortingng # Removes beginning-whitespace from each line of a ssortingng. # But only as many whitespace as the first line has. # # Ment to be used with heredoc ssortingngs like so: # # text = < <-EOS.unindent # This line has no indentation # This line has 2 spaces of indentation # This line is also not indented # EOS # def unindent lines = [] each_line {|ln| lines << ln } first_line_ws = lines[0].match(/^\s+/)[0] re = Regexp.new('^\s{0,' + first_line_ws.length.to_s + '}') lines.collect {|line| line.sub(re, "") }.join end end 

Remarque: Comme @radiospiel l’a souligné, Ssortingng#squish est uniquement disponible dans le contexte ActiveSupport .


Je crois Ruby Ssortingng#squish est plus proche de ce que vous cherchez vraiment:

Voici comment je traiterais votre exemple:

 def distinct_count < <-SQL.squish SELECT CAST('#{name}' AS VARCHAR(30)) as COLUMN_NAME, COUNT(DISTINCT #{name}) AS DISTINCT_COUNT FROM #{table.call} SQL end 

une autre option facile à retenir est d’utiliser une gem

 require 'unindent' p < <-end.unindent hello world end # => "hello\n world\n" 

Je collectionne les réponses et j’ai ceci:

 class Match < ActiveRecord::Base has_one :invitation scope :upcoming, -> do joins(:invitation) .where(< <-SQL_QUERY.strip_heredoc, Date.current, Date.current).order('invitations.date ASC') CASE WHEN invitations.autogenerated_for_round IS NULL THEN invitations.date >= ? ELSE (invitations.round_end_time >= ? AND match_plays.winner_id IS NULL) END SQL_QUERY end end 

Il génère un excellent SQL et ne sort pas des scopes AR.

J’avais besoin d’utiliser un system avec lequel je pouvais diviser les longues commandes sed entre les lignes, puis supprimer l’indentation AND newlines …

 def update_makefile(build_path, version, sha1) system < <-CMD.strip_heredoc(true) \\sed -i".bak" -e "s/GIT_VERSION[\ ]*:=.*/GIT_VERSION := 20171-2342/g" -e "s/GIT_VERSION_SHA1[\ ]:=.*/GIT_VERSION_SHA1 := 2342/g" "/tmp/Makefile" CMD end 

Donc j'ai trouvé ça:

 class ::Ssortingng def ssortingp_heredoc(compress = false) ssortingpped = gsub(/^#{scan(/^\s*/).min_by(&:length)}/, "") compress ? ssortingpped.gsub(/\n/," ").chop : ssortingpped end end 

Le comportement par défaut consiste à ne pas supprimer les nouvelles lignes, comme tous les autres exemples.