rubypants-0.7.1/0000755000004100000410000000000013641342337013604 5ustar www-datawww-datarubypants-0.7.1/LICENSE.rdoc0000644000004100000410000000614113641342337015541 0ustar www-datawww-data= Copyright and License == Copyright Ported by Christian Neukirchen Copyright (C) 2004 Christian Neukirchen Incorporates ideas, comments and documentation by Chad Miller Copyright (C) 2004 Chad Miller Original SmartyPants by John Gruber Copyright (C) 2003 John Gruber == SmartyPants license: Copyright (c) 2003 John Gruber (http://daringfireball.net) All rights reserved. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. * Neither the name "SmartyPants" nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. === RubyPants license RubyPants is a derivative work of SmartyPants and smartypants.py. Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. This software is provided by the copyright holders and contributors "as is" and any express or implied warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose are disclaimed. In no event shall the copyright owner or contributors be liable for any direct, indirect, incidental, special, exemplary, or consequential damages (including, but not limited to, procurement of substitute goods or services; loss of use, data, or profits; or business interruption) however caused and on any theory of liability, whether in contract, strict liability, or tort (including negligence or otherwise) arising in any way out of the use of this software, even if advised of the possibility of such damage. rubypants-0.7.1/.travis.yml0000644000004100000410000000042313641342337015714 0ustar www-datawww-datarvm: - 2.4 - 2.5 - 2.6 - jruby # Workaround for `NoMethodError: undefined method 'spec' for nil:NilClass`, # see https://travis-ci.org/jmcnevin/rubypants/jobs/167182556 # and https://github.com/travis-ci/travis-ci/issues/5239 before_install: - gem install bundler rubypants-0.7.1/test/0000755000004100000410000000000013641342337014563 5ustar www-datawww-datarubypants-0.7.1/test/helper.rb0000644000004100000410000000015313641342337016366 0ustar www-datawww-datarequire 'simplecov' SimpleCov.start require 'codecov' SimpleCov.formatter = SimpleCov::Formatter::Codecov rubypants-0.7.1/test/rubypants_test.rb0000644000004100000410000002650713641342337020210 0ustar www-datawww-datarequire_relative 'helper' require 'minitest/autorun' require_relative '../lib/rubypants' # Test EVERYTHING against SmartyPants.pl output! class RubyPantsTest < Minitest::Test def assert_rp_equal(str, orig, options=[2], entities = {}) assert_equal orig, RubyPants.new(str, options, entities).to_html end def refute_rp_equal(str, orig, options=[2], entities = {}) refute_equal orig, RubyPants.new(str, options, entities).to_html end def assert_verbatim(str) assert_rp_equal str, str end def test_verbatim assert_verbatim "foo!" assert_verbatim "
This is HTML
" assert_verbatim "
This is HTML with tags>" assert_verbatim <html code EOF end def test_quotes assert_rp_equal '"A first example"', '“A first example”' assert_rp_equal '"A first "nested" example"', '“A first “nested” example”' assert_rp_equal '".', '”.' assert_rp_equal '"a', '“a' assert_rp_equal "'.", '’.' assert_rp_equal "'a", '‘a' assert_rp_equal %{

He said, "'Quoted' words in a larger quote."

}, "

He said, “‘Quoted’ words in a larger quote.”

