haml_lint 0.13.0 → 0.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/config/default.yml +1 -0
  3. data/lib/haml_lint/cli.rb +25 -8
  4. data/lib/haml_lint/configuration.rb +56 -34
  5. data/lib/haml_lint/configuration_loader.rb +22 -6
  6. data/lib/haml_lint/constants.rb +1 -1
  7. data/lib/haml_lint/document.rb +102 -0
  8. data/lib/haml_lint/file_finder.rb +14 -5
  9. data/lib/haml_lint/lint.rb +17 -1
  10. data/lib/haml_lint/linter/alt_text.rb +1 -1
  11. data/lib/haml_lint/linter/class_attribute_with_static_value.rb +2 -2
  12. data/lib/haml_lint/linter/classes_before_ids.rb +2 -2
  13. data/lib/haml_lint/linter/consecutive_comments.rb +3 -5
  14. data/lib/haml_lint/linter/consecutive_silent_scripts.rb +5 -5
  15. data/lib/haml_lint/linter/empty_script.rb +1 -1
  16. data/lib/haml_lint/linter/html_attributes.rb +2 -2
  17. data/lib/haml_lint/linter/implicit_div.rb +3 -3
  18. data/lib/haml_lint/linter/leading_comment_space.rb +1 -1
  19. data/lib/haml_lint/linter/line_length.rb +2 -2
  20. data/lib/haml_lint/linter/multiline_pipe.rb +3 -3
  21. data/lib/haml_lint/linter/multiline_script.rb +3 -3
  22. data/lib/haml_lint/linter/object_reference_attributes.rb +1 -1
  23. data/lib/haml_lint/linter/rubocop.rb +60 -34
  24. data/lib/haml_lint/linter/ruby_comments.rb +1 -1
  25. data/lib/haml_lint/linter/space_before_script.rb +4 -4
  26. data/lib/haml_lint/linter/space_inside_hash_attributes.rb +2 -2
  27. data/lib/haml_lint/linter/tag_name.rb +1 -1
  28. data/lib/haml_lint/linter/trailing_whitespace.rb +2 -2
  29. data/lib/haml_lint/linter/unnecessary_interpolation.rb +3 -3
  30. data/lib/haml_lint/linter/unnecessary_string_output.rb +10 -3
  31. data/lib/haml_lint/linter.rb +30 -10
  32. data/lib/haml_lint/linter_registry.rb +13 -2
  33. data/lib/haml_lint/linter_selector.rb +77 -0
  34. data/lib/haml_lint/node_transformer.rb +5 -5
  35. data/lib/haml_lint/options.rb +13 -6
  36. data/lib/haml_lint/rake_task.rb +18 -3
  37. data/lib/haml_lint/report.rb +7 -0
  38. data/lib/haml_lint/reporter/default_reporter.rb +2 -2
  39. data/lib/haml_lint/reporter/json_reporter.rb +15 -10
  40. data/lib/haml_lint/reporter.rb +17 -11
  41. data/lib/haml_lint/{script_extractor.rb → ruby_extractor.rb} +26 -17
  42. data/lib/haml_lint/runner.rb +44 -44
  43. data/lib/haml_lint/tree/node.rb +7 -8
  44. data/lib/haml_lint/utils.rb +88 -21
  45. data/lib/haml_lint/version.rb +1 -1
  46. data/lib/haml_lint.rb +2 -1
  47. metadata +5 -4
  48. data/lib/haml_lint/parser.rb +0 -87
@@ -24,7 +24,7 @@ module HamlLint
24
24
  # Plain text nodes are allowed to consist of a single pipe
25
25
  return if line.strip == '|'
26
26
 
27
- add_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
27
+ record_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
28
28
  end
29
29
 
30
30
  private
@@ -32,12 +32,12 @@ module HamlLint
32
32
  MULTILINE_PIPE_REGEX = /\s+\|\s*$/
33
33
 
34
34
  def line_text_for_node(node)
35
- parser.lines[node.line - 1]
35
+ document.source_lines[node.line - 1]
36
36
  end
37
37
 
38
38
  def check(node)
39
39
  line = line_text_for_node(node)
40
- add_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
40
+ record_lint(node, MESSAGE) if line.match(MULTILINE_PIPE_REGEX)
41
41
  end
