Module: Seams::Generators::SiblingRubocopWriter

Defined in:
lib/seams/generators/sibling_rubocop_writer.rb

Overview

Rewrites the OtherEngines lists inside every engine’s .rubocop.yml so each engine is configured with every OTHER engine as a boundary. Used by both the seams:engine and seams:remove generators — generation adds the new engine, removal prunes the gone one.

The replacement is scoped tightly to the OtherEngines key so we never clobber the surrounding ExposedConcerns / Enabled / OwnEngine values. The previous version of this code used a too-greedy lookahead that ate the next sibling key.

Constant Summary collapse

MODULE_ACCESS_KEY =
"Seams/NoCrossEngineModelAccess"
DEPENDENCY_KEY =
"Seams/NoCrossEngineDependency"

Class Method Summary collapse

Class Method Details

.camelcase(name) ⇒ Object



40
41
42
# File 'lib/seams/generators/sibling_rubocop_writer.rb', line 40

def camelcase(name)
  name.split("_").map(&:capitalize).join
end

.formatted_block(values) ⇒ Object



63
64
65
66
67
# File 'lib/seams/generators/sibling_rubocop_writer.rb', line 63

def formatted_block(values)
  return "  OtherEngines: []" if values.empty?

  "  OtherEngines:\n#{values.map { |v| "    - #{v}" }.join("\n")}"
end

.other_engines_regex(cop_key) ⇒ Object



69
70
71
72
73
74
# File 'lib/seams/generators/sibling_rubocop_writer.rb', line 69

def other_engines_regex(cop_key)
  prefix     = "#{Regexp.escape(cop_key)}:[^\\n]*\\n(?:[ \\t]+[^\\n]+\\n)*?"
  empty_form = "[ \\t]*\\[\\][ \\t]*\\n"
  list_form  = "[ \\t]*\\n(?:    -[ \\t]+[^\\n]+\\n)*"
  Regexp.new("(#{prefix})  OtherEngines:(?:#{empty_form}|#{list_form})")
end

.replace_other_engines(content, cop_key, values) ⇒ Object

Matches ‘<cop_key>:` and the very next `OtherEngines:` line under it, replacing only the value of OtherEngines. We don’t try to span multiple following keys — the regex stops as soon as it has consumed either the inline ‘[]` form or the indented list form.



48
49
50
51
52
53
54
55
56
57
58
59
60
61
# File 'lib/seams/generators/sibling_rubocop_writer.rb', line 48

def replace_other_engines(content, cop_key, values)
  regex     = other_engines_regex(cop_key)
  formatted = formatted_block(values)

  unless regex.match?(content)
    raise ArgumentError,
          "SiblingRubocopWriter could not find a writable `OtherEngines:` " \
          "value under `#{cop_key}`. Hand-edited or flow-style YAML " \
          "(`{ OtherEngines: [Foo] }`) is not supported — restore the " \
          "block-style template and retry."
  end

  content.sub(regex, "\\1#{formatted}\n")
end

.rewrite!(engines_root:, dirs:) ⇒ Object

Parameters:

  • engines_root (String)

    absolute or relative path to engines/

  • dirs (Array<String>)

    directory names of the engines that currently exist on disk



26
27
28
29
30
31
32
33
34
35
36
37
38
# File 'lib/seams/generators/sibling_rubocop_writer.rb', line 26

def rewrite!(engines_root:, dirs:)
  dirs.each do |engine_dir|
    others        = (dirs - [engine_dir]).sort
    others_module = others.map { |d| camelcase(d) }
    rubocop_path  = File.join(engines_root, engine_dir, ".rubocop.yml")
    next unless File.exist?(rubocop_path)

    content = File.read(rubocop_path)
    content = replace_other_engines(content, MODULE_ACCESS_KEY, others_module)
    content = replace_other_engines(content, DEPENDENCY_KEY,    others)
    File.write(rubocop_path, content)
  end
end