Skip to content

Word & Character Counter — Ruby source

Live word, character, sentence, and paragraph counts plus reading-time estimate as you type.

This is the Ruby implementation — the same logic the interactive tool runs, in a shareable, citable form.

# word-counter — Ruby port: word / character / sentence / paragraph counts + 200-wpm reading-time estimate.
#
# Display snippet: ports the counters from the TypeScript lib (src/lib/wordCount.ts).
# String#length counts characters; the TS reference counts UTF-16 code units —
# the two agree for all BMP text. /\s/ mirrors the JS whitespace class.

module WordCounter
  module_function

  # Words: String#split with no argument splits on Unicode whitespace runs and
  # drops empty leading fields, so empty or whitespace-only input -> 0.
  def count_words(text)
    text.split.length
  end

  # Total character count, whitespace included.
  def count_chars(text)
    text.length
  end

  # Character count with all whitespace removed.
  def count_chars_no_spaces(text)
    text.gsub(/\s/, '').length
  end

  # Sentences: maximal runs of non-terminators each closed by one or more of
  # . ! ? — the TS regex /[^.!?]+[.!?]+/g.
  def count_sentences(text)
    text.scan(/[^.!?]+[.!?]+/).length
  end

  # Paragraphs separated by one or more blank lines; blank segments don't count.
  def count_paragraphs(text)
    text.split(/\n{2,}/).count { |segment| !segment.strip.empty? }
  end

  # Reading time at 200 wpm: 0 for empty input, otherwise >= 1 minute.
  # (words + 100) / 200 is integer round-half-up — it matches Math.round
  # exactly and avoids Float#round's banker's rounding on exact halves.
  def reading_time_min(words)
    return 0 if words <= 0

    [(words + 100) / 200, 1].max
  end
end

if $PROGRAM_NAME == __FILE__
  text = "CosmoDev counts words. Live totals update as you type!\n\n" \
         "Second paragraph? Two sentences - each with words. Zero logins needed."
  puts "words:       #{WordCounter.count_words(text)}"                       # 20
  puts "chars:       #{WordCounter.count_chars(text)}"                       # 126
  puts "no spaces:   #{WordCounter.count_chars_no_spaces(text)}"             # 106
  puts "sentences:   #{WordCounter.count_sentences(text)}"                   # 5
  puts "paragraphs:  #{WordCounter.count_paragraphs(text)}"                  # 2
  puts "reading min: #{WordCounter.reading_time_min(WordCounter.count_words(text))}" # 1
end

Also available in 13 other languages

Every CosmoDev tool ships its pure logic in TypeScript (web) and Go (CLI), with authored implementations in a dozen-plus languages — the same contract, ported. Compare all languages side by side →