Class: Cane::DocCheck

Inherits:
Struct
  • Object
show all
Defined in:
lib/cane/doc_check.rb

Overview

Creates violations for class definitions that do not have an explantory comment immediately preceding.

Defined Under Namespace

Classes: ClassDefinition

Constant Summary collapse

DESCRIPTION =
"Class and Module definitions require explanatory comments on previous line"
MAGIC_COMMENT_REGEX =

Stolen from ERB source, amended to be slightly stricter to work around some known false positives.

%r"#(\s+-\*-)?\s+(en)?coding\s*[=:]\s*([[:alnum:]\-_]+)"
CLASS_REGEX =
/^\s*(?:class|module)\s+([^\s;]+)/
SINGLE_LINE_CLASS_REGEX =
/^\s*(?:class|module).*;\s*end\s*(#.*)?\s*$/
METHOD_REGEX =
/(?:^|\s)def\s+/

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Instance Attribute Details

#optsObject

Returns the value of attribute opts

Returns:

  • (Object)

    the current value of opts



8
9
10
# File 'lib/cane/doc_check.rb', line 8

def opts
  @opts
end

Class Method Details

.keyObject



21
# File 'lib/cane/doc_check.rb', line 21

def self.key; :doc; end

.nameObject



22
# File 'lib/cane/doc_check.rb', line 22

def self.name; "documentation checking"; end

.optionsObject



23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
# File 'lib/cane/doc_check.rb', line 23

def self.options
  {
    doc_glob:    ['Glob to run doc checks over',
                    default:  '{app,lib}/**/*.rb',
                    variable: 'GLOB',
                    clobber:  :no_doc],
    doc_exclude: ['Exclude file or glob from documentation checking',
                    variable: 'GLOB',
                    type: Array,
                    default: [],
                    clobber: :no_doc],
    no_readme:   ['Disable readme checking', cast: ->(x) { !x }],
    no_doc:      ['Disable documentation checking', cast: ->(x) { !x }]
  }
end

Instance Method Details

#class_definition(number, line, last_line) ⇒ Object



96
97
98
99
100
101
102
103
# File 'lib/cane/doc_check.rb', line 96

def class_definition(number, line, last_line)
  ClassDefinition.new({
    line: (number + 1),
    label: extract_class_name(line),
    has_doc: comment?(last_line),
    requires_doc: method_definition?(line)
  })
end

#class_definition?(line) ⇒ Boolean

Returns:

  • (Boolean)


124
125
126
# File 'lib/cane/doc_check.rb', line 124

def class_definition?(line)
  line =~ CLASS_REGEX && $1.index('<<') != 0
end

#class_definitions_in(file_name) ⇒ Object



73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'lib/cane/doc_check.rb', line 73

def class_definitions_in(file_name)
  closed_classes = []
  open_classes = []
  last_line = ""

  Cane::File.iterator(file_name).each_with_index do |line, number|
    if class_definition? line
      if single_line_class_definition? line
        closed_classes
      else
        open_classes
      end.push class_definition(number, line, last_line)

    elsif method_definition?(line) && !open_classes.empty?
      open_classes.last.requires_doc = true
    end

    last_line = line
  end

  (closed_classes + open_classes).sort_by(&:line)
end

#comment?(line) ⇒ Boolean

Returns:

  • (Boolean)


132
133
134
# File 'lib/cane/doc_check.rb', line 132

def comment?(line)
  line =~ /^\s*#/ && !(MAGIC_COMMENT_REGEX =~ line)
end

#excluded?(file) ⇒ Boolean

Returns:

  • (Boolean)


146
147
148
# File 'lib/cane/doc_check.rb', line 146

def excluded?(file)
  exclusions.include?(file)
end

#exclusionsObject



140
141
142
143
144
# File 'lib/cane/doc_check.rb', line 140

def exclusions
  @exclusions ||= opts.fetch(:doc_exclude, []).flatten.map do |i|
    Dir[i]
  end.flatten.to_set
end

#extract_class_name(line) ⇒ Object



136
137
138
# File 'lib/cane/doc_check.rb', line 136

def extract_class_name(line)
  line.match(CLASS_REGEX)[1]
end

#file_namesObject



116
117
118
# File 'lib/cane/doc_check.rb', line 116

def file_names
  Dir[opts.fetch(:doc_glob)].reject { |file| excluded?(file) }
end

#find_violations(file_name) ⇒ Object



60
61
62
63
64
65
66
67
68
69
70
71
# File 'lib/cane/doc_check.rb', line 60

def find_violations(file_name)
  class_definitions_in(file_name).map do |class_definition|
    if class_definition.requires_doc? && class_definition.missing_doc?
      {
        file:        file_name,
        line:        class_definition.line,
        label:       class_definition.label,
        description: DESCRIPTION
      }
    end
  end.compact
end

#method_definition?(line) ⇒ Boolean

Returns:

  • (Boolean)


120
121
122
# File 'lib/cane/doc_check.rb', line 120

def method_definition?(line)
  line =~ METHOD_REGEX
end

#missing_file_violationsObject



105
106
107
108
109
110
111
112
113
114
# File 'lib/cane/doc_check.rb', line 105

def missing_file_violations
  result = []
  return result if opts[:no_readme]

  if Cane::File.case_insensitive_glob("README*").none?
    result << { description: 'Missing documentation',
                label: 'No README found' }
  end
  result
end

#single_line_class_definition?(line) ⇒ Boolean

Returns:

  • (Boolean)


128
129
130
# File 'lib/cane/doc_check.rb', line 128

def single_line_class_definition?(line)
  line =~ SINGLE_LINE_CLASS_REGEX
end

#violationsObject



52
53
54
55
56
57
58
# File 'lib/cane/doc_check.rb', line 52

def violations
  return [] if opts[:no_doc]

  missing_file_violations + worker.map(file_names) {|file_name|
    find_violations(file_name)
  }.flatten
end

#workerObject



150
151
152
# File 'lib/cane/doc_check.rb', line 150

def worker
  Cane.task_runner(opts)
end