Class: Rails::Generators::Hyperdrive::InstallGenerator

Inherits:
Base
  • Object
show all
Includes:
ContentSyncSupport, GitignoreSupport
Defined in:
lib/generators/hyperdrive/install/install_generator.rb

Constant Summary collapse

ENGINE_MOUNT_TOKEN =
"Rails::Hyperdrive::Engine"
DEFAULT_MOUNT_AT =
"/_hyperdrive".freeze
MCP_JSON_PATH =
".mcp.json".freeze
MCP_SERVER_KEY =
"rails-hyperdrive".freeze
GEMFILE =
"Gemfile".freeze
BUNDLER_PLUGIN =
"bundler-rails-hyperdrive".freeze

Constants included from GitignoreSupport

GitignoreSupport::GITIGNORE

Instance Method Summary collapse

Methods included from GitignoreSupport

#ensure_gitignored

Methods included from ContentSyncSupport

#options

Instance Method Details

#discover_artifactsObject



35
36
37
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 35

def discover_artifacts
  runner.discover_artifacts(skip: options[:skip_content])
end

#ignore_discover_cacheObject



56
57
58
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 56

def ignore_discover_cache
  ensure_gitignored(::Rails::Hyperdrive::CompanionDiscovery::CACHE_RELATIVE_PATH)
end

#mount_engineObject



85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 85

def mount_engine
  routes_file = "config/routes.rb"
  unless File.exist?(::Rails.root.join(routes_file))
    say_status :skip, "no #{routes_file} found; skipping engine mount", :yellow
    return
  end

  contents = File.read(::Rails.root.join(routes_file))
  if contents.include?(ENGINE_MOUNT_TOKEN)
    say_status :identical, "#{routes_file} (engine already mounted)", :blue
    return
  end

  snippet = "  mount Rails::Hyperdrive::Engine => \"#{mount_path}\" if Rails.env.development?\n"
  inject_into_file routes_file, snippet, after: /Rails\.application\.routes\.draw do\s*\n/
end


110
111
112
113
114
115
116
117
118
119
120
121
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 110

def print_summary
  say ""
  say_status :done, "hyperdrive initialized", :green
  say "  Mount: #{mount_path} (in config/routes.rb)"
  say "  Server: #{::Rails::Hyperdrive::McpServer::TOOLS.size} MCP tools at http://localhost:3000#{mount_path}/mcp"
  runner.summary_lines.each { |line| say line } unless options[:skip_content]
  say ""
  say "  Next steps:"
  say "    1. bin/rails server"
  say "    2. Open Claude Code in this directory; it will read .mcp.json"
  say "    3. Verify the connection: curl http://localhost:3000#{mount_path}/mcp"
end

#register_bundler_pluginObject

Any existing directive counts as registered — a path- or version-qualified line is a deliberate choice this must not duplicate or rewrite.



63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 63

def register_bundler_plugin
  gemfile = ::Rails.root.join(GEMFILE)
  unless File.exist?(gemfile)
    say_status :skip, "no #{GEMFILE} found; add plugin #{BUNDLER_PLUGIN.inspect} manually", :yellow
    return
  end

  body = File.read(gemfile)
  if body.match?(/^\s*plugin\s+["']#{BUNDLER_PLUGIN}["']/)
    say_status :identical, "#{GEMFILE} (#{BUNDLER_PLUGIN} plugin already registered)", :blue
    return
  end

  prefix = body.end_with?("\n") || body.empty? ? "" : "\n"
  append_to_file GEMFILE, "#{prefix}plugin #{BUNDLER_PLUGIN.inspect}\n"
end

#sync_contentObject

--skip-content writes no lockfile either: the lock is a manifest of installed content, and an empty one would assert "zero files is the managed set". A later init or sync reconstructs the full state.



105
106
107
108
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 105

def sync_content
  return if options[:skip_content]
  runner.install(mode: :preserve)
end

#verify_environmentObject



31
32
33
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 31

def verify_environment
  runner.verify_environment!
end

#write_initializerObject



80
81
82
83
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 80

def write_initializer
  return if mount_path == DEFAULT_MOUNT_AT
  template "initializer.rb.tt", "config/initializers/hyperdrive.rb"
end

#write_mcp_jsonObject

The write is forced: Thor's conflict prompt would otherwise block the run waiting on stdin.



41
42
43
44
45
46
47
48
49
50
51
52
53
54
# File 'lib/generators/hyperdrive/install/install_generator.rb', line 41

def write_mcp_json
  existing = mcp_json_on_disk
  document = existing ? parse_mcp_json(existing) : {}
  return if document.nil?

  (document["mcpServers"] ||= {})[MCP_SERVER_KEY] = mcp_server_entry
  content = JSON.pretty_generate(document) + "\n"

  if existing == content
    say_status :unchanged, MCP_JSON_PATH, :blue
  else
    create_file MCP_JSON_PATH, content, force: true
  end
end