Class: MaquinaStream::Generators::InstallGenerator

Inherits:
Rails::Generators::Base
  • Object
show all
Defined in:
lib/generators/maquina_stream/install/install_generator.rb

Overview

Wires the engine into a host: the initializer, the mount, the importmap pins, the Stimulus registration and the stylesheets.

bin/rails generate maquina_stream:install

Every step is idempotent and none of them overwrites a host file. A step whose ingredient is missing — no importmap, no Stimulus entrypoint, no layout — says what to do by hand instead of failing or silently doing nothing, because a generator that no-ops quietly is worse than one that is not there.

Two things it exists to prevent, both learned the hard way:

  • Turbo must be pinned by the host. Repair applies Turbo Stream morphs; with window.Turbo undefined every repair fails inside a catch and nothing in the browser says so.
  • A deferred-renderer pin needs preload: false. importmap-rails preloads by default, which fetches the library on every page and defeats the lazy import it exists to avoid.

Constant Summary collapse

INITIALIZER =
"config/initializers/maquina_stream.rb"
ROUTES =
"config/routes.rb"
IMPORTMAP =
"config/importmap.rb"
LAYOUT =
"app/views/layouts/application.html.erb"
ENTRYPOINTS =

Where registerMaquinaStreamControllers(application) can go, best first. Both have an application in scope in a stock importmap app.

%w[
  app/javascript/controllers/index.js
  app/javascript/application.js
].freeze
MOUNT =
'mount MaquinaStream::Engine => "/maquina_stream"'
TURBO_PIN =
<<~RUBY
  # Required by maquina_stream: repair applies Turbo Stream morphs, and with
  # `window.Turbo` undefined every repair fails silently inside a catch.
  pin "@hotwired/turbo-rails", to: "turbo.min.js"
RUBY
DEFERRED_PINS =
<<~RUBY
  # Libraries the client-deferred renderers import lazily. `preload: false` is
  # load-bearing: importmap-rails preloads by default, which would emit a
  # <link rel="modulepreload"> and fetch both on every page — exactly the cost
  # the lazy import inside `ms-deferred#library()` exists to avoid.
  pin "mermaid", to: "https://cdn.jsdelivr.net/npm/[email protected]/+esm", preload: false
  pin "katex", to: "https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.mjs", preload: false
RUBY
REGISTRATION =
<<~JS
  // maquina_stream registers its own Stimulus identifiers rather than relying
  // on an eager-load glob: those identifiers are part of the DOM contract.
  import { registerMaquinaStreamControllers } from "maquina_stream"
  registerMaquinaStreamControllers(application)
JS
STYLESHEETS =
<<~ERB
  <%= stylesheet_link_tag "maquina_stream/reveal" %>
  <%= stylesheet_link_tag "maquina_stream/themes/light" %>
  <%= stylesheet_link_tag "maquina_stream/themes/dark" %>
ERB

Instance Method Summary collapse

Instance Method Details

#create_initializer ⇒ Object



78
79
80
81
82
83
84
# File 'lib/generators/maquina_stream/install/install_generator.rb', line 78

def create_initializer
  if exists?(INITIALIZER)
    skip INITIALIZER, "already exists — left untouched"
  else
    template "initializer.rb.tt", INITIALIZER
  end
end


117
118
119
120
121
122
# File 'lib/generators/maquina_stream/install/install_generator.rb', line 117

def link_stylesheets
  return by_hand(LAYOUT, STYLESHEETS) unless exists?(LAYOUT)
  return skip(LAYOUT, "stylesheets already linked") if read(LAYOUT).include?("maquina_stream/reveal")

  inject_into_file LAYOUT, STYLESHEETS.gsub(/^/, "    "), before: %r{^\s*</head>}
end

#mount_engine ⇒ Object



86
87
88
89
90
91
# File 'lib/generators/maquina_stream/install/install_generator.rb', line 86

def mount_engine
  return by_hand(ROUTES, MOUNT) unless exists?(ROUTES)
  return skip(ROUTES, "engine already mounted") if read(ROUTES).include?("MaquinaStream::Engine")

  route MOUNT
end

#pin_deferred_renderers ⇒ Object



101
102
103
104
105
106
107
# File 'lib/generators/maquina_stream/install/install_generator.rb', line 101

def pin_deferred_renderers
  return unless options[:deferred_renderers]
  return by_hand(IMPORTMAP, DEFERRED_PINS) unless exists?(IMPORTMAP)
  return skip(IMPORTMAP, "mermaid and katex already pinned") if read(IMPORTMAP).include?('pin "mermaid"')

  append_to_file IMPORTMAP, "\n#{DEFERRED_PINS}"
end

#pin_turbo ⇒ Object



93
94
95
96
97
98
99
# File 'lib/generators/maquina_stream/install/install_generator.rb', line 93

def pin_turbo
  return by_hand(IMPORTMAP, TURBO_PIN, importmap_absent_note) unless exists?(IMPORTMAP)
  return skip(IMPORTMAP, "@hotwired/turbo-rails already pinned") if read(IMPORTMAP).include?("@hotwired/turbo-rails")
  return turbo_missing unless turbo_available?

  append_to_file IMPORTMAP, "\n#{TURBO_PIN}"
end

#register_controllers ⇒ Object



109
110
111
112
113
114
115
# File 'lib/generators/maquina_stream/install/install_generator.rb', line 109

def register_controllers
  entrypoint = ENTRYPOINTS.find { |path| exists?(path) && read(path).match?(/\bapplication\b/) }
  return by_hand(ENTRYPOINTS.first, REGISTRATION, entrypoint_absent_note) if entrypoint.nil?
  return skip(entrypoint, "controllers already registered") if read(entrypoint).include?("registerMaquinaStreamControllers")

  append_to_file entrypoint, "\n#{REGISTRATION}"
end

#report_the_two_seams ⇒ Object



124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/generators/maquina_stream/install/install_generator.rb', line 124

def report_the_two_seams
  say ""
  say "maquina_stream is wired. Two things are still yours:", :green
  say ""
  say "  1. #{INITIALIZER} — `authorize` is a stub that denies everything."
  say "     Until you replace it every repair request is refused, which is safe"
  say "     and also broken: the browser can never repair a message."
  say ""
  say "  2. bin/rails generate maquina_stream:streamable Message"
  say "     — the migration and the model macro, and it fills in `find_stream`."
  say ""
end