2
\$\begingroup\$

I have a series of string manipulation, I realize this with the following code. I want it to be more elegant and readable such as str.sub_heading().sub_codeblock()..., how could I rewrite my code?

def sub_heading(str)
  str.gsub!(/^(#+)\w+/) {|m|  m.gsub("#", "=")}
  str.gsub!(/^(=+)(.+)/, '\1 \2 \1')
end

def sub_codeblock(str)
  str.gsub!(/```(.+\n)((.|\n)+)```/,  '{{{#!text_pygments \1\2}}}')
end

def sub_inlinecode(str)
  str.gsub!(/`(.+)`/, '{{{\1}}}')
end

def sub_bold(str)
  str.gsub!(/([^\w]|^)\*\*\w[\w\s\d\-_'"]*\*\*([^\w]|$)/) {|m|  m.gsub("**", "'''")}
end

def sub_italic(str)
  str.gsub!(/([^*]|^)\*\w[\w\s\d\-_'"]*\*([^*]|$)/) {|m|  m.gsub("*", "''")}
end

def sub_link(str)
  str.gsub!(/\[\[(.+)\|(.+)\]\]/, '[[\2|\1]]')
end

sub_heading(inputdata)
sub_codeblock(inputdata)
sub_inlinecode(inputdata)
sub_bold(inputdata)
sub_italic(inputdata)
sub_link(inputdata)
\$\endgroup\$

2 Answers 2

1
\$\begingroup\$

You may collect the replacements in a Hash and call them on after the other.

replacements = { 
  /1/ => ' one ',
  /2/ => ' two ',
  /([34])/ => ' (\1 is three or four) ',
}

test = '123'
replacements.each{|regex, repl|
  test.gsub!(regex, repl)
}
p test

The gsub-version with blocks need a little trick:

replacements = { 
  /1/ => ' one ',
  /2/ => ' two ',
  /3/ => Proc.new{|m| m*2},
}

test = '123'
replacements.each{|regex, repl|
  if repl.is_a?(Proc)
    test.gsub!(regex){|m| repl.call(m)}
  else
  test.gsub!(regex, repl)
  end
}
p test

In your case you may define your replacements with:

Replacements = {
  # sub_heading 
  /^(#+)\w+/                                    => Proc.new{|m|  m.gsub("#", "=")},
  /^(=+)(.+)/                                   => '\1 \2 \1',
  # sub_codeblock
  /```(.+\n)((.|\n)+)```/                       => '{{{#!text_pygments \1\2}}}',
  # sub_inlinecode 
   /`(.+)`/                                     => '{{{\1}}}',
  # sub_bold
  /([^\w]|^)\*\*\w[\w\s\d\-_'"]*\*\*([^\w]|$)/  => Proc.new{|m|  m.gsub("**", "'''")},
  # sub_italic
  /([^*]|^)\*\w[\w\s\d\-_'"]*\*([^*]|$)/        => Proc.new{|m|  m.gsub("*", "''")},
  # sub_link
  /\[\[(.+)\|(.+)\]\]/                          => '[[\2|\1]]',
}

def my_replace(inputstring)
  Replacements.each{|regex, repl|
    if repl.is_a?(Proc)
      inputstring.gsub!(regex){|m| repl.call(m)}
    else
    inputstring.gsub!(regex, repl)
    end
  }
end

my_replace(inputdata)
\$\endgroup\$
1
\$\begingroup\$

I want it to be more elegant and readable such as str.sub_heading().sub_codeblock()

  1. If you want to call directly on String object you must open the class and add the methods.

  2. If you want "str.method1.method2...." (which is the correct approach) you must not use in-place methods like sub!. So:

    class String
     def sub_heading(str)
       gsub(/^(#+)\w+/) { |m| m.gsub("#", "=") }.
        gsub(/^(=+)(.+)/, '\1 \2 \1')
     end
    
     ....
    end
    

You get the idea for the others.

However, I woulnd't open a standard class to add such specific functions, write a class-method with this always (remember: create new strings, don't update).

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.