Class: Wool::Scanner

Inherits:
Object
  • Object
show all
Defined in:
lib/wool/scanner.rb

Constant Summary collapse

DEFAULT_SETTINGS =
{:fix => false, :output => STDOUT, :indent_size => 2,
:__using__ => Wool::Warning.all_warnings,
:__fix__ => Wool::Warning.all_warnings}

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(settings = DEFAULT_SETTINGS) ⇒ Scanner

Initializes the scanner with the given settings

Parameters:

  • settings (Hash) (defaults to: DEFAULT_SETTINGS)

    the settings to use to customize this scanner’s scanning behavior



14
15
16
17
18
# File 'lib/wool/scanner.rb', line 14

def initialize(settings = DEFAULT_SETTINGS)
  @settings = DEFAULT_SETTINGS.merge(settings)
  @settings[:__scanner__] = self
  self.indent_stack = []
end

Instance Attribute Details

#indent_stackObject

Returns the value of attribute indent_stack.



4
5
6
# File 'lib/wool/scanner.rb', line 4

def indent_stack
  @indent_stack
end

#settingsObject

Returns the value of attribute settings.



3
4
5
# File 'lib/wool/scanner.rb', line 3

def settings
  @settings
end

Instance Method Details

#all_warnings_for_line(line, line_number, filename) ⇒ Object

Returns all warnings that match the line



88
89
90
91
92
# File 'lib/wool/scanner.rb', line 88

def all_warnings_for_line(line, line_number, filename)
  new_warnings = check_for_indent_warnings!(line, filename)
  new_warnings.concat scan_for_line_warnings(line, filename)
  new_warnings.each {|warning| warning.line_number = line_number}
end

#check_for_indent_warnings!(line, filename) ⇒ Object

Checks for new warnings based on indentation.



100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# File 'lib/wool/scanner.rb', line 100

def check_for_indent_warnings!(line, filename)
  return [] if line == ""
  indent_size = get_indent_size line
  if indent_size > current_indent
    self.indent_stack.push indent_size
  elsif indent_size < current_indent
    previous = self.indent_stack.pop
    if indent_size != current_indent &&
       using.include?(MisalignedUnindentationWarning)
      warnings_to_check = [MisalignedUnindentationWarning.new(filename, line, current_indent)]
      return filtered_warnings_from_line(line, warnings_to_check)
    end
  end
  []
end

#current_indentObject

Gets the current indent size



117
118
119
# File 'lib/wool/scanner.rb', line 117

def current_indent
  self.indent_stack.last || 0
end

#filter_fixable(warnings) ⇒ Object

Returns only the warnings that we should fix



95
96
97
# File 'lib/wool/scanner.rb', line 95

def filter_fixable(warnings)
  warnings.select {|warning| warning.fixable? && fixing?(warning) }
end

#fixObject

Returns the list of warnings to use for scanning.



31
32
33
# File 'lib/wool/scanner.rb', line 31

def fix
  @settings[:__fix__]
end

#fix_input(warnings, line, line_number, filename) ⇒ Object

Tries to fix the given line with a set of matching warnings for that line. May recurse if there are multiple warnings on the same line.



75
76
77
78
79
80
81
82
83
84
85
# File 'lib/wool/scanner.rb', line 75

def fix_input(warnings, line, line_number, filename)
  fixable_warnings = filter_fixable warnings
  if fixable_warnings.size == 1
    self.settings[:output_lines] << fixable_warnings.first.fix rescue line
  elsif fixable_warnings.size > 1
    new_text = fixable_warnings.first.fix rescue line
    process_line(new_text, line_number, filename)
  else
    self.settings[:output_lines] << line
  end
end

#fixing?(warning) ⇒ Boolean

Should we use this warning?

Returns:

  • (Boolean)


36
37
38
# File 'lib/wool/scanner.rb', line 36

def fixing?(warning)
  @settings[:__fix__].include? warning.class
end

#get_indent_size(line) ⇒ Object

Gets the indent size of a given line



122
123
124
# File 'lib/wool/scanner.rb', line 122

def get_indent_size(line)
  line.match(/^\s*/)[0].size
end

#process_line(line, line_number, filename) ⇒ Object

Finds all matching warnings, and if the user wishes, fix a subset of them.



67
68
69
70
71
# File 'lib/wool/scanner.rb', line 67

def process_line(line, line_number, filename)
  warnings = all_warnings_for_line(line, line_number, filename)
  fix_input(warnings, line, line_number, filename) if @settings[:fix]
  warnings
end

#scan(text, filename = '(none)') ⇒ Array[Wool::Warnings]

Scans the text for warnings.

Parameters:

  • text (String)

    the input ruby file to scan

Returns:

  • (Array[Wool::Warnings])

    the warnings generated by the code. If the code is clean, an empty array is returned.



45
46
47
48
49
50
51
52
53
54
55
56
# File 'lib/wool/scanner.rb', line 45

def scan(text, filename='(none)')
  warnings = scan_for_file_warnings(text, filename)
  text = filter_fixable(warnings).inject(text) do |text, warning|
    warning.fix(text)
  end
  with_fixing_piped_to_output do
    text.split(/\n/).each_with_index do |line, number|
      warnings.concat process_line(line, number + 1, filename)
    end
  end
  warnings
end

#scan_for_file_warnings(file, filename) ⇒ Object

Goes through all file warning subclasses and see what warnings the file generates as a whole.



128
129
130
# File 'lib/wool/scanner.rb', line 128

def scan_for_file_warnings(file, filename)
  scan_for_warnings(using & FileWarning.all_warnings, file, filename)
end

#scan_for_line_warnings(line, filename) ⇒ Object

Goes through all line warning subclasses and checks if we got some new warnings for a given line



134
135
136
137
# File 'lib/wool/scanner.rb', line 134

def scan_for_line_warnings(line, filename)
  warnings = scan_for_warnings(using & LineWarning.all_warnings, line, filename)
  filtered_warnings_from_line(line, warnings)
end

#usingObject

Returns the list of warnings to use for scanning.



21
22
23
# File 'lib/wool/scanner.rb', line 21

def using
  @settings[:__using__]
end

#using?(warning) ⇒ Boolean

Should we use this warning?

Returns:

  • (Boolean)


26
27
28
# File 'lib/wool/scanner.rb', line 26

def using?(warning)
  @settings[:__using__].include? warning
end

#with_fixing_piped_to_outputObject



58
59
60
61
62
63
64
# File 'lib/wool/scanner.rb', line 58

def with_fixing_piped_to_output
  self.settings[:output_lines] = []
  yield
  if @settings[:fix]
    self.settings[:output_file].write self.settings[:output_lines].join("\n")
  end
end