42
42
  end
43
43
  end
@@ -34,9 +34,9 @@ module HamlLint
34
34
  def check(node)
35
35
  operator = node.script[/\s+(\S+)\z/, 1]
36
36
  if SPLIT_OPERATORS.include?(operator)
37
- add_lint(node,
38
- "Script with trailing operator `#{operator}` should be " \
39
- 'merged with the script on the following line')
37
+ record_lint(node,
38
+ "Script with trailing operator `#{operator}` should be " \
39
+ 'merged with the script on the following line')
40
40
  end
41
41
  end
42
42
  end
@@ -7,7 +7,7 @@ module HamlLint
7
7
  def visit_tag(node)
8
8
  return unless node.object_reference?
9
9
 
10
- add_lint(node, 'Avoid using object reference syntax to assign class/id ' \
10
+ record_lint(node, 'Avoid using object reference syntax to assign class/id ' \
11
11
  'attributes for tags')
12
12
  end
13
13
  end
@@ -1,4 +1,4 @@
1
- require 'haml_lint/script_extractor'
1
+ require 'haml_lint/ruby_extractor'
2
2
  require 'rubocop'
3
3
  require 'tempfile'
4
4
 
@@ -7,33 +7,35 @@ module HamlLint
7
7
  class Linter::RuboCop < Linter
8
8
  include LinterRegistry
9
9
 
10
- def initialize(config)
11
- super
12
- @rubocop = ::RuboCop::CLI.new
13
- @ignored_cops = Array(config['ignored_cops']).flatten
14
- end
10
+ def visit_root(_node)
11
+ extractor = HamlLint::RubyExtractor.new
12
+ extracted_source = extractor.extract(document)
15
13
 
16
- def run(parser)
17
- @parser = parser
18
- @extractor = ScriptExtractor.new(parser)
19
- extracted_code = @extractor.extract.strip
14
+ return if extracted_source.source.empty?
20
15
 
21
- # Ensure a final newline in the code we feed to RuboCop
22
- find_lints(extracted_code + "\n") unless extracted_code.empty?
16
+ find_lints(extracted_source.source, extracted_source.source_map)
23
17
  end
24
18
 
25
19
  private
26
20
 
27
- def find_lints(code)
28
- original_filename = @parser.filename || 'ruby_script'
21
+ # Executes RuboCop against the given Ruby code and records the offenses as
22
+ # lints.
23
+ #
24
+ # @param ruby [String] Ruby code
25
+ # @param source_map [Hash] map of Ruby code line numbers to original line
26
+ # numbers in the template
27
+ def find_lints(ruby, source_map)
28
+ rubocop = ::RuboCop::CLI.new
29
+
30
+ original_filename = document.file || 'ruby_script'
29
31
  filename = "#{File.basename(original_filename)}.haml_lint.tmp"
30
32
  directory = File.dirname(original_filename)
31
33
 
32
34
  Tempfile.open(filename, directory) do |f|
33
35
  begin
34
- f.write(code)
36
+ f.write(ruby)
35
37
  f.close
36
- extract_lints_from_offences(lint_file(f.path))
38
+ extract_lints_from_offenses(lint_file(rubocop, f.path), source_map)
37
39
  ensure
38
40
  f.unlink
39
41
  end
@@ -41,36 +43,60 @@ module HamlLint
41
43
  end
42
44
 
43
45
  # Defined so we can stub the results in tests
44
- def lint_file(file)
45
- @rubocop.run(%w[--format HamlLint::OffenceCollector] << file)
46
- OffenceCollector.offences
46
+ #
47
+ # @param rubocop [RuboCop::CLI]
48
+ # @param file [String]
49
+ # @return [Array<RuboCop::Cop::Offense>]
50
+ def lint_file(rubocop, file)
51
+ rubocop.run(rubocop_flags << file)
52
+ OffenseCollector.offenses
47
53
  end
48
54
 
49
- def extract_lints_from_offences(offences)
50
- offences.select { |offence| !@ignored_cops.include?(offence.cop_name) }
51
- .each do |offence|
52
- @lints << Lint.new(self,
53
- @parser.filename,
54
- @extractor.source_map[offence.line],
55
- "#{offence.cop_name}: #{offence.message}")
55
+ # Aggregates RuboCop offenses and converts them to {HamlLint::Lint}s
56
+ # suitable for reporting.
57
+ #
58
+ # @param offenses [Array<RuboCop::Cop::Offense>]
59
+ # @param source_map [Hash]
60
+ def extract_lints_from_offenses(offenses, source_map)
61
+ dummy_node = Struct.new(:line)
62
+
63
+ offenses.select { |offense| !config['ignored_cops'].include?(offense.cop_name) }
64
+ .each do |offense|
65
+ record_lint(dummy_node.new(source_map[offense.line]), offense.message)
56
66
  end
57
67
  end
58
- end
59
68
 
60
- # Collects offences detected by RuboCop.
61
- class OffenceCollector < ::RuboCop::Formatter::BaseFormatter
62
- attr_accessor :offences
69
+ # Returns flags that will be passed to RuboCop CLI.
70
+ #
71
+ # @return [Array<String>]
72
+ def rubocop_flags
73
+ flags = %w[--format HamlLint::OffenseCollector]
74
+ flags += ['--config', ENV['HAML_LINT_RUBOCOP_CONF']] if ENV['HAML_LINT_RUBOCOP_CONF']
75
+ flags
76
+ end
77
+ end
63
78
 
79
+ # Collects offenses detected by RuboCop.
80
+ class OffenseCollector < ::RuboCop::Formatter::BaseFormatter
64
81
  class << self
65
- attr_accessor :offences
82
+ # List of offenses reported by RuboCop.
83
+ attr_accessor :offenses
66
84
  end
67
85
 
86
+ # Executed when RuboCop begins linting.
87
+ #
88
+ # @param _target_files [Array<String>]
68
89
  def started(_target_files)
69
- self.class.offences = []
90
+ self.class.offenses = []
70
91
  end
71
92
 
72
- def file_finished(_file, offences)
73
- self.class.offences += offences
93
+ # Executed when a file has been scanned by RuboCop, adding the reported
94
+ # offenses to our collection.
95
+ #
96
+ # @param _file [String]
97
+ # @param offenses [Array<RuboCop::Cop::Offense>]
98
+ def file_finished(_file, offenses)
99
+ self.class.offenses += offenses
74
100
  end
75
101
  end
76
102
  end
@@ -5,7 +5,7 @@ module HamlLint
5
5
 
6
6
  def visit_silent_script(node)
7
7
  if code_comment?(node)
8
- add_lint(node, 'Use `-#` for comments instead of `- #`')
8
+ record_lint(node, 'Use `-#` for comments instead of `- #`')
9
9
  end
10
10
  end
11
11
 
@@ -28,18 +28,18 @@ module HamlLint
28
28
  # (need to do it this way as the parser strips whitespace from node)
29
29
  return unless tag_with_text[index - 1] != ' '
30
30
 
31
- add_lint(node, MESSAGE_FORMAT % '=')
31
+ record_lint(node, MESSAGE_FORMAT % '=')
32
32
  end
33
33
 
34
34
  def visit_script(node)
35
35
  # Plain text nodes with interpolation are converted to script nodes, so we
36
36
  # need to ignore them here.
37
- return unless parser.lines[node.line - 1].lstrip.start_with?('=')
38
- add_lint(node, MESSAGE_FORMAT % '=') if missing_space?(node)
37
+ return unless document.source_lines[node.line - 1].lstrip.start_with?('=')
38
+ record_lint(node, MESSAGE_FORMAT % '=') if missing_space?(node)
39
39
  end
40
40
 
41
41
  def visit_silent_script(node)
42
- add_lint(node, MESSAGE_FORMAT % '-') if missing_space?(node)
42
+ record_lint(node, MESSAGE_FORMAT % '-') if missing_space?(node)
43
43
  end
44
44
 
45
45
  private
@@ -25,8 +25,8 @@ module HamlLint
25
25
  style = STYLE[config['style'] == 'no_space' ? 'no_space' : 'space']
26
26
  source = node.hash_attributes_source
27
27
 
28
- add_lint(node, style[:start_message]) unless source =~ style[:start_regex]
29
- add_lint(node, style[:end_message]) unless source =~ style[:end_regex]
28
+ record_lint(node, style[:start_message]) unless source =~ style[:start_regex]
29
+ record_lint(node, style[:end_message]) unless source =~ style[:end_regex]
30
30
  end
31
31
  end
32
32
  end
@@ -7,7 +7,7 @@ module HamlLint
7
7
  tag = node.tag_name
8
8
  return unless tag.match(/[A-Z]/)
9
9
 
10
- add_lint(node, "`#{tag}` should be written in lowercase as `#{tag.downcase}`")
10
+ record_lint(node, "`#{tag}` should be written in lowercase as `#{tag.downcase}`")
11
11
  end
12
12
  end
13
13
  end
@@ -6,10 +6,10 @@ module HamlLint
6
6
  def visit_root(_node)
7
7
  dummy_node = Struct.new(:line)
8
8
 
9
- parser.lines.each_with_index do |line, index|
9
+ document.source_lines.each_with_index do |line, index|
10
10
  next unless line =~ /\s+$/
11
11
 
12
- add_lint dummy_node.new(index + 1), 'Line contains trailing whitespace'
12
+ record_lint dummy_node.new(index + 1), 'Line contains trailing whitespace'
13
13
  end
14
14
  end
15
15
  end
@@ -14,15 +14,15 @@ module HamlLint
14
14
 
15
15
  count = 0
16
16
  chars = 2 # Include surrounding quote chars
17
- HamlLint::Utils.extract_interpolated_values(node.script) do |interpolated_code|
17
+ HamlLint::Utils.extract_interpolated_values(node.script) do |interpolated_code, _line|
18
18
  count += 1
19
19
  return if count > 1 # rubocop:disable Lint/NonLocalExitFromIterator
20
20
  chars += interpolated_code.length + 3
21
21
  end
22
22
 
23
23
  if chars == node.script.length
24
- add_lint(node, '`%... \#{expression}` can be written without ' \
25
- 'interpolation as `%...= expression`')
24
+ record_lint(node, '`%... \#{expression}` can be written without ' \
25
+ 'interpolation as `%...= expression`')
26
26
  end
27
27
  end
28
28
  end
@@ -13,7 +13,7 @@ module HamlLint
13
13
 
14
14
  def visit_tag(node)
15
15
  if tag_has_inline_script?(node) && inline_content_is_string?(node)
16
- add_lint(node, MESSAGE)
16
+ record_lint(node, MESSAGE)
17
17
  end
18
18
  end
19
19
 
@@ -23,7 +23,7 @@ module HamlLint
23
23
  return if node.source_code !~ /\s*=/
24
24
 
25
25
  if outputs_string_literal?(node)
26
- add_lint(node, MESSAGE)
26
+ record_lint(node, MESSAGE)
27
27
  end
28
28
  end
29
29
 
@@ -31,9 +31,16 @@ module HamlLint
31
31
 
32
32
  def outputs_string_literal?(script_node)
33
33
  tree = parse_ruby(script_node.script)
34
- [:str, :dstr].include?(tree.type)
34
+ [:str, :dstr].include?(tree.type) &&
35
+ starts_with_reserved_character?(tree.children.first)
35
36
  rescue ::Parser::SyntaxError # rubocop:disable Lint/HandleExceptions
36
37
  # Gracefully ignore syntax errors, as that's managed by a different linter
37
38
  end
39
+
40
+ # Returns whether a string starts with a character that would otherwise be
41
+ # given special treatment, thus making enclosing it in a string necessary.
42
+ def starts_with_reserved_character?(string)
43
+ string !~ %r{\A\s*[/#-=%~]}
44
+ end
38
45
  end
39
46
  end
@@ -1,33 +1,52 @@
1
1
  module HamlLint
2
2
  # Base implementation for all lint checks.
3
+ #
4
+ # @abstract
3
5
  class Linter
4
6
  include HamlVisitor
5
7
 
6
- attr_reader :parser, :lints
8
+ # List of lints reported by this linter.
9
+ #
10
+ # @todo Remove once spec/support/shared_linter_context returns an array of
11
+ # lints for the subject instead of the linter itself.
12
+ attr_reader :lints
7
13
 
14
+ # Initializes a linter with the specified configuration.
15
+ #
8
16
  # @param config [Hash] configuration for this linter
9
17
  def initialize(config)
10
18
  @config = config
11
19
  @lints = []
12
- @ruby_parser = nil
13
20
  end
14
21
 
15
- def run(parser)
16
- @parser = parser
17
- visit(parser.tree)
22
+ # Runs the linter against the given Haml document.
23
+ #
24
+ # @param document [HamlLint::Document]
25
+ def run(document)
26
+ @document = document
27
+ @lints = []
28
+ visit(document.tree)
29
+ @lints
18
30
  end
19
31
 
20
32
  # Returns the simple name for this linter.
33
+ #
34
+ # @return [String]
21
35
  def name
22
36
  self.class.name.split('::').last
23
37
  end
24
38
 
25
39
  private
26
40
 
27
- attr_reader :config
41
+ attr_reader :config, :document
28
42
 
29
- def add_lint(node, message)
30
- @lints << Lint.new(self, parser.filename, node.line, message)
43
+ # Record a lint for reporting back to the user.
44
+ #
45
+ # @param node [#line] node to extract the line number from
46
+ # @param message [String] error/warning to display to the user
47
+ def record_lint(node, message)
48
+ @lints << HamlLint::Lint.new(self, @document.file, node.line, message,
49
+ config.fetch('severity', :warning).to_sym)
31
50
  end
32
51
 
33
52
  # Parse Ruby code into an abstract syntax tree.
@@ -136,7 +155,7 @@ module HamlLint
136
155
  def following_node_line(node)
137
156
  [
138
157
  [node.children.first, next_node(node)].compact.map(&:line),
139
- parser.lines.count + 1,
158
+ @document.source_lines.count + 1,
140
159
  ].flatten.min
141
160
  end
142
161
 
@@ -148,7 +167,8 @@ module HamlLint
148
167
  def tag_with_inline_text(tag_node)
149
168
  # Normalize each of the lines to ignore the multiline bar (|) and
150
169
  # excess whitespace
151
- parser.lines[(tag_node.line - 1)...(following_node_line(tag_node) - 1)].map do |line|
170
+ @document.source_lines[(tag_node.line - 1)...(following_node_line(tag_node) - 1)]
171
+ .map do |line|
152
172
  line.strip.gsub(/\|\z/, '').rstrip
153
173
  end.join(' ')
154
174
  end
@@ -6,12 +6,23 @@ module HamlLint
6
6
  @linters = []
7
7
 
8
8
  class << self
9
+ # List of all registered linters.
9
10
  attr_reader :linters
10
11
 
11
- def included(base)
12
- @linters << base
12
+ # Executed when a linter includes the {LinterRegistry} module.
13
+ #
14
+ # This results in the linter being registered with the registry.
15
+ #
16
+ # @param subclass [Class]
17
+ def included(subclass)
18
+ @linters << subclass
13
19
  end
14
20
 
21
+ # Return a list of {HamlLint::Linter} {Class}es corresponding to the
22
+ # specified list of names.
23
+ #
24
+ # @param linter_names [Array<String>]
25
+ # @return [Array<Class>]
15
26
  def extract_linters_from(linter_names)
16
27
  linter_names.map do |linter_name|
17
28
  begin
@@ -0,0 +1,77 @@
1
+ module HamlLint
2
+ # Chooses the appropriate linters to run given the specified configuration.
3
+ class LinterSelector
4
+ # Creates a selector using the given configuration and additional options.
5
+ #
6
+ # @param config [HamlLint::Configuration]
7
+ # @param options [Hash]
8
+ def initialize(config, options)
9
+ @config = config
10
+ @options = options
11
+ end
12
+
13
+ # Returns the set of linters to run against the given file.
14
+ #
15
+ # @param file [String]
16
+ # @raise [HamlLint::Exceptions::NoLintersError] when no linters are enabled
17
+ # @return [Array<HamlLint::Linter>]
18
+ def linters_for_file(file)
19
+ @linters ||= extract_enabled_linters(@config, @options)
20
+ @linters.select { |linter| run_linter_on_file?(@config, linter, file) }
21
+ end
22
+
23
+ private
24
+
25
+ # Returns a list of linters that are enabled given the specified
26
+ # configuration and additional options.
27
+ #
28
+ # @param config [HamlLint::Configuration]
29
+ # @param options [Hash]
30
+ # @return [Array<HamlLint::Linter>]
31
+ def extract_enabled_linters(config, options)
32
+ included_linters = LinterRegistry
33
+ .extract_linters_from(options.fetch(:included_linters, []))
34
+
35
+ included_linters = LinterRegistry.linters if included_linters.empty?
36
+
37
+ excluded_linters = LinterRegistry
38
+ .extract_linters_from(options.fetch(:excluded_linters, []))
39
+
40
+ # After filtering out explicitly included/excluded linters, only include
41
+ # linters which are enabled in the configuration
42
+ linters = (included_linters - excluded_linters).map do |linter_class|
43
+ linter_config = config.for_linter(linter_class)
44
+ linter_class.new(linter_config) if linter_config['enabled']
45
+ end.compact
46
+
47
+ # Highlight condition where all linters were filtered out, as this was
48
+ # likely a mistake on the user's part
49
+ if linters.empty?
50
+ raise HamlLint::Exceptions::NoLintersError, 'No linters specified'
51
+ end
52
+
53
+ linters
54
+ end
55
+
56
+ # Whether to run the given linter against the specified file.
57
+ #
58
+ # @param config [HamlLint::Configuration]
59
+ # @param linter [HamlLint::Linter]
60
+ # @param file [String]
61
+ # @return [Boolean]
62
+ def run_linter_on_file?(config, linter, file)
63
+ linter_config = config.for_linter(linter)
64
+
65
+ if linter_config['include'].any? &&
66
+ !HamlLint::Utils.any_glob_matches?(linter_config['include'], file)
67
+ return false
68
+ end
69
+
70
+ if HamlLint::Utils.any_glob_matches?(linter_config['exclude'], file)
71
+ return false
72
+ end
73
+
74
+ true
75
+ end
76
+ end
77
+ end
@@ -7,11 +7,11 @@ module HamlLint
7
7
  # would expect. This class is intended to isolate and handle these cases so
8
8
  # that linters don't have to deal with them.
9
9
  class NodeTransformer
10
- # Creates a node transformer for the given parser context.
10
+ # Creates a node transformer for the given Haml document.
11
11
  #
12
- # @param parser [HamlLint::Parser]
13
- def initialize(parser)
14
- @parser = parser
12
+ # @param document [HamlLint::Document]
13
+ def initialize(document)
14
+ @document = document
15
15
  end
16
16
 
17
17
  # Transforms the given {Haml::Parser::ParseNode} into its corresponding
@@ -19,7 +19,7 @@ module HamlLint
19
19
  def transform(haml_node)
20
20
  node_class = "#{HamlLint::Utils.camel_case(haml_node.type.to_s)}Node"
21
21
 
22
- HamlLint::Tree.const_get(node_class).new(@parser, haml_node)
22
+ HamlLint::Tree.const_get(node_class).new(@document, haml_node)
23
23
  rescue NameError
24
24
  # TODO: Wrap in parser error?
25
25
  raise
@@ -31,11 +31,6 @@ module HamlLint
31
31
  private
32
32
 
33
33
  def add_linter_options(parser)
34
- parser.on('-e', '--exclude file,...', Array,
35
- 'List of file names to exclude') do |files|
36
- @options[:excluded_files] = files
37
- end
38
-
39
34
  parser.on('-i', '--include-linter linter,...', Array,
40
35
  'Specify which linters you want to run') do |linters|
41
36
  @options[:included_linters] = linters
@@ -48,10 +43,22 @@ module HamlLint
48
43
 
49
44
  parser.on('-r', '--reporter reporter', String,
50
45
  'Specify which reporter you want to use to generate the output') do |reporter|
51
- @options[:reporter] = HamlLint::Reporter.const_get("#{reporter.capitalize}Reporter")
46
+ @options[:reporter] = load_reporter_class(reporter.capitalize)
52
47
  end
53
48
  end
54
49
 
50
+ # Returns the class of the specified Reporter.
51
+ #
52
+ # @param reporter_name [String]
53
+ # @raise [HamlLint::Exceptions::InvalidCLIOption] if reporter doesn't exist
54
+ # @return [Class]
55
+ def load_reporter_class(reporter_name)
56
+ HamlLint::Reporter.const_get("#{reporter_name}Reporter")
57
+ rescue NameError
58
+ raise HamlLint::Exceptions::InvalidCLIOption,
59
+ "#{reporter_name}Reporter does not exist"
60
+ end
61
+
55
62
  def add_file_options(parser)
56
63
  parser.on('-c', '--config config-file', String,
57
64
  'Specify which configuration file you want to use') do |conf_file|
@@ -1,5 +1,6 @@
1
1
  require 'rake'
2
2
  require 'rake/tasklib'
3
+ require 'haml_lint/constants'
3
4
 
4
5
  module HamlLint
5
6
  # Rake task interface for haml-lint command line interface.
@@ -51,6 +52,8 @@ module HamlLint
51
52
  attr_accessor :quiet
52
53
 
53
54
  # Create the task so it exists in the current namespace.
55
+ #
56
+ # @param name [Symbol] task name
54
57
  def initialize(name = :haml_lint)
55
58
  @name = name
56
59
  @files = ['.'] # Search for everything under current directory by default
@@ -63,27 +66,34 @@ module HamlLint
63
66
 
64
67
  private
65
68
 
69
+ # Defines the Rake task.
66
70
  def define
67
71
  desc default_description unless ::Rake.application.last_description
68
72
 
69
73
  task(name, [:files]) do |_task, task_args|
70
74
  # Lazy-load so task doesn't affect Rakefile load time
71
- require 'haml_lint'
72
75
  require 'haml_lint/cli'
73
76
 
74
77
  run_cli(task_args)
75
78
  end
76
79
  end
77
80
 
81
+ # Executes the CLI given the specified task arguments.
82
+ #
83
+ # @param task_args [Rake::TaskArguments]
78
84
  def run_cli(task_args)
79
85
  cli_args = ['--config', config] if config
80
86
 
81
87
  logger = quiet ? HamlLint::Logger.silent : HamlLint::Logger.new(STDOUT)
82
88
  result = HamlLint::CLI.new(logger).run(Array(cli_args) + files_to_lint(task_args))
83
89
 
84
- fail "haml-lint failed with exit code #{result}" unless result == 0
90
+ fail "#{HamlLint::APP_NAME} failed with exit code #{result}" unless result == 0
85
91
  end
86
92
 
93
+ # Returns the list of files that should be linted given the specified task
94
+ # arguments.
95
+ #
96
+ # @param task_args [Rake::TaskArguments]
87
97
  def files_to_lint(task_args)
88
98
  # Note: we're abusing Rake's argument handling a bit here. We call the
89
99
  # first argument `files` but it's actually only the first file--we pull
@@ -96,8 +106,13 @@ module HamlLint
96
106
  end
97
107
 
98
108
  # Friendly description that shows the full command that will be executed.
109
+ #
110
+ # This allows us to change the information displayed by `rake --tasks` based
111
+ # on the options passed to the constructor which defined the task.
112
+ #
113
+ # @return [String]
99
114
  def default_description
100
- description = 'Run `haml-lint'
115
+ description = "Run `#{HamlLint::APP_NAME}"
101
116
  description += " --config #{config}" if config
102
117
  description += " #{files.join(' ')}" if files.any?
103
118
  description += ' [files...]`'
@@ -1,9 +1,16 @@
1
1
  module HamlLint
2
2
  # Contains information about all lints detected during a scan.
3
3
  class Report
4
+ # List of lints that were found.
4
5
  attr_accessor :lints
6
+
7
+ # List of files that were linted.
5
8
  attr_reader :files
6
9
 
10
+ # Creates a report.
11
+ #
12
+ # @param lints [Array<HamlLint::Lint>] lints that were found
13
+ # @param files [Array<String>] files that were linted
7
14
  def initialize(lints, files)
8
15
  @lints = lints.sort_by { |l| [l.filename, l.line] }
9
16
  @files = files
@@ -2,8 +2,8 @@ module HamlLint
2
2
  # Outputs lints in a simple format with the filename, line number, and lint
3
3
  # message.
4
4
  class Reporter::DefaultReporter < Reporter
5
- def report_lints
6
- sorted_lints = lints.sort_by { |l| [l.filename, l.line] }
5
+ def display_report(report)
6
+ sorted_lints = report.lints.sort_by { |l| [l.filename, l.line] }
7
7
 
8
8
  sorted_lints.each do |lint|
9
9
  print_location(lint)