Skip to content

Commit 757aeb8

Browse files
tomazzlenderclaude
andcommitted
Re-add minimal Rails shim for the tidewave.ai frontend
The frontend (and the project_eval connectivity probe) evaluates Ruby that references `Rails.env`, `Rails.application.config.*`, etc. to confirm the host is alive. Under Sinatra / plain Rack / standalone processes there is no Rails constant, so the probe raises NameError and the UI reports "Could not connect to your app". Add Tidewave::RailsShim, auto-installed from Tidewave::Middleware#initialize when no real Rails is defined: - StringInquirer (subclass of String) answers development?/test?/production? so frontend env predicates work. - DeepStub returns self for any method call so chains like Rails.application.config.something resolve harmlessly instead of raising. - FakeRails exposes env, root, logger, application, version backed by Tidewave.config where possible. install! is idempotent and a no-op when real Rails is already loaded, so this is safe to include in Rails hosts too. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 432fb74 commit 757aeb8

2 files changed

Lines changed: 82 additions & 0 deletions

File tree

lib/tidewave/middleware.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
require "logger"
99
require "fileutils"
1010
require "tidewave"
11+
require_relative "rails_shim"
1112
require_relative "streamable_http_transport"
1213

1314
# Tools register themselves as descendants of Tidewave::Tools::Base, so we
@@ -29,6 +30,7 @@ class Tidewave::Middleware
2930
INVALID_ORIGIN = "For security reasons, Tidewave does not accept requests with an origin header for this endpoint.".freeze
3031

3132
def initialize(app, config = nil)
33+
Tidewave::RailsShim.install!
3234
@config = config || Tidewave.config
3335
@allow_remote_access = @config.allow_remote_access
3436
@client_url = @config.client_url

lib/tidewave/rails_shim.rb

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
# frozen_string_literal: true
2+
3+
# Minimal Rails-API stub for the tidewave.ai frontend and the Tidewave
4+
# connectivity probe, which evaluate things like `Rails.env` or
5+
# `Rails.application.config.something` to confirm the app is alive.
6+
#
7+
# This is NOT Rails. It answers just enough of the surface so probes
8+
# don't raise NameError when running under Sinatra, plain Rack, workers,
9+
# or standalone scripts.
10+
#
11+
# Auto-installed by Tidewave::Middleware (and Tidewave::Server) when no
12+
# real Rails constant is defined. Hosts that already have Rails get the
13+
# real one and skip this entirely.
14+
module Tidewave
15+
module RailsShim
16+
# String-like that responds to development?/test?/production? etc.,
17+
# matching ActiveSupport::StringInquirer behavior closely enough for
18+
# the frontend's environment checks.
19+
class StringInquirer < String
20+
def method_missing(name, *args)
21+
name = name.to_s
22+
return self == name.chomp("?") if name.end_with?("?")
23+
24+
super
25+
end
26+
27+
def respond_to_missing?(name, include_private = false)
28+
name.to_s.end_with?("?") || super
29+
end
30+
end
31+
32+
# Returns itself for any method call so chained probes like
33+
# Rails.application.config.something resolve harmlessly to an
34+
# inspectable sentinel rather than raising NoMethodError.
35+
class DeepStub
36+
def method_missing(_name, *_args, &_blk) = self
37+
def respond_to_missing?(_name, _ = false) = true
38+
def to_s = ""
39+
def inspect = "#<Rails (Tidewave shim)>"
40+
end
41+
42+
# The faux Rails module installed at the top level. Mirrors the
43+
# handful of methods the tidewave.ai frontend touches.
44+
module FakeRails
45+
module_function
46+
47+
def env
48+
@env ||= StringInquirer.new(
49+
Tidewave.config.environment || ENV.fetch("RACK_ENV", "development")
50+
)
51+
end
52+
53+
def root
54+
@root ||= Tidewave.config.root || Pathname.new(Dir.pwd)
55+
end
56+
57+
def logger
58+
@logger ||= Tidewave.config.resolved_logger if Tidewave.config.respond_to?(:resolved_logger)
59+
@logger ||= Logger.new($stdout)
60+
end
61+
62+
def application
63+
@application ||= DeepStub.new
64+
end
65+
66+
def version = "0.0.0-tidewave-shim"
67+
end
68+
69+
# Define ::Rails as the FakeRails module if no real Rails is loaded.
70+
# Idempotent: a second call (e.g., after a real Rails appears later)
71+
# is a no-op.
72+
def self.install!
73+
return if defined?(::Rails)
74+
75+
require "logger"
76+
require "pathname"
77+
Object.const_set(:Rails, FakeRails)
78+
end
79+
end
80+
end

0 commit comments

Comments
 (0)