" assert_rp_equal %{"I like the 70's"}, '“I like the 70’s”' assert_rp_equal %{"I like the '70s"}, '“I like the ’70s”' assert_rp_equal %{"I like the '70!"}, '“I like the ‘70!”' assert_rp_equal 'pre"post', 'pre”post' assert_rp_equal 'pre "post', 'pre “post' assert_rp_equal 'pre "post', 'pre “post' assert_rp_equal 'pre--"post', 'pre–“post' assert_rp_equal 'pre--"!', 'pre–”!' assert_rp_equal "pre'post", 'pre’post' assert_rp_equal "pre 'post", 'pre ‘post' assert_rp_equal "pre 'post", 'pre ‘post' assert_rp_equal "pre--'post", 'pre–‘post' assert_rp_equal "pre--'!", 'pre–’!' assert_rp_equal "'", "" assert_rp_equal "foo'", "foo" assert_rp_equal '"', "" assert_rp_equal 'foo"', "foo" assert_rp_equal "foo\u00a0\"bar\"", "foo\u00a0“bar”" assert_rp_equal "foo\u00a0'bar'", "foo\u00a0‘bar’" end def test_dashes assert_rp_equal "foo--bar", 'foo—bar', 1 assert_rp_equal "foo---bar", 'foo---bar', 1 assert_rp_equal "foo----bar", 'foo----bar', 1 assert_rp_equal "--foo--bar--quux--", '—foo—bar—quux—', 1 assert_rp_equal "foo--bar", 'foo⁠—bar', [1, :prevent_breaks] assert_rp_equal "foo --bar", 'foo —bar', 1 assert_rp_equal "foo --bar", 'foo —bar', [1, :prevent_breaks] assert_rp_equal "foo -- bar", 'foo — bar', [1, :prevent_breaks] assert_rp_equal "foo --bar", 'foo —bar', [1, :prevent_breaks] assert_rp_equal "foo--bar", 'foo–bar', 2 assert_rp_equal "foo---bar", 'foo—bar', 2 assert_rp_equal "foo----bar", 'foo----bar', 2 assert_rp_equal "--foo--bar--quux--", '–foo–bar–quux–', 2 assert_rp_equal "foo--bar", 'foo⁠–bar', [2, :prevent_breaks] assert_rp_equal "foo --bar", 'foo –bar', 2 assert_rp_equal "foo --bar", 'foo –bar', [2, :prevent_breaks] assert_rp_equal "foo -- bar", 'foo – bar', [2, :prevent_breaks] assert_rp_equal "foo --bar", 'foo –bar', [2, :prevent_breaks] assert_rp_equal "foo---bar", 'foo⁠—bar', [2, :prevent_breaks] assert_rp_equal "foo ---bar", 'foo —bar', 2 assert_rp_equal "foo ---bar", 'foo —bar', [2, :prevent_breaks] assert_rp_equal "foo --- bar", 'foo — bar', [2, :prevent_breaks] assert_rp_equal "foo ---bar", 'foo —bar', [2, :prevent_breaks] assert_rp_equal "foo--bar", 'foo—bar', 3 assert_rp_equal "foo---bar", 'foo–bar', 3 assert_rp_equal "foo----bar", 'foo----bar', 3 assert_rp_equal "--foo--bar--quux--", '—foo—bar—quux—', 3 assert_rp_equal "foo--bar", 'foo⁠—bar', [3, :prevent_breaks] assert_rp_equal "foo --bar", 'foo —bar', 3 assert_rp_equal "foo --bar", 'foo —bar', [3, :prevent_breaks] assert_rp_equal "foo -- bar", 'foo — bar', [3, :prevent_breaks] assert_rp_equal "foo --bar", 'foo —bar', [3, :prevent_breaks] assert_rp_equal "foo---bar", 'foo⁠–bar', [3, :prevent_breaks] assert_rp_equal "foo ---bar", 'foo –bar', 3 assert_rp_equal "foo ---bar", 'foo –bar', [3, :prevent_breaks] assert_rp_equal "foo --- bar", 'foo – bar', [3, :prevent_breaks] assert_rp_equal "foo ---bar", 'foo –bar', [3, :prevent_breaks] end def test_html_comments assert_verbatim "" assert_verbatim "" assert_verbatim "" assert_rp_equal "----", '––' end def test_pre_tags assert_verbatim "
--
" assert_verbatim "
----
" assert_rp_equal "--
--
", '–
--
' end def test_ellipses assert_rp_equal "foo..bar", 'foo..bar', [:ellipses] assert_rp_equal "foo...bar", 'foo…bar', [:ellipses] assert_rp_equal "foo....bar", 'foo....bar', [:ellipses] # and with :prevent_breaks assert_rp_equal "foo..bar", 'foo..bar', [:ellipses, :prevent_breaks] assert_rp_equal "foo...bar", 'foo⁠…bar', [:ellipses, :prevent_breaks] assert_rp_equal "foo....bar", 'foo....bar', [:ellipses, :prevent_breaks] # dots and spaces assert_rp_equal "foo. . .bar", 'foo…bar', [:ellipses] assert_rp_equal "foo . . . bar", 'foo … bar', [:ellipses] assert_rp_equal "foo. . . .bar", 'foo. . . .bar', [:ellipses] assert_rp_equal "foo . . . . bar", 'foo . . . . bar', [:ellipses] # and with :prevent_breaks assert_rp_equal "foo. . .bar", 'foo⁠…bar', [:ellipses, :prevent_breaks] assert_rp_equal "foo . . . bar", 'foo … bar', [:ellipses, :prevent_breaks] assert_rp_equal "foo. . . .bar", 'foo. . . .bar', [:ellipses, :prevent_breaks] assert_rp_equal "foo . . . . bar", 'foo . . . . bar', [:ellipses, :prevent_breaks] # dots and tab-spaces refute_rp_equal "foo. . .bar", 'foo…bar', [:ellipses] refute_rp_equal "foo . . . bar", 'foo … bar', [:ellipses] assert_rp_equal "foo. . . .bar", 'foo. . . .bar', [:ellipses] assert_rp_equal "foo . . . . bar", 'foo . . . . bar', [:ellipses] # and with :prevent_breaks refute_rp_equal "foo. . .bar", 'foo⁠…bar', [:ellipses, :prevent_breaks] refute_rp_equal "foo . . . bar", 'foo … bar', [:ellipses, :prevent_breaks] assert_rp_equal "foo. . . .bar", 'foo. . . .bar', [:ellipses, :prevent_breaks] assert_rp_equal "foo . . . . bar", 'foo . . . . bar', [:ellipses, :prevent_breaks] # dots and line-breaks refute_rp_equal "foo.\n.\n.bar", 'foo…bar', [:ellipses] refute_rp_equal "foo\n.\n.\n.\nbar", "foo\n…\nbar", [:ellipses] assert_rp_equal "foo.\n.\n.\n.bar", "foo.\n.\n.\n.bar", [:ellipses] assert_rp_equal "foo\n.\n.\n.\n.\nbar", "foo\n.\n.\n.\n.\nbar", [:ellipses] # and with :prevent_breaks refute_rp_equal "foo.\n.\n.bar", "foo⁠…bar", [:ellipses, :prevent_breaks] refute_rp_equal "foo\n.\n.\n.\nbar", "foo …\nbar", [:ellipses, :prevent_breaks] assert_rp_equal "foo.\n.\n.\n.bar", "foo.\n.\n.\n.bar", [:ellipses, :prevent_breaks] assert_rp_equal "foo\n.\n.\n.\n.\nbar", "foo\n.\n.\n.\n.\nbar", [:ellipses, :prevent_breaks] # nasty ones assert_rp_equal "foo. . ..bar", 'foo. . ..bar', [:ellipses] assert_rp_equal "foo. . ...bar", 'foo. . …bar', [:ellipses] assert_rp_equal "foo. . ....bar", 'foo. . ....bar', [:ellipses] # and with :prevent_breaks assert_rp_equal "foo. . ..bar", 'foo. . ..bar', [:ellipses, :prevent_breaks] assert_rp_equal "foo. . ...bar", 'foo. . …bar', [:ellipses, :prevent_breaks] assert_rp_equal "foo. . ....bar", 'foo. . ....bar', [:ellipses, :prevent_breaks] end def test_backticks assert_rp_equal "pre``post", 'pre“post' assert_rp_equal "pre ``post", 'pre “post' assert_rp_equal "pre ``post", 'pre “post' assert_rp_equal "pre--``post", 'pre–“post' assert_rp_equal "pre--``!", 'pre–“!' assert_rp_equal "pre''post", 'pre”post' assert_rp_equal "pre ''post", 'pre ”post' assert_rp_equal "pre ''post", 'pre ”post' assert_rp_equal "pre--''post", 'pre–”post' assert_rp_equal "pre--''!", 'pre–”!' end def test_single_backticks o = [:oldschool, :allbackticks] assert_rp_equal "`foo'", "‘foo’", o assert_rp_equal "pre`post", 'pre‘post', o assert_rp_equal "pre `post", 'pre ‘post', o assert_rp_equal "pre `post", 'pre ‘post', o assert_rp_equal "pre--`post", 'pre–‘post', o assert_rp_equal "pre--`!", 'pre–‘!', o assert_rp_equal "pre'post", 'pre’post', o assert_rp_equal "pre 'post", 'pre ’post', o assert_rp_equal "pre 'post", 'pre ’post', o assert_rp_equal "pre--'post", 'pre–’post', o assert_rp_equal "pre--'!", 'pre–’!', o end def test_stupefy o = [:stupefy] assert_rp_equal "

He said, “‘Quoted’ words " + "in a larger quote.”

", %{

He said, "'Quoted' words in a larger quote."

}, o assert_rp_equal "– — ‘’ “” …", %{- -- '' "" ...}, o assert_rp_equal %{- -- '' "" ...}, %{- -- '' "" ...}, o end def test_process_escapes assert_rp_equal %q{foo\bar}, "foo\\bar" assert_rp_equal %q{foo\\\bar}, "foo\bar" assert_rp_equal %q{foo\\\\\bar}, "foo\\\bar" assert_rp_equal %q{foo\...bar}, "foo...bar" assert_rp_equal %q{foo\.\.\.bar}, "foo...bar" assert_rp_equal %q{foo\'bar}, "foo'bar" assert_rp_equal %q{foo\"bar}, "foo"bar" assert_rp_equal %q{foo\-bar}, "foo-bar" assert_rp_equal %q{foo\`bar}, "foo`bar" assert_rp_equal %q{foo\#bar}, "foo\\#bar" assert_rp_equal %q{foo\*bar}, "foo\\*bar" assert_rp_equal %q{foo\&bar}, "foo\\&bar" end def test_modified_entities entities = { :single_left_quote => 'SHAZAM', :single_right_quote => 'POWZAP' } assert_rp_equal "Testing 'FOO!'", "Testing SHAZAMFOO!POWZAP", [2], entities end def test_named_entities assert_rp_equal "Testing 'FOO!'", "Testing ‘FOO!’", [2, :named_entities] end def test_character_entities assert_rp_equal "Testing 'FOO!'", "Testing ‘FOO!’", [2, :character_entities] assert_rp_equal "foo---bar", "foo⁠—bar", [2, :character_entities, :prevent_breaks] assert_rp_equal "foo ---bar", "foo —bar", [2, :character_entities, :prevent_breaks] assert_rp_equal "foo ---bar", "foo\u00A0—bar", [2, :character_entities, :character_spaces, :prevent_breaks] end end rubypants-0.7.1/.gitignore0000644000004100000410000000001513641342337015570 0ustar www-datawww-dataGemfile.lock rubypants-0.7.1/Rakefile0000644000004100000410000000017613641342337015255 0ustar www-datawww-datadesc "Run all the tests" task :default => [:test] desc "Run all the tests" task :test do ruby 'test/rubypants_test.rb' end rubypants-0.7.1/lib/0000755000004100000410000000000013641342337014352 5ustar www-datawww-datarubypants-0.7.1/lib/rubypants.rb0000644000004100000410000004042513641342337016733 0ustar www-datawww-datarequire_relative 'version' class RubyPants < String extend RubyPantsVersion # Create a new RubyPants instance with the text in +string+. # # Allowed elements in the options array: # # 0 :: do nothing # 1 :: enable all, using only em-dash shortcuts # 2 :: enable all, using old school en- and em-dash shortcuts (*default*) # 3 :: enable all, using inverted old school en and em-dash shortcuts # -1 :: stupefy (translate HTML entities to their ASCII-counterparts) # # If you don't like any of these defaults, you can pass symbols to change # RubyPants' behavior: # # :quotes :: quotes # :backticks :: backtick quotes (``double'' only) # :allbackticks :: backtick quotes (``double'' and `single') # :dashes :: dashes # :oldschool :: old school dashes # :inverted :: inverted old school dashes # :ellipses :: ellipses # :prevent_breaks :: use nbsp and word-joiner to avoid breaking # before dashes and ellipses # :named_entities :: used named entities instead of the default # decimal entities (see below) # :convertquotes :: convert " entities to # " # :stupefy :: translate RubyPants HTML entities # to their ASCII counterparts. # # In addition, you can customize the HTML entities that will be injected by # passing in a hash for the final argument. The defaults for these entities # are as follows: # # :single_left_quote :: # :double_left_quote :: # :single_right_quote :: # :double_right_quote :: # :em_dash :: # :en_dash :: # :ellipsis :: # :non_breaking_space ::   # :word_joiner :: # # If the :named_entities option is used, the default entities are # as follows: # # :single_left_quote :: # :double_left_quote :: # :single_right_quote :: # :double_right_quote :: # :em_dash :: # :en_dash :: # :ellipsis :: # :non_breaking_space ::   # :word_joiner :: # # If the :character_entities option is used, RubyPants will # emit Unicode characters directly, rather than HTML entities. By default # this excludes the space characters (non-breaking space and # word-joiner). To additionally emit Unicode space characters, use the # :character_spaces option. # def initialize(string, options=[2], entities = {}) super string @options = [*options] @entities = default_entities @entities.merge!(named_entities) if @options.include?(:named_entities) @entities.merge!(character_entities) if @options.include?(:character_entities) @entities.merge!(character_spaces) if @options.include?(:character_spaces) @entities.merge!(entities) @single_left_quote = @entities[:single_left_quote] @single_right_quote = @entities[:single_right_quote] @double_left_quote = @entities[:double_left_quote] @double_right_quote = @entities[:double_right_quote] @ellipsis = @entities[:ellipsis] @em_dash = @entities[:em_dash] @en_dash = @entities[:en_dash] end SPECIAL_HTML_TAGS = %r!\A<(/?)(pre|code|kbd|script|style|math)[\s>]! NON_WHITESPACE_CHARS = /\S/ # Apply SmartyPants transformations. def to_html do_quotes = do_backticks = do_dashes = do_ellipses = do_stupify = nil convert_quotes = prevent_breaks = nil if @options.include?(0) # Do nothing. return self elsif @options.include?(1) # Do everything, turn all options on. do_quotes = do_backticks = do_ellipses = true do_dashes = :normal elsif @options.include?(2) # Do everything, turn all options on, use old school dash shorthand. do_quotes = do_backticks = do_ellipses = true do_dashes = :oldschool elsif @options.include?(3) # Do everything, turn all options on, use inverted old school # dash shorthand. do_quotes = do_backticks = do_ellipses = true do_dashes = :inverted elsif @options.include?(-1) do_stupefy = true end # Explicit flags override numeric flag groups. do_quotes = true if @options.include?(:quotes) do_backticks = true if @options.include?(:backticks) do_backticks = :both if @options.include?(:allbackticks) do_dashes = :normal if @options.include?(:dashes) do_dashes = :oldschool if @options.include?(:oldschool) do_dashes = :inverted if @options.include?(:inverted) prevent_breaks = true if @options.include?(:prevent_breaks) do_ellipses = true if @options.include?(:ellipses) convert_quotes = true if @options.include?(:convertquotes) do_stupefy = true if @options.include?(:stupefy) # Parse the HTML tokens = tokenize # Keep track of when we're inside
 or  tags.
    in_pre = nil

    # Here is the result stored in.
    result = ""

    # This is a cheat, used to get some context for one-character
    # tokens that consist of just a quote char. What we do is remember
    # the last character of the previous text token, to use as context
    # to curl single- character quote tokens correctly.
    prev_token_last_char = nil

    tokens.each do |token|
      if token.first == :tag
        result << token[1]
        if token[1].end_with? '/>'
          # ignore self-closing tags
        elsif token[1] =~ SPECIAL_HTML_TAGS
          if $1 == '' && ! in_pre
            in_pre = $2
          elsif $1 == '/' && $2 == in_pre
            in_pre = nil
          end
        end
      else
        t = token[1]

        # Remember last char of this token before processing.
        last_char = t[-1].chr

        unless in_pre
          t = process_escapes t

          t.gsub!('"', '"') if convert_quotes

          if do_dashes
            t = educate_dashes t, prevent_breaks           if do_dashes == :normal
            t = educate_dashes_oldschool t, prevent_breaks if do_dashes == :oldschool
            t = educate_dashes_inverted t, prevent_breaks  if do_dashes == :inverted
          end

          t = educate_ellipses t, prevent_breaks if do_ellipses

          # Note: backticks need to be processed before quotes.
          if do_backticks
            t = educate_backticks t
            t = educate_single_backticks t if do_backticks == :both
          end

          if do_quotes
            if t == "'"
              # Special case: single-character ' token
              if prev_token_last_char =~ NON_WHITESPACE_CHARS
                t = @single_right_quote
              else
                t = @single_left_quote
              end
            elsif t == '"'
              # Special case: single-character " token
              if prev_token_last_char =~ NON_WHITESPACE_CHARS
                t = @double_right_quote
              else
                t = @double_left_quote
              end
            else
              # Normal case:
              t = educate_quotes t
            end
          end

          t = stupefy_entities t if do_stupefy
        end

        prev_token_last_char = last_char
        result << t
      end
    end

    # Done
    result
  end

  protected

  # Return the string, with after processing the following backslash
  # escape sequences. This is useful if you want to force a "dumb" quote
  # or other character to appear.
  #
  # Escaped are:
  #      \\    \"    \'    \.    \-    \`
  #
  def process_escapes(str)
    str.
      gsub('\\\\', '\').
      gsub('\"',   '"').
      gsub("\\\'", ''').
      gsub('\.',   '.').
      gsub('\-',   '-').
      gsub('\`',   '`')
  end

  def self.n_of(n, x)
    x = Regexp.escape(x)
    /(?[[:space:]]*)#{patt}/
    str.gsub(patt) do
      spaces = if prevent_breaks && $~['spaces'].length > 0
                 entity(:non_breaking_space) # * $~['spaces'].length
               elsif prevent_breaks
                 entity(:word_joiner)
               else
                 $~['spaces']
               end
      spaces + repl
    end
  end

  # Return the string, with each instance of "--" translated to an
  # em-dash HTML entity.
  #
  def educate_dashes(str, prevent_breaks=false)
    educate(str, DOUBLE_DASH, @em_dash, prevent_breaks)
  end

  # Return the string, with each instance of "--" translated to an
  # en-dash HTML entity, and each "---" translated to an
  # em-dash HTML entity.
  #
  def educate_dashes_oldschool(str, prevent_breaks=false)
    str = educate(str, TRIPLE_DASH, @em_dash, prevent_breaks)
    educate(str, DOUBLE_DASH, @en_dash, prevent_breaks)
  end

  # Return the string, with each instance of "--" translated
  # to an em-dash HTML entity, and each "---" translated to
  # an en-dash HTML entity. Two reasons why: First, unlike the en- and
  # em-dash syntax supported by +educate_dashes_oldschool+, it's
  # compatible with existing entries written before SmartyPants 1.1,
  # back when "--" was only used for em-dashes.  Second,
  # em-dashes are more common than en-dashes, and so it sort of makes
  # sense that the shortcut should be shorter to type. (Thanks to
  # Aaron Swartz for the idea.)
  #
  def educate_dashes_inverted(str, prevent_breaks=false)
    str = educate(str, TRIPLE_DASH, @en_dash, prevent_breaks)
    educate(str, DOUBLE_DASH, @em_dash, prevent_breaks)
  end

  SPACED_ELLIPSIS_PATTERN = /(?..." translated
  # to an ellipsis HTML entity. Also converts the case where there are
  # spaces between the dots.
  #
  def educate_ellipses(str, prevent_breaks=false)
    str = educate(str, TRIPLE_DOTS, @ellipsis, prevent_breaks)
    educate(str, SPACED_ELLIPSIS_PATTERN,
            @ellipsis, prevent_breaks)
  end

  # Return the string, with "``backticks''"-style single quotes
  # translated into HTML curly quote entities.
  #
  def educate_backticks(str)
    str.
      gsub("``", @double_left_quote).
      gsub("''", @double_right_quote)
  end

  # Return the string, with "`backticks'"-style single quotes
  # translated into HTML curly quote entities.
  #
  def educate_single_backticks(str)
    str.
      gsub("`", @single_left_quote).
      gsub("'", @single_right_quote)
  end

  PUNCT_CLASS = '[!"#\$\%\'()*+,\-.\/:;<=>?\@\[\\\\\]\^_`{|}~]'.freeze
  SNGL_QUOT_PUNCT_CASE = /^'(?=#{PUNCT_CLASS}\B)/
  DBLE_QUOT_PUNCT_CASE = /^"(?=#{PUNCT_CLASS}\B)/

  STARTS_MIXED_QUOTS_WITH_SNGL = /'"(?=\w)/
  STARTS_MIXED_QUOTS_WITH_DBLE = /"'(?=\w)/

  DECADE_ABBR_CASE = /'(?=\d\ds)/

  CLOSE_CLASS = '[^\ \t\r\n\\[\{\(\-]'.freeze
  CHAR_LEADS_SNGL_QUOTE = /(#{CLOSE_CLASS})'/
  WHITESPACE_TRAILS_SNGL_QUOTE = /'(\s|s\b|$)/
  CHAR_LEADS_DBLE_QUOTE = /(#{CLOSE_CLASS})"/
  WHITESPACE_TRAILS_DBLE_QUOTE = /"(\s|s\b|$)/

  # Return the string, with "educated" curly quote HTML entities.
  #
  def educate_quotes(str)
    str = str.dup

    # Special case if the very first character is a quote followed by
    # punctuation at a non-word-break. Close the quotes by brute
    # force:
    str.gsub!(SNGL_QUOT_PUNCT_CASE,
              @single_right_quote)
    str.gsub!(DBLE_QUOT_PUNCT_CASE,
              @double_right_quote)

    # Special case for double sets of quotes, e.g.:
    #   

He said, "'Quoted' words in a larger quote."

str.gsub!(STARTS_MIXED_QUOTS_WITH_DBLE, "#{@double_left_quote}#{@single_left_quote}") str.gsub!(STARTS_MIXED_QUOTS_WITH_SNGL, "#{@single_left_quote}#{@double_left_quote}") # Special case for decade abbreviations (the '80s): str.gsub!(DECADE_ABBR_CASE, @single_right_quote) dec_dashes = "#{@en_dash}|#{@em_dash}" quote_precedent = "[[:space:]]| |--|&[mn]dash;|#{dec_dashes}|ȁ[34];" # Get most opening single quotes: str.gsub!(/(#{quote_precedent})'(?=\w)/, '\1' + @single_left_quote) # Single closing quotes: str.gsub!(CHAR_LEADS_SNGL_QUOTE, '\1' + @single_right_quote) str.gsub!(WHITESPACE_TRAILS_SNGL_QUOTE, @single_right_quote + '\1') # Any remaining single quotes should be opening ones: str.gsub!("'", @single_left_quote) # Get most opening double quotes: str.gsub!(/(#{quote_precedent})"(?=\w)/, '\1' + @double_left_quote) # Double closing quotes: str.gsub!(CHAR_LEADS_DBLE_QUOTE, '\1' + @double_right_quote) str.gsub!(WHITESPACE_TRAILS_DBLE_QUOTE, @double_right_quote + '\1') # Any remaining quotes should be opening ones: str.gsub!('"', @double_left_quote) str end # Return the string, with each RubyPants HTML entity translated to # its ASCII counterpart. # # Note: This is not reversible (but exactly the same as in SmartyPants) # def stupefy_entities(str) new_str = str.dup { :en_dash => '-', :em_dash => '--', :single_left_quote => "'", :single_right_quote => "'", :double_left_quote => '"', :double_right_quote => '"', :ellipsis => '...' }.each do |k,v| new_str.gsub!(entity(k).to_s, v) end new_str end TAG_SOUP = /([^<]*)(|<[^>]*>)/m # Return an array of the tokens comprising the string. Each token is # either a tag (possibly with nested, tags contained therein, such # as , or a run of text between # tags. Each element of the array is a two-element array; the first # is either :tag or :text; the second is the actual value. # # Based on the _tokenize() subroutine from Brad Choate's # MTRegex plugin. # # This is actually the easier variant using tag_soup, as used by # Chad Miller in the Python port of SmartyPants. # def tokenize tokens = [] prev_end = 0 scan(TAG_SOUP) do tokens << [:text, $1] if $1 != "" tokens << [:tag, $2] prev_end = $~.end(0) end if prev_end < size tokens << [:text, self[prev_end..-1]] end tokens end def default_entities { :single_left_quote => "‘", :double_left_quote => "“", :single_right_quote => "’", :double_right_quote => "”", :em_dash => "—", :en_dash => "–", :ellipsis => "…", :non_breaking_space => " ", :word_joiner => "⁠", } end def named_entities { :single_left_quote => '‘', :double_left_quote => "“", :single_right_quote => "’", :double_right_quote => "”", :em_dash => "—", :en_dash => "–", :ellipsis => "…", :non_breaking_space => " ", # :word_joiner => N/A, } end def character_entities { :single_left_quote => "\u2018", :double_left_quote => "\u201C", :single_right_quote => "\u2019", :double_right_quote => "\u201D", :em_dash => "\u2014", :en_dash => "\u2013", :ellipsis => "\u2026", } end def character_spaces { :non_breaking_space => "\u00A0", :word_joiner => "\u2060", } end def entity(key) @entities[key] end end rubypants-0.7.1/lib/version.rb0000644000004100000410000000006013641342337016360 0ustar www-datawww-datamodule RubyPantsVersion VERSION = "0.7.1" end rubypants-0.7.1/rubypants.gemspec0000644000004100000410000000217713641342337017207 0ustar www-datawww-data# -*- encoding: utf-8 -*- lib = File.expand_path('../lib', __FILE__) $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'version' Gem::Specification.new do |s| s.name = 'rubypants' s.version = RubyPantsVersion::VERSION s.summary = "RubyPants is a Ruby port of the smart-quotes library SmartyPants." s.description = <<-EOF The original "SmartyPants" is a free web publishing plug-in for Movable Type, Blosxom, and BBEdit that easily translates plain ASCII punctuation characters into "smart" typographic punctuation HTML entities. EOF s.authors = [ "John Gruber", "Chad Miller", "Christian Neukirchen", "Jeremy McNevin", "Aron Griffis" ] s.email = 'jeremy@spokoino.net' s.files = `git ls-files`.split($/) s.test_files = s.files.grep(%r{^(test|spec|features)/}) s.require_paths = ["lib"] s.homepage = 'https://github.com/jmcnevin/rubypants' s.license = 'MIT' s.add_development_dependency('minitest') end rubypants-0.7.1/README.rdoc0000644000004100000410000000771613641342337015425 0ustar www-datawww-data= RubyPants: SmartyPants for Ruby {Gem}[https://rubygems.org/gems/rubypants] {Travis}[https://travis-ci.org/jmcnevin/rubypants] {CodeCov}[https://codecov.io/gh/jmcnevin/rubypants] == Synopsis RubyPants is a Ruby port of the smart-quotes library SmartyPants. The original "SmartyPants" is a free web publishing plug-in for Movable Type, Blosxom, and BBEdit that easily translates plain ASCII punctuation characters into "smart" typographic punctuation HTML entities. == Description RubyPants can perform the following transformations: * Straight quotes (" and ') into "curly" quote HTML entities * Backticks-style quotes (``like this'') into "curly" quote HTML entities * Dashes (-- and ---) into en- and em-dash entities * Three consecutive dots (... or . . .) into an ellipsis entity This means you can write, edit, and save your posts using plain old ASCII straight quotes, plain dashes, and plain dots, but your published posts (and final HTML output) will appear with smart quotes, em-dashes, and proper ellipses. RubyPants does not modify characters within
,
, , ,