Location via proxy:   [ UP ]  
[Report a bug]   [Manage cookies]                
SlideShare a Scribd company logo
My First Rails Plugin
     Marking up user content
The problem
Lots of websites invite
      user input.
Blogs
Forums
Wikis
‘Web 2.0’ sites
And they all have to
format it somehow.
There are some
existing solutions
‘Some HTML accepted’
Textile
Markdown
But asking users to pick
between them is confusing!
Some of the syntax is a
  bit unnatural too...
Textile
“Test”:http://www.test.com


# Ordered list
# Ordered list
Textile
Markdown
> This is a blockquote with two paragraphs. Lorem ipsum dolor sit amet,
> consectetuer adipiscing elit. Aliquam hendrerit mi posuere lectus.
> Vestibulum enim wisi, viverra nec, fringilla in, laoreet vitae, risus.
>
> Donec sit amet nisl. Aliquam semper ipsum sit amet velit. Suspendisse
> id sem consectetuer libero luctus adipiscing.


=> <blockquote><p>This is a blockquote with two paragraphs. Lorem ipsum
dolor sit amet, consectetuer adipiscing elit. Aliquam hendrerit mi
posuere lectus. Vestibulum enim wisi, viverra nec, fringilla in, laoreet
vitae, risus.</p></blockquote>
Users shouldn’t have to
think about the markup
        syntax!
Instead, interpret what
they’ve typed and make
   intelligent guesses!
I decided to write my
     own code...
...as a Ruby on Rails
        plugin
I want to support...
Paragraphs
Unordered lists
Ordered lists
Blockquotes
Code examples
Rails already has a
simple_format(text) helper
simple_format(text)

simple_format(“Hello.

Are you my mummy?”)

=> “<p>Hello.</p>

<p>Are you my mummy?</p>”
How does
simple_format work?
simple_format()
def simple_format(text, html_options={})
 start_tag = tag('p', html_options, true)

 text = text.to_s.dup
 text.gsub!(/rn?/, quot;nquot;)                     # rn and r -> n

 text.gsub!(/nn+/, quot;</p>nn#{start_tag}quot;)   # 2+ newline   -> paragraph
 text.gsub!(/([^n]n)(?=[^n])/, '1<br />') # 1 newline     -> br

 text.insert 0, start_tag
 text << quot;</p>quot;

end
So borrowing from
      that...
Dealing with newlines
def cleanup_newlines(text)
  text.gsub(/rn?/, quot;nquot;) # rn and r -> n
end
Block elements
Unordered list:   Blockquotes:
* Item            “To be, or not to
* Item            be.”
Ordered list:
1. Item
2. Item
Or
1) Item
2) Item
Block elements
def block_element_markup(text)
 blocks = text.split(quot;nnquot;)
 new_text = Array.new
 blocks.each do |block|
      # Work out what type of block element it is.
 end


 return new_text.join(quot;nnquot;)
end
Block elements
if block.match(/((^*s.*$n?)+)/)
  lines = Array.new
  block.each_line do |line|
      lines << line.gsub(/*s(.*)$/, '<li>1</li>')
  end
  new_text << content_tag(quot;ulquot;, quot;nquot; + lines.join(quot;quot;) + quot;nquot;)


elsif block.match(/((^d+[.)]s.*$n?)+)/)
  lines = Array.new
  block.each_line do |line|
      lines << line.gsub(/d+.s(.*)$/, '<li>1</li>')
  end
  new_text << content_tag(quot;olquot;, quot;nquot; + lines.join(quot;quot;) + quot;nquot;)


  ...
end
Block elements
elsif block.match(/^“.*”$/)
  new_text << content_tag(quot;blockquotequot;, content_tag(quot;pquot;, block.gsub(/
^“(.*)”$/, '1')))
  elsif block.match(/(^<code>.*</code>$)/)
  new_text << content_tag(quot;divquot;, block)
else
  new_text << content_tag(quot;pquot;, block)
end
Dealing with links...
def auto_link_urls(text, href_options = {})

  extra_options = tag_options(href_options.stringify_keys) || quot;quot;

  auto_link_re = %r{

   (                            # leading text

       <w+.*?>|                # leading HTML tag, or
       [^=!:'quot;/]|               # leading punctuation, or
       ^                        # beginning of line

   )

   (https?://|ftp://)                # protocol spec

   (

       [-w]+                   # subdomain or domain
       (?:.[-w]+)*            # remaining subdomains or domain
       (?::d+)?                # port
       (?:/(?:(?:[~w+@%-]|(?:[,.;:][^s$]))+)?)* # path
       (?:?[w+@%&=.;-]+)?     # query string
       (?:#[w-/]*)?           # trailing anchor

   )(([[:punct:]]|s|<|$))         # trailing text

  }x

  text.gsub(auto_link_re) do

      all, a, b, c, d = $&, $1, $2, $3, $5
      text = a + quot;<a href=quot;quot; + b + c + quot;quot;>quot; + truncate_in_middle(c, 40) + quot;</a>quot; + $5

  end

end
Truncate URLS in the
            middle...
def truncate_in_middle(text, length = 30, truncate_string = quot;...quot;)
  if text
      l = ((length - truncate_string.chars.length) / 2).to_int
      chars = text.chars
    return (chars.length > length ? chars[0...l] + truncate_string
+ chars[chars.length-l...chars.length] : text).to_s
  end
end
Detecting code snippets
 def mark_code(text)
   h(text).
   gsub(/(^&lt;[a-zA-Z]+.*$|&lt;[a-zA-Z]+.*&gt;)/) do
       text = quot;<code>quot; + ($1) + quot;</code>quot;
   end
 end


 <html>   => <code>&lt;html&gt;</code>
Detecting phone
            numbers
07736111111

=> <a tel=quot;07736111111quot;> 07736111111</a>
 def auto_link_phone_numbers(text)
   text.gsub(/(s|^)((0|+44)d{10,10})b/) do
     text = $1 + quot;<a href=quot;tel:quot; + $2 + quot;quot;>quot; + $2
 + quot;</a>quot;
   end
 end
Typography
quot; => “ / ”


' => ‘ / ’ (quote) / ’ (apos) / ' (prime)


... => …


etc...
Typography
gsub(/([^.])...([^.]|$)/, '1…2')


gsub(/([^s]+)quot;/, '1”')


gsub(/([^s]+)'(s)/, '1’2')


gsub(/([^s])'([^s])/, '1’2')


etc...
Tests!
Hosting




http://code.google.com/p/usertext/
Live demo!
http://www.usertext.org
What next?
What next?
•   Test on some real user input!
What next?
•   Test on some real user input!
•   Make it configurable
What next?
•   Test on some real user input!
•   Make it configurable
•   Port to PHP (for Wordpress)?
What next?
•   Test on some real user input!
•   Make it configurable
•   Port to PHP (for Wordpress)?
•   gem vs plugin?
What next?
•   Test on some real user input!
•   Make it configurable
•   Port to PHP (for Wordpress)?
•   gem vs plugin?
•   Get feedback from other developers.
Thanks...
Frankie Roberto
frankie@frankieroberto.com

More Related Content

My First Rails Plugin - Usertext