Establish standard Ruby project structure
Moves app to lib/pro_tacts/ so tests can require without relative
paths via Minitest::TestTask's load path.

Assisted-by: Claude Opus 4.5 via Claude Code
change qkowsttplxmzsvwttwylwmlomvzwssuy
commit 812fdffe99ffbd1e7fa17d4f5f3158c2bdb337d4
author Alpha Chen <alpha@kejadlen.dev>
date
parent qpovkvqx
diff --git a/Gemfile b/Gemfile
index 4ced7cb..d502359 100644
--- a/Gemfile
+++ b/Gemfile
@@ -12,4 +12,5 @@ gem "sentry-ruby"
 group :development do
   gem "irb"
   gem "minitest"
+  gem "rack-test"
 end
diff --git a/Gemfile.lock b/Gemfile.lock
index ef389f9..7a2252e 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -24,6 +24,8 @@ GEM
     puma (7.1.0)
       nio4r (~> 2.0)
     rack (3.2.4)
+    rack-test (2.2.0)
+      rack (>= 1.3)
     rackup (2.3.1)
       rack (>= 3)
     rake (13.3.1)
@@ -50,6 +52,7 @@ DEPENDENCIES
   logger
   minitest
   puma
+  rack-test
   rackup
   rake
   roda
@@ -71,6 +74,7 @@ CHECKSUMS
   psych (5.3.1) sha256=eb7a57cef10c9d70173ff74e739d843ac3b2c019a003de48447b2963d81b1974
   puma (7.1.0) sha256=e45c10cb124f224d448c98db653a75499794edbecadc440ad616cf50f2fd49dd
   rack (3.2.4) sha256=5d74b6f75082a643f43c1e76b419c40f0e5527fcfee1e669ac1e6b73c0ccb6f6
+  rack-test (2.2.0) sha256=005a36692c306ac0b4a9350355ee080fd09ddef1148a5f8b2ac636c720f5c463
   rackup (2.3.1) sha256=6c79c26753778e90983761d677a48937ee3192b3ffef6bc963c0950f94688868
   rake (13.3.1) sha256=8c9e89d09f66a26a01264e7e3480ec0607f0c497a861ef16063604b1b08eb19c
   rdoc (7.0.3) sha256=dfe3d0981d19b7bba71d9dbaeb57c9f4e3a7a4103162148a559c4fc687ea81f9
diff --git a/Rakefile b/Rakefile
new file mode 100644
index 0000000..cc4ae53
--- /dev/null
+++ b/Rakefile
@@ -0,0 +1,7 @@
+# frozen_string_literal: true
+
+require "minitest/test_task"
+
+Minitest::TestTask.create
+
+task default: :test
diff --git a/config.ru b/config.ru
index c588f40..21df719 100644
--- a/config.ru
+++ b/config.ru
@@ -1,61 +1,5 @@
-require "roda"
-require "sentry-ruby"
+# frozen_string_literal: true
 
-Sentry.init do |config|
-  # TODO: Consolidate configuration
-  config.dsn = ENV.fetch("SENTRY_DSN")
+require_relative "lib/pro_tacts/web"
 
-  # Get breadcrumbs from logs
-  config.breadcrumbs_logger = [:sentry_logger, :http_logger]
-
-  # Add data like request headers and IP for users, if applicable;
-  # see https://docs.sentry.io/platforms/ruby/data-management/data-collected/ for more info
-  config.send_default_pii = true
-
-  # Trace all the things!
-  config.traces_sample_rate = 1.0
-end
-
-class App < Roda
-  plugin :not_found do
-    Sentry.capture_message("404 Not Found", level: :warning, extra: {
-      path: request.path,
-      method: request.request_method
-    })
-    "Not Found"
-  end
-
-  route do |r|
-    # GET / request
-    r.root do
-      r.redirect "/hello"
-    end
-
-    # /hello branch
-    r.on "hello" do
-      # Set variable for all routes in /hello branch
-      @greeting = "Hello"
-
-      # GET /hello/world request
-      r.get "world" do
-        "#{@greeting} world!"
-      end
-
-      # /hello request
-      r.is do
-        # GET /hello request
-        r.get do
-          "#{@greeting}!"
-        end
-
-        # POST /hello request
-        r.post do
-          puts "Someone said #{@greeting}!"
-          r.redirect
-        end
-      end
-    end
-  end
-end
-
-run App.freeze.app
+run ProTacts::Web.freeze.app
diff --git a/lib/pro_tacts/web.rb b/lib/pro_tacts/web.rb
new file mode 100644
index 0000000..1622e54
--- /dev/null
+++ b/lib/pro_tacts/web.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require "roda"
+require "sentry-ruby"
+
+Sentry.init do |config|
+  # TODO: Consolidate configuration
+  config.dsn = ENV.fetch("SENTRY_DSN")
+
+  # Get breadcrumbs from logs
+  config.breadcrumbs_logger = [:sentry_logger, :http_logger]
+
+  # Add data like request headers and IP for users, if applicable;
+  # see https://docs.sentry.io/platforms/ruby/data-management/data-collected/ for more info
+  config.send_default_pii = true
+
+  # Trace all the things!
+  config.traces_sample_rate = 1.0
+end
+
+module ProTacts
+  class Web < Roda
+    plugin :not_found do
+      Sentry.capture_message("404 Not Found", level: :warning, extra: {
+        path: request.path,
+        method: request.request_method
+      })
+      "Not Found"
+    end
+
+    route do |r|
+      # GET / request
+      r.root do
+        r.redirect "/hello"
+      end
+
+      # /hello branch
+      r.on "hello" do
+        # Set variable for all routes in /hello branch
+        @greeting = "Hello"
+
+        # GET /hello/world request
+        r.get "world" do
+          "#{@greeting} world!"
+        end
+
+        # /hello request
+        r.is do
+          # GET /hello request
+          r.get do
+            "#{@greeting}!"
+          end
+
+          # POST /hello request
+          r.post do
+            puts "Someone said #{@greeting}!"
+            r.redirect
+          end
+        end
+      end
+    end
+  end
+end
diff --git a/test/pro_tacts/test_web.rb b/test/pro_tacts/test_web.rb
new file mode 100644
index 0000000..abfad25
--- /dev/null
+++ b/test/pro_tacts/test_web.rb
@@ -0,0 +1,21 @@
+# frozen_string_literal: true
+
+require "minitest/autorun"
+require "rack/test"
+
+require "pro_tacts/web"
+
+class WebTest < Minitest::Test
+  include Rack::Test::Methods
+
+  def app
+    ProTacts::Web
+  end
+
+  def test_hello
+    get "/hello"
+
+    assert_equal 200, last_response.status
+    assert_equal "Hello!", last_response.body
+  end
+end