Skip to content

Commit

Permalink
Improve the word_wrap utility method
Browse files Browse the repository at this point in the history
We use the word_wrap method right now to reformat warning messages so
that they fit within a 72-character space. We'd like to use this for
error messages too, but we want the word_wrap method to be smart when
reformatting bulleted or numbered lists, and also to ignore code blocks.
  • Loading branch information
mcmire committed Oct 9, 2015
1 parent 2f6c143 commit f67183e
Show file tree
Hide file tree
Showing 4 changed files with 378 additions and 10 deletions.
2 changes: 2 additions & 0 deletions lib/shoulda/matchers/util.rb
@@ -1,3 +1,5 @@
require 'shoulda/matchers/util/word_wrap'

module Shoulda
module Matchers
# @private
Expand Down
178 changes: 178 additions & 0 deletions lib/shoulda/matchers/util/word_wrap.rb
@@ -0,0 +1,178 @@
module Shoulda
module Matchers
# @private
def self.word_wrap(document)
Document.new(document).wrap
end

# @private
class Document
def initialize(document)
@document = document
end

def wrap
wrapped_paragraphs.map { |lines| lines.join("\n") }.join("\n\n")
end

protected

attr_reader :document

private

def paragraphs
document.split(/\n{2,}/)
end

def wrapped_paragraphs
paragraphs.map do |paragraph|
Paragraph.new(paragraph).wrap
end
end
end

# @private
class Text < ::String
LIST_ITEM_REGEXP = /\A((?:[a-z0-9]+(?:\)|\.)|\*) )/

def indented?
self =~ /\A[ ]+/
end

def list_item?
self =~ LIST_ITEM_REGEXP
end

def match_as_list_item
match(LIST_ITEM_REGEXP)
end
end

# @private
class Paragraph
def initialize(paragraph)
@paragraph = Text.new(paragraph)
end

def wrap
if paragraph.indented?
lines
elsif paragraph.list_item?
wrap_list_item
else
wrap_generic_paragraph
end
end

protected

attr_reader :paragraph

private

def wrap_list_item
wrap_lines(combine_list_item_lines(lines))
end

def lines
paragraph.split("\n").map { |line| Text.new(line) }
end

def combine_list_item_lines(lines)
lines.reduce([]) do |combined_lines, line|
if line.list_item?
combined_lines << line
else
combined_lines.last << (' ' + line).squeeze(' ')
end

combined_lines
end
end

def wrap_lines(lines)
lines.map { |line| Line.new(line).wrap }
end

def wrap_generic_paragraph
Line.new(combine_paragraph_into_one_line).wrap
end

def combine_paragraph_into_one_line
paragraph.gsub(/\n/, ' ')
end
end

# @private
class Line
TERMINAL_WIDTH = 72

def initialize(line)
@original_line = @line_to_wrap = Text.new(line)
@indentation = nil
end

def wrap
lines = []

if line_to_wrap.indented?
lines << line_to_wrap
else
loop do
new_line = (indentation || '') + line_to_wrap
result = wrap_line(new_line)
lines << result[:fitted_line].rstrip
@indentation ||= read_indentation
@line_to_wrap = result[:leftover]

if line_to_wrap.empty? || @original_line == @line_to_wrap
break
end
end
end

lines
end

protected

attr_reader :original_line, :line_to_wrap, :indentation

private

def read_indentation
match = line_to_wrap.match_as_list_item

if match
' ' * match[1].length
else
''
end
end

def wrap_line(line)
if line.length > TERMINAL_WIDTH
index = determine_where_to_break_line(line)
fitted_line = line[0 .. index].rstrip
leftover = line[index + 1 .. -1]
else
fitted_line = line
leftover = ''
end

{ fitted_line: fitted_line, leftover: leftover }
end

def determine_where_to_break_line(line)
index = TERMINAL_WIDTH - 1

while line[index] !~ /\s/
index -= 1
end

index
end
end
end
end
11 changes: 1 addition & 10 deletions lib/shoulda/matchers/warn.rb
Expand Up @@ -6,7 +6,7 @@ module Matchers
def self.warn(message)
header = "Warning from shoulda-matchers:"
divider = "*" * TERMINAL_MAX_WIDTH
wrapped_message = word_wrap(message, TERMINAL_MAX_WIDTH)
wrapped_message = word_wrap(message)
full_message = [
divider,
[header, wrapped_message.strip].join("\n\n"),
Expand All @@ -23,14 +23,5 @@ def self.warn_about_deprecated_method(old_method, new_method)
release. Please use #{new_method} instead.
EOT
end

# Source: <https://www.ruby-forum.com/topic/57805>
# @private
def self.word_wrap(text, width=80)
text.
gsub(/\n+/, " ").
gsub( /(\S{#{width}})(?=\S)/, '\1 ' ).
gsub( /(.{1,#{width}})(?:\s+|$)/, "\\1\n" )
end
end
end

0 comments on commit f67183e

Please sign in to comment.