Class: Mbeditor::ModelGraphService
- Inherits:
-
Object
- Object
- Mbeditor::ModelGraphService
- Defined in:
- app/services/mbeditor/model_graph_service.rb
Overview
Builds a graph of the host app's ActiveRecord models and the associations between them, for the Models sidebar tab.
Reads the associations by reflection rather than by parsing model files.
mbeditor already runs inside the host app, so reflect_on_all_associations
is right there — and it resolves class_name:, through:, polymorphic and
inverse sides correctly, which regex or AST parsing of has_many lines
silently gets wrong. (ruby-lsp cannot help here: its index models constants
and methods, not associations.)
Deliberately never touches the database connection. Reflections are pure metadata, so this works on an app whose database isn't running or migrated; column counts are only filled in when a connection happens to be live.
Constant Summary collapse
- IGNORED_NAMESPACES =
Rails' own models — ActiveStorage, ActionText, SolidQueue, schema migrations — are not what you opened this tab to look at.
%w[ ActiveRecord ActiveStorage ActionText ActionMailbox SolidQueue SolidCache SolidCable ].freeze
- MAX_MODELS =
300- MAX_COLUMNS_SENT =
Only the first few columns travel: the diagram box shows that many and the full list is a click away in the schema modal, which fetches its own data from /model_schema.
8
Class Method Summary collapse
- .call(workspace_root) ⇒ Object
-
.fingerprint_for(root) ⇒ Object
A cheap stand-in for "have the models changed": the newest mtime and the file count across the model and migration directories.
-
.invalidate(_root = nil) ⇒ Object
Only needed for an explicit refresh: ordinary saves are picked up by the fingerprint below, so there is no file-change hook to keep in sync.
Class Method Details
.call(workspace_root) ⇒ Object
33 34 35 36 37 38 39 40 41 42 43 44 45 |
# File 'app/services/mbeditor/model_graph_service.rb', line 33 def call(workspace_root) root = workspace_root.to_s fingerprint = fingerprint_for(root) cached = MUTEX.synchronize { @cache } return cached[:payload] if cached && cached[:fingerprint] == fingerprint && cached[:root] == root payload = build(root) payload[:fingerprint] = fingerprint MUTEX.synchronize { @cache = { root: root, fingerprint: fingerprint, payload: payload } } write_artifacts(root, payload) payload end |
.fingerprint_for(root) ⇒ Object
A cheap stand-in for "have the models changed": the newest mtime and the file count across the model and migration directories. Avoids eager-loading the app just to decide whether the cache is stale.
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 |
# File 'app/services/mbeditor/model_graph_service.rb', line 57 def fingerprint_for(root) dirs = [File.join(root, "app", "models"), File.join(root, "db", "migrate")] newest = 0 count = 0 dirs.each do |dir| next unless File.directory?(dir) Dir.glob(File.join(dir, "**", "*.rb")).each do |f| count += 1 mtime = File.mtime(f).to_i newest = mtime if mtime > newest end end "#{newest}:#{count}" rescue StandardError "unknown" end |
.invalidate(_root = nil) ⇒ Object
Only needed for an explicit refresh: ordinary saves are picked up by the fingerprint below, so there is no file-change hook to keep in sync.
49 50 51 52 |
# File 'app/services/mbeditor/model_graph_service.rb', line 49 def invalidate(_root = nil) MUTEX.synchronize { @cache = nil } nil end |