Class: Seams::Generators::EngineGenerator

Inherits:
Rails::Generators::NamedBase
  • Object
show all
Includes:
HostInjector
Defined in:
lib/generators/seams/engine/engine_generator.rb

Overview

Generates a new Rails engine under engines//, fully isolated via isolate_namespace, with the Seams boundary cops pre-wired in the engine's own .rubocop.yml. After generating the new engine, the cop config of every existing sibling engine is updated so boundary enforcement covers the new engine without manual edits.

Run with: bin/rails generate seams:engine billing

Constant Summary collapse

NAME_PATTERN =
/\A[a-z][a-z0-9_]*\z/

Instance Method Summary collapse

Methods included from HostInjector

#host_inject_gem, #host_inject_include_in_application_controller, #host_inject_include_in_user, #host_inject_mount, #host_uninject_gem, #host_uninject_include, #host_uninject_mount, #routes_draw_anchor

Instance Method Details

#create_app ⇒ Object



57
58
59
60
61
62
63
# File 'lib/generators/seams/engine/engine_generator.rb', line 57

def create_app
  template "app/application_controller.rb.tt",
           "engines/#{name}/app/controllers/#{name}/application_controller.rb"
  # Phase 1.6 — engine-scoped abstract AR base.
  template "app/application_record.rb.tt",
           "engines/#{name}/app/models/#{name}/application_record.rb"
end

#create_config ⇒ Object



53
54
55
# File 'lib/generators/seams/engine/engine_generator.rb', line 53

def create_config
  template "config/routes.rb.tt", "engines/#{name}/config/routes.rb"
end

#create_dummy_app ⇒ Object

Phase 1.6 — per-engine dummy app via the shared DummyAppWriter. Generic engines start with an empty schema; canonical generators (auth/billing/etc) overwrite this with their own schema by calling DummyAppWriter.write! again with the right SCHEMA body. Runs AFTER create_spec_helper so DummyAppWriter's File.write silently overwrites the lightweight Thor template rather than tripping a Thor "conflict" prompt.



104
105
106
107
108
109
110
111
# File 'lib/generators/seams/engine/engine_generator.rb', line 104

def create_dummy_app
  Seams::Generators::DummyAppWriter.write!(
    engine_path: File.join(destination_root, "engines", name),
    engine_module: module_name,
    mount_at: "/#{name}",
    schema: "# generic engine — no schema yet"
  )
end

#create_engine_gemfile ⇒ Object



72
73
74
# File 'lib/generators/seams/engine/engine_generator.rb', line 72

def create_engine_gemfile
  template "Gemfile.tt", "engines/#{name}/Gemfile"
end

#create_engine_rakefile ⇒ Object



76
77
78
# File 'lib/generators/seams/engine/engine_generator.rb', line 76

def create_engine_rakefile
  template "Rakefile.tt", "engines/#{name}/Rakefile"
end

#create_gemspec ⇒ Object



43
44
45
# File 'lib/generators/seams/engine/engine_generator.rb', line 43

def create_gemspec
  template "gemspec.tt", "engines/#{name}/#{name}.gemspec"
end

#create_lib ⇒ Object



47
48
49
50
51
# File 'lib/generators/seams/engine/engine_generator.rb', line 47

def create_lib
  template "lib/engine.rb.tt",  "engines/#{name}/lib/#{name}/engine.rb"
  template "lib/version.rb.tt", "engines/#{name}/lib/#{name}/version.rb"
  template "lib/root.rb.tt",    "engines/#{name}/lib/#{name}.rb"
end

#create_license ⇒ Object



89
90
91
# File 'lib/generators/seams/engine/engine_generator.rb', line 89

def create_license
  template "LICENSE.tt", "engines/#{name}/LICENSE"
end

#create_locales ⇒ Object

Phase 1.6 — i18n stub + standalone Gemfile + Rakefile so the engine can be tested in isolation (cd engines/ && rake).



67
68
69
70
# File 'lib/generators/seams/engine/engine_generator.rb', line 67

def create_locales
  template "config/locales/en.yml.tt",
           "engines/#{name}/config/locales/en.yml"
end

#create_readme ⇒ Object



93
94
95
# File 'lib/generators/seams/engine/engine_generator.rb', line 93

def create_readme
  template "README.md.tt", "engines/#{name}/README.md"
end

#create_rubocop_config ⇒ Object



80
81
82
# File 'lib/generators/seams/engine/engine_generator.rb', line 80

def create_rubocop_config
  template "rubocop.yml.tt", "engines/#{name}/.rubocop.yml"
end

#create_spec_helper ⇒ Object



84
85
86
87
# File 'lib/generators/seams/engine/engine_generator.rb', line 84

def create_spec_helper
  template "spec/spec_helper.rb.tt",   "engines/#{name}/spec/spec_helper.rb"
  template "spec/example_spec.rb.tt",  "engines/#{name}/spec/#{name}_spec.rb"
end

#report ⇒ Object



144
145
146
147
148
149
# File 'lib/generators/seams/engine/engine_generator.rb', line 144

def report
  say ""
  say "  Engine `#{name}` generated at engines/#{name}/", :green
  say "  Run its specs with: bin/rails seams:test[#{name}]"
  say ""
end

#update_sibling_engines ⇒ Object



132
133
134
135
136
137
138
139
140
141
142
# File 'lib/generators/seams/engine/engine_generator.rb', line 132

def update_sibling_engines
  sibling_dirs = sibling_engine_dirs
  return if sibling_dirs.empty?

  Seams::Generators::SiblingRubocopWriter.rewrite!(
    engines_root: engines_root,
    dirs: (sibling_dirs + [name]).sort
  )

  say "  update  .rubocop.yml of #{sibling_dirs.size} sibling engine(s)", :green
end

#validate_name ⇒ Object



30
31
32
33
34
35
36
37
38
39
40
41
# File 'lib/generators/seams/engine/engine_generator.rb', line 30

def validate_name
  unless NAME_PATTERN.match?(name)
    raise Seams::GeneratorError,
          "Engine name #{name.inspect} must be lowercase letters, digits, " \
          "and underscores, starting with a letter."
  end

  engine_root = File.join(destination_root, "engines", name)
  return unless File.exist?(engine_root)

  raise Seams::GeneratorError, "Engine #{name.inspect} already exists at #{engine_root}"
end

#wire_into_host ⇒ Object



113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
# File 'lib/generators/seams/engine/engine_generator.rb', line 113

def wire_into_host
  return if options[:skip_host_wiring]

  # Mount the engine into the host's routes (idempotent — skips
  # if the line already exists, so canonical generators that
  # call this and ALSO mount themselves are safe).
  host_inject_mount(engine_class: "#{module_name}::Engine", at: "/#{name}")

  # Drop a host-side initializer stub the user can fill in.
  # Skipped if a canonical generator (or the host) has already
  # created one.
  initializer_path = File.join(destination_root, "config/initializers/#{name}.rb")
  if File.exist?(initializer_path)
    say "  exist   config/initializers/#{name}.rb (kept)", :blue
  elsif File.directory?(File.join(destination_root, "config/initializers"))
    template "host_initializer.rb.tt", "config/initializers/#{name}.rb"
  end
end