Class: RuboCop::Cop::Koi::TableLinkHeading

Inherits:
Base
  • Object
show all
Extended by:
AutoCorrector
Defined in:
lib/rubocop/cop/koi/table_link_heading.rb

Overview

Koi admin tables should link to each record exactly once, from the column that identifies the record, and that link should render as a row heading (th) for accessibility.

When a table renders a single record link, mark it as the row heading with heading: true (autocorrectable). When a table renders more than one record link, the extra columns should use text instead; heading: false documents an intentional exception. Links that override url: are not record links, so they are ignored, as are links that forward options (**options).

erb_lint feeds RuboCop one ERB tag at a time, so the fragment being inspected is not enough context to count links. When linting a view, this cop reads the whole file from disk and counts record links across all of its ERB tags. Outside a view file it counts links in the current source. Counting is per file, so a view that renders two tables is treated as one.

This cop only takes effect through erb_lint: plain rubocop does not inspect .erb files, and the Include configuration restricts it to Koi admin views.

Examples:

# bad
row.link(:name)

# good
row.link(:name, heading: true)

# good - not a record link
row.link(:name, url: :edit_admin_page_path)

# good - intentional extra link
row.link(:name, heading: true)
row.link(:homepage, heading: false)

Constant Summary collapse

MSG_HEADING =
"The record link should be the row heading; add `heading: true`."
MSG_EXTRA =
"Tables should link to the record once, from the column that identifies it. " \
"Use `text` instead, or `heading: false` to keep an intentional extra link."
RESTRICT_ON_SEND =
%i[link].freeze
BLOCK_EXPR =

How erb_lint trims trailing block expressions from ERB tags, copied from Rails: action_view/template/handlers/erb/erubi.rb

/\s*((\s+|\))do|\{)(\s*\|[^|]*\|)?\s*\Z/
ERB_TAG =
/<%(?:(?!%>).)*%>/m

Instance Method Summary collapse

Instance Method Details

#on_new_investigationObject



61
62
63
64
# File 'lib/rubocop/cop/koi/table_link_heading.rb', line 61

def on_new_investigation
  super
  @record_link_count = nil
end

#on_send(node) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/rubocop/cop/koi/table_link_heading.rb', line 66

def on_send(node)
  return unless row_link?(node)

  options = options(node)
  return if options && explicit_options?(options)

  anchor = node.arguments.reject(&:block_pass_type?).last
  return unless anchor

  if record_link_count > 1
    add_offense(node, message: MSG_EXTRA)
  else
    add_offense(node, message: MSG_HEADING) do |corrector|
      corrector.insert_after(anchor, ", heading: true")
    end
  end
end

#row_link?(node) ⇒ Object



57
58
59
# File 'lib/rubocop/cop/koi/table_link_heading.rb', line 57

def_node_matcher :row_link?, <<~PATTERN
  (send {(send nil? :row) (lvar :row)} :link _ ...)
PATTERN