debug-mcp 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (122) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +3 -0
  3. data/CHANGELOG.md +83 -0
  4. data/LICENSE +21 -0
  5. data/README.ja.md +383 -0
  6. data/README.md +384 -0
  7. data/examples/01_simple_bug.rb +43 -0
  8. data/examples/02_data_pipeline.rb +93 -0
  9. data/examples/03_recursion.rb +96 -0
  10. data/examples/RAILS_SCENARIOS.md +350 -0
  11. data/examples/SCENARIOS.md +142 -0
  12. data/examples/rails_test_app/setup.sh +428 -0
  13. data/examples/rails_test_app/testapp/.dockerignore +10 -0
  14. data/examples/rails_test_app/testapp/.ruby-version +1 -0
  15. data/examples/rails_test_app/testapp/Dockerfile +23 -0
  16. data/examples/rails_test_app/testapp/Gemfile +17 -0
  17. data/examples/rails_test_app/testapp/README.md +65 -0
  18. data/examples/rails_test_app/testapp/Rakefile +6 -0
  19. data/examples/rails_test_app/testapp/app/assets/images/.keep +0 -0
  20. data/examples/rails_test_app/testapp/app/assets/stylesheets/application.css +1 -0
  21. data/examples/rails_test_app/testapp/app/controllers/application_controller.rb +4 -0
  22. data/examples/rails_test_app/testapp/app/controllers/concerns/.keep +0 -0
  23. data/examples/rails_test_app/testapp/app/controllers/dashboard_controller.rb +38 -0
  24. data/examples/rails_test_app/testapp/app/controllers/health_controller.rb +11 -0
  25. data/examples/rails_test_app/testapp/app/controllers/orders_controller.rb +100 -0
  26. data/examples/rails_test_app/testapp/app/controllers/posts_controller.rb +82 -0
  27. data/examples/rails_test_app/testapp/app/controllers/sessions_controller.rb +25 -0
  28. data/examples/rails_test_app/testapp/app/controllers/users_controller.rb +44 -0
  29. data/examples/rails_test_app/testapp/app/helpers/application_helper.rb +2 -0
  30. data/examples/rails_test_app/testapp/app/models/application_record.rb +3 -0
  31. data/examples/rails_test_app/testapp/app/models/comment.rb +8 -0
  32. data/examples/rails_test_app/testapp/app/models/concerns/.keep +0 -0
  33. data/examples/rails_test_app/testapp/app/models/order.rb +56 -0
  34. data/examples/rails_test_app/testapp/app/models/order_item.rb +16 -0
  35. data/examples/rails_test_app/testapp/app/models/post.rb +29 -0
  36. data/examples/rails_test_app/testapp/app/models/user.rb +34 -0
  37. data/examples/rails_test_app/testapp/app/services/order_report_service.rb +40 -0
  38. data/examples/rails_test_app/testapp/app/views/layouts/application.html.erb +28 -0
  39. data/examples/rails_test_app/testapp/app/views/pwa/manifest.json.erb +22 -0
  40. data/examples/rails_test_app/testapp/app/views/pwa/service-worker.js +26 -0
  41. data/examples/rails_test_app/testapp/bin/ci +6 -0
  42. data/examples/rails_test_app/testapp/bin/dev +2 -0
  43. data/examples/rails_test_app/testapp/bin/rails +4 -0
  44. data/examples/rails_test_app/testapp/bin/rake +4 -0
  45. data/examples/rails_test_app/testapp/bin/setup +35 -0
  46. data/examples/rails_test_app/testapp/config/application.rb +42 -0
  47. data/examples/rails_test_app/testapp/config/boot.rb +3 -0
  48. data/examples/rails_test_app/testapp/config/ci.rb +14 -0
  49. data/examples/rails_test_app/testapp/config/database.yml +32 -0
  50. data/examples/rails_test_app/testapp/config/environment.rb +5 -0
  51. data/examples/rails_test_app/testapp/config/environments/development.rb +54 -0
  52. data/examples/rails_test_app/testapp/config/environments/production.rb +67 -0
  53. data/examples/rails_test_app/testapp/config/environments/test.rb +42 -0
  54. data/examples/rails_test_app/testapp/config/initializers/content_security_policy.rb +29 -0
  55. data/examples/rails_test_app/testapp/config/initializers/filter_parameter_logging.rb +8 -0
  56. data/examples/rails_test_app/testapp/config/initializers/inflections.rb +16 -0
  57. data/examples/rails_test_app/testapp/config/locales/en.yml +31 -0
  58. data/examples/rails_test_app/testapp/config/puma.rb +39 -0
  59. data/examples/rails_test_app/testapp/config/routes.rb +34 -0
  60. data/examples/rails_test_app/testapp/config.ru +6 -0
  61. data/examples/rails_test_app/testapp/db/migrate/20260216002916_create_users.rb +12 -0
  62. data/examples/rails_test_app/testapp/db/migrate/20260216002919_create_posts.rb +13 -0
  63. data/examples/rails_test_app/testapp/db/migrate/20260216002922_create_comments.rb +11 -0
  64. data/examples/rails_test_app/testapp/db/migrate/20260222000001_create_orders.rb +14 -0
  65. data/examples/rails_test_app/testapp/db/migrate/20260222000002_create_order_items.rb +13 -0
  66. data/examples/rails_test_app/testapp/db/schema.rb +71 -0
  67. data/examples/rails_test_app/testapp/db/seeds.rb +85 -0
  68. data/examples/rails_test_app/testapp/docker-compose.yml +21 -0
  69. data/examples/rails_test_app/testapp/docker-entrypoint.sh +10 -0
  70. data/examples/rails_test_app/testapp/lib/tasks/.keep +0 -0
  71. data/examples/rails_test_app/testapp/log/.keep +0 -0
  72. data/examples/rails_test_app/testapp/public/400.html +135 -0
  73. data/examples/rails_test_app/testapp/public/404.html +135 -0
  74. data/examples/rails_test_app/testapp/public/406-unsupported-browser.html +135 -0
  75. data/examples/rails_test_app/testapp/public/422.html +135 -0
  76. data/examples/rails_test_app/testapp/public/500.html +135 -0
  77. data/examples/rails_test_app/testapp/public/icon.png +0 -0
  78. data/examples/rails_test_app/testapp/public/icon.svg +3 -0
  79. data/examples/rails_test_app/testapp/public/robots.txt +1 -0
  80. data/examples/rails_test_app/testapp/script/.keep +0 -0
  81. data/examples/rails_test_app/testapp/storage/.keep +0 -0
  82. data/examples/rails_test_app/testapp/tmp/.keep +0 -0
  83. data/examples/rails_test_app/testapp/tmp/pids/.keep +0 -0
  84. data/examples/rails_test_app/testapp/tmp/storage/.keep +0 -0
  85. data/examples/rails_test_app/testapp/vendor/.keep +0 -0
  86. data/exe/debug-mcp +39 -0
  87. data/exe/debug-rails +127 -0
  88. data/lib/debug_mcp/client_cleanup.rb +102 -0
  89. data/lib/debug_mcp/code_safety_analyzer.rb +124 -0
  90. data/lib/debug_mcp/debug_client.rb +1143 -0
  91. data/lib/debug_mcp/exit_message_builder.rb +112 -0
  92. data/lib/debug_mcp/pending_http_helper.rb +25 -0
  93. data/lib/debug_mcp/rails_helper.rb +155 -0
  94. data/lib/debug_mcp/server.rb +364 -0
  95. data/lib/debug_mcp/session_manager.rb +436 -0
  96. data/lib/debug_mcp/stop_event_annotator.rb +152 -0
  97. data/lib/debug_mcp/tcp_session_discovery.rb +226 -0
  98. data/lib/debug_mcp/tools/connect.rb +669 -0
  99. data/lib/debug_mcp/tools/continue_execution.rb +161 -0
  100. data/lib/debug_mcp/tools/disconnect.rb +169 -0
  101. data/lib/debug_mcp/tools/evaluate_code.rb +354 -0
  102. data/lib/debug_mcp/tools/finish.rb +84 -0
  103. data/lib/debug_mcp/tools/get_context.rb +217 -0
  104. data/lib/debug_mcp/tools/get_source.rb +193 -0
  105. data/lib/debug_mcp/tools/inspect_object.rb +107 -0
  106. data/lib/debug_mcp/tools/list_debug_sessions.rb +60 -0
  107. data/lib/debug_mcp/tools/list_files.rb +189 -0
  108. data/lib/debug_mcp/tools/list_paused_sessions.rb +108 -0
  109. data/lib/debug_mcp/tools/next.rb +70 -0
  110. data/lib/debug_mcp/tools/rails_info.rb +200 -0
  111. data/lib/debug_mcp/tools/rails_model.rb +362 -0
  112. data/lib/debug_mcp/tools/rails_routes.rb +186 -0
  113. data/lib/debug_mcp/tools/read_file.rb +214 -0
  114. data/lib/debug_mcp/tools/remove_breakpoint.rb +173 -0
  115. data/lib/debug_mcp/tools/run_debug_command.rb +55 -0
  116. data/lib/debug_mcp/tools/run_script.rb +293 -0
  117. data/lib/debug_mcp/tools/set_breakpoint.rb +206 -0
  118. data/lib/debug_mcp/tools/step.rb +67 -0
  119. data/lib/debug_mcp/tools/trigger_request.rb +515 -0
  120. data/lib/debug_mcp/version.rb +5 -0
  121. data/lib/debug_mcp.rb +40 -0
  122. metadata +251 -0
@@ -0,0 +1,100 @@
1
+ class OrdersController < ApplicationController
2
+ # GET /orders
3
+ def index
4
+ @orders = Order.includes(:user, :order_items).order(created_at: :desc)
5
+ render json: @orders.map { |o| order_json(o) }
6
+ end
7
+
8
+ # GET /orders/:id
9
+ # Scenario 1: total_cents may be off by 1 cent due to floating point truncation
10
+ def show
11
+ @order = Order.includes(:order_items).find(params[:id])
12
+ render json: order_json(@order).merge(
13
+ items: @order.order_items.map { |item| item_json(item) }
14
+ )
15
+ end
16
+
17
+ # POST /orders
18
+ def create
19
+ @order = Order.new(order_params)
20
+ if @order.save
21
+ render json: order_json(@order), status: :created
22
+ else
23
+ render json: { errors: @order.errors.full_messages }, status: :unprocessable_entity
24
+ end
25
+ end
26
+
27
+ # PATCH /orders/:id
28
+ # Scenario 2: updating a cancelled order triggers recalculate_total
29
+ # which computes total from zero-quantity items, resulting in total_cents = 0
30
+ def update
31
+ @order = Order.unscoped.find(params[:id])
32
+ if @order.update(order_params)
33
+ render json: order_json(@order)
34
+ else
35
+ render json: { errors: @order.errors.full_messages }, status: :unprocessable_entity
36
+ end
37
+ end
38
+
39
+ # POST /orders/:id/cancel
40
+ # Scenario 2: cancelling zeroes item quantities via after_save callback
41
+ def cancel
42
+ @order = Order.find(params[:id])
43
+ @order.status = :cancelled
44
+ if @order.save
45
+ render json: { message: "Order ##{@order.id} cancelled", order: order_json(@order) }
46
+ else
47
+ render json: { errors: @order.errors.full_messages }, status: :unprocessable_entity
48
+ end
49
+ end
50
+
51
+ # GET /orders/user_orders?user_id=3&status=cancelled
52
+ # Scenario 4: default_scope creates contradictory SQL
53
+ # WHERE status != 3 AND status = 3 -> always empty
54
+ def user_orders
55
+ orders = Order.where(user_id: params[:user_id])
56
+ orders = orders.where(status: params[:status]) if params[:status].present?
57
+ render json: {
58
+ user_id: params[:user_id],
59
+ status_filter: params[:status],
60
+ count: orders.count,
61
+ orders: orders.map { |o| order_json(o) }
62
+ }
63
+ end
64
+
65
+ # GET /orders/report
66
+ # Scenario 3: report drops orders with deleted users silently
67
+ def report
68
+ render json: OrderReportService.generate
69
+ end
70
+
71
+ private
72
+
73
+ def order_params
74
+ params.permit(:user_id, :status, :discount_code, :discount_percent)
75
+ end
76
+
77
+ def order_json(order)
78
+ {
79
+ id: order.id,
80
+ user_id: order.user_id,
81
+ user_name: order.user&.name,
82
+ status: order.status,
83
+ total_cents: order.total_cents,
84
+ discount_code: order.discount_code,
85
+ discount_percent: order.discount_percent,
86
+ completed_at: order.completed_at,
87
+ items_count: order.order_items.size
88
+ }
89
+ end
90
+
91
+ def item_json(item)
92
+ {
93
+ id: item.id,
94
+ product_name: item.product_name,
95
+ quantity: item.quantity,
96
+ unit_price: item.unit_price,
97
+ tax_rate: item.tax_rate
98
+ }
99
+ end
100
+ end
@@ -0,0 +1,82 @@
1
+ class PostsController < ApplicationController
2
+ before_action :set_post, only: [:show, :update]
3
+
4
+ def index
5
+ @posts = Post.published.includes(:user).recent
6
+ render json: @posts.as_json(include: { user: { only: [:id, :name] } })
7
+ end
8
+
9
+ def show
10
+ render json: @post.as_json(
11
+ include: {
12
+ user: { only: [:id, :name] },
13
+ comments: { include: { user: { only: [:id, :name] } } }
14
+ }
15
+ )
16
+ end
17
+
18
+ def create
19
+ @post = Post.new(post_params)
20
+ if @post.save
21
+ render json: @post, status: :created
22
+ else
23
+ render json: { errors: @post.errors.full_messages }, status: :unprocessable_entity
24
+ end
25
+ end
26
+
27
+ def update
28
+ if @post.update(post_params)
29
+ render json: @post
30
+ else
31
+ render json: { errors: @post.errors.full_messages }, status: :unprocessable_entity
32
+ end
33
+ end
34
+
35
+ # 人気記事の取得
36
+ def trending
37
+ posts = Post.trending
38
+ render json: posts.map { |post|
39
+ {
40
+ id: post.id,
41
+ title: post.title,
42
+ author: post.user.name,
43
+ comments_count: post.comments.size
44
+ }
45
+ }
46
+ end
47
+
48
+ # 検索エンドポイント(公開済み記事のみ対象)
49
+ def search
50
+ query = params[:q].to_s
51
+ scope = Post.published
52
+
53
+ # ユーザーでフィルタリング
54
+ if params[:user_id].present?
55
+ scope = Post.by_user(params[:user_id])
56
+ end
57
+
58
+ scope = scope.where("title LIKE ?", "%#{query}%") if query.present?
59
+
60
+ results = scope.map do |post|
61
+ {
62
+ id: post.id,
63
+ title: post.title,
64
+ author: post.user.name,
65
+ status: post.status,
66
+ comments_count: post.comments.count
67
+ }
68
+ end
69
+
70
+ render json: results
71
+ end
72
+
73
+ private
74
+
75
+ def set_post
76
+ @post = Post.find(params[:id])
77
+ end
78
+
79
+ def post_params
80
+ params.permit(:title, :body, :status, :user_id, :published_at)
81
+ end
82
+ end
@@ -0,0 +1,25 @@
1
+ class SessionsController < ApplicationController
2
+ def create
3
+ user = User.find_by(email: params[:email])
4
+ if user
5
+ session[:user_id] = user.id
6
+ render json: { message: "Logged in", user: user.as_json(only: [:id, :name, :email, :role]) }
7
+ else
8
+ render json: { error: "Invalid email" }, status: :unauthorized
9
+ end
10
+ end
11
+
12
+ def show
13
+ if session[:user_id]
14
+ user = User.find(session[:user_id])
15
+ render json: { logged_in: true, user: user.as_json(only: [:id, :name, :email, :role]) }
16
+ else
17
+ render json: { logged_in: false }
18
+ end
19
+ end
20
+
21
+ def destroy
22
+ session.delete(:user_id)
23
+ render json: { message: "Logged out" }
24
+ end
25
+ end
@@ -0,0 +1,44 @@
1
+ class UsersController < ApplicationController
2
+ before_action :set_user, only: [:show, :update, :destroy]
3
+
4
+ def index
5
+ @users = User.all
6
+ render json: @users
7
+ end
8
+
9
+ def show
10
+ render json: @user.as_json(include: { posts: { only: [:id, :title, :status] } })
11
+ end
12
+
13
+ def create
14
+ @user = User.new(user_params)
15
+ if @user.save
16
+ render json: @user, status: :created
17
+ else
18
+ render json: { errors: @user.errors.full_messages }, status: :unprocessable_entity
19
+ end
20
+ end
21
+
22
+ def update
23
+ if @user.update(user_params)
24
+ render json: @user
25
+ else
26
+ render json: { errors: @user.errors.full_messages }, status: :unprocessable_entity
27
+ end
28
+ end
29
+
30
+ def destroy
31
+ @user.destroy
32
+ head :no_content
33
+ end
34
+
35
+ private
36
+
37
+ def set_user
38
+ @user = User.find(params[:id])
39
+ end
40
+
41
+ def user_params
42
+ params.permit(:name, :email, :role, :active)
43
+ end
44
+ end
@@ -0,0 +1,2 @@
1
+ module ApplicationHelper
2
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ primary_abstract_class
3
+ end
@@ -0,0 +1,8 @@
1
+ class Comment < ApplicationRecord
2
+ belongs_to :user
3
+ belongs_to :post
4
+
5
+ validates :body, presence: true, length: { minimum: 3 }
6
+
7
+ scope :recent, -> { order(created_at: :desc) }
8
+ end
@@ -0,0 +1,56 @@
1
+ class Order < ApplicationRecord
2
+ default_scope { where.not(status: :cancelled) }
3
+
4
+ belongs_to :user, optional: true
5
+ has_many :order_items, dependent: :destroy
6
+
7
+ enum :status, { cart: 0, pending: 1, completed: 2, cancelled: 3 }
8
+
9
+ before_save :recalculate_total
10
+ after_save :archive_items_if_cancelled
11
+
12
+ validates :status, presence: true
13
+
14
+ scope :completed, -> { where(status: :completed) }
15
+
16
+ def self.revenue_stats
17
+ {
18
+ total_revenue: completed.sum(:total_cents),
19
+ average_order: completed.average(:total_cents)&.round || 0,
20
+ order_count: completed.count
21
+ }
22
+ end
23
+
24
+ private
25
+
26
+ # Scenario 1:
27
+ def recalculate_total
28
+ return if order_items.empty?
29
+
30
+ # SQL SUM returns BigDecimal
31
+ subtotal = order_items.sum("quantity * unit_price")
32
+
33
+ # Ruby Enumerable#sum with .to_f produces Float
34
+ tax_total = order_items.to_a.sum do |item|
35
+ item.quantity * item.unit_price.to_f * item.tax_rate.to_f
36
+ end
37
+
38
+ computed_total = subtotal + tax_total
39
+
40
+ if discount_percent.to_f > 0
41
+ computed_total = computed_total * (100 - discount_percent.to_f) / 100
42
+ end
43
+
44
+ # Bug: .to_i truncates instead of .round, losing up to 1 cent
45
+ self.total_cents = (computed_total * 100).to_i
46
+ end
47
+
48
+ # Scenario 2: zeroes item quantities on cancel
49
+ # This creates a "time bomb": any subsequent save triggers recalculate_total
50
+ # which recomputes total from zero-quantity items, resulting in total_cents = 0
51
+ def archive_items_if_cancelled
52
+ if saved_change_to_status? && cancelled?
53
+ order_items.update_all(quantity: 0)
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,16 @@
1
+ class OrderItem < ApplicationRecord
2
+ belongs_to :order
3
+
4
+ validates :product_name, presence: true
5
+ validates :quantity, presence: true, numericality: { greater_than_or_equal_to: 0 }
6
+ validates :unit_price, presence: true, numericality: { greater_than_or_equal_to: 0 }
7
+ validates :tax_rate, presence: true, numericality: { greater_than_or_equal_to: 0 }
8
+
9
+ def line_total
10
+ quantity * unit_price
11
+ end
12
+
13
+ def tax_amount
14
+ line_total * tax_rate
15
+ end
16
+ end
@@ -0,0 +1,29 @@
1
+ class Post < ApplicationRecord
2
+ belongs_to :user
3
+ has_many :comments, dependent: :destroy
4
+
5
+ validates :title, presence: true, length: { maximum: 100 }
6
+ validates :body, presence: true
7
+
8
+ enum :status, { draft: 0, published: 1, archived: 2 }
9
+
10
+ scope :published, -> { where(status: :published) }
11
+ scope :drafts, -> { where(status: :draft) }
12
+ scope :recent, -> { order(published_at: :desc) }
13
+ scope :by_user, ->(user_id) { where(user_id: user_id) }
14
+
15
+ # コメント数の多い人気記事を取得(パフォーマンス向上のためメモ化)
16
+ def self.trending(limit = 5)
17
+ @trending_posts ||= published
18
+ .left_joins(:comments)
19
+ .group(:id)
20
+ .order("COUNT(comments.id) DESC")
21
+ .limit(limit)
22
+ .includes(:user)
23
+ .to_a
24
+ end
25
+
26
+ def summary
27
+ body.to_s.truncate(100)
28
+ end
29
+ end
@@ -0,0 +1,34 @@
1
+ class User < ApplicationRecord
2
+ has_many :posts, dependent: :destroy
3
+ has_many :comments, dependent: :destroy
4
+ has_many :orders
5
+
6
+ validates :name, presence: true, length: { maximum: 50 }
7
+ validates :email, presence: true, uniqueness: true,
8
+ format: { with: /\A[^@\s]+@[^@\s]+\z/ }
9
+
10
+ enum :role, { guest: 0, member: 1, editor: 2, admin: 3 }
11
+
12
+ scope :active, -> { where(active: true) }
13
+ scope :admins, -> { where(role: :admin) }
14
+ scope :recent, -> { order(created_at: :desc) }
15
+
16
+ # NULLロールのユーザーが作られないようにデフォルトを設定
17
+ before_save :ensure_default_role
18
+
19
+ def display_name
20
+ "#{name} (#{role})"
21
+ end
22
+
23
+ # 管理者のみが他ユーザーの情報を編集可能
24
+ def editable_by?(editor)
25
+ editor.admin? || editor == self
26
+ end
27
+
28
+ private
29
+
30
+ def ensure_default_role
31
+ # role_before_type_castで内部整数値を確認し、未設定なら初期値を設定
32
+ self.role = :member if role_before_type_cast.nil? || role_before_type_cast == 0
33
+ end
34
+ end
@@ -0,0 +1,40 @@
1
+ class OrderReportService
2
+ # Scenario 3: silent exception swallowing causes data loss
3
+ # When order.user is nil (deleted user), order.user.name raises NoMethodError
4
+ # The rescue => e catches it and drops the order from the report
5
+ # This causes summary.total_orders != orders.length
6
+
7
+ def self.generate
8
+ orders = Order.completed.includes(:user, :order_items)
9
+
10
+ report = {
11
+ generated_at: Time.current,
12
+ summary: {
13
+ total_orders: orders.count,
14
+ total_revenue: orders.sum(:total_cents)
15
+ },
16
+ orders: []
17
+ }
18
+
19
+ orders.each do |order|
20
+ serialized = serialize_order(order)
21
+ report[:orders] << serialized if serialized
22
+ end
23
+
24
+ report
25
+ end
26
+
27
+ def self.serialize_order(order)
28
+ {
29
+ id: order.id,
30
+ customer: order.user.name,
31
+ total_cents: order.total_cents,
32
+ items_count: order.order_items.size,
33
+ completed_at: order.completed_at
34
+ }
35
+ rescue => e
36
+ Rails.logger.warn("Failed to serialize order ##{order.id}: #{e.message}")
37
+ nil
38
+ end
39
+ private_class_method :serialize_order
40
+ end
@@ -0,0 +1,28 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title><%= content_for(:title) || "Testapp" %></title>
5
+ <meta name="viewport" content="width=device-width,initial-scale=1">
6
+ <meta name="apple-mobile-web-app-capable" content="yes">
7
+ <meta name="application-name" content="Testapp">
8
+ <meta name="mobile-web-app-capable" content="yes">
9
+ <%= csrf_meta_tags %>
10
+ <%= csp_meta_tag %>
11
+
12
+ <%= yield :head %>
13
+
14
+ <%# Enable PWA manifest for installable apps (make sure to enable in config/routes.rb too!) %>
15
+ <%#= tag.link rel: "manifest", href: pwa_manifest_path(format: :json) %>
16
+
17
+ <link rel="icon" href="/icon.png" type="image/png">
18
+ <link rel="icon" href="/icon.svg" type="image/svg+xml">
19
+ <link rel="apple-touch-icon" href="/icon.png">
20
+
21
+ <%# Includes all stylesheet files in app/assets/stylesheets %>
22
+ <%= stylesheet_link_tag "application" %>
23
+ </head>
24
+
25
+ <body>
26
+ <%= yield %>
27
+ </body>
28
+ </html>
@@ -0,0 +1,22 @@
1
+ {
2
+ "name": "Testapp",
3
+ "icons": [
4
+ {
5
+ "src": "/icon.png",
6
+ "type": "image/png",
7
+ "sizes": "512x512"
8
+ },
9
+ {
10
+ "src": "/icon.png",
11
+ "type": "image/png",
12
+ "sizes": "512x512",
13
+ "purpose": "maskable"
14
+ }
15
+ ],
16
+ "start_url": "/",
17
+ "display": "standalone",
18
+ "scope": "/",
19
+ "description": "Testapp.",
20
+ "theme_color": "red",
21
+ "background_color": "red"
22
+ }
@@ -0,0 +1,26 @@
1
+ // Add a service worker for processing Web Push notifications:
2
+ //
3
+ // self.addEventListener("push", async (event) => {
4
+ // const { title, options } = await event.data.json()
5
+ // event.waitUntil(self.registration.showNotification(title, options))
6
+ // })
7
+ //
8
+ // self.addEventListener("notificationclick", function(event) {
9
+ // event.notification.close()
10
+ // event.waitUntil(
11
+ // clients.matchAll({ type: "window" }).then((clientList) => {
12
+ // for (let i = 0; i < clientList.length; i++) {
13
+ // let client = clientList[i]
14
+ // let clientPath = (new URL(client.url)).pathname
15
+ //
16
+ // if (clientPath == event.notification.data.path && "focus" in client) {
17
+ // return client.focus()
18
+ // }
19
+ // }
20
+ //
21
+ // if (clients.openWindow) {
22
+ // return clients.openWindow(event.notification.data.path)
23
+ // }
24
+ // })
25
+ // )
26
+ // })
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "active_support/continuous_integration"
4
+
5
+ CI = ActiveSupport::ContinuousIntegration
6
+ require_relative "../config/ci.rb"
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ exec "./bin/rails", "server", *ARGV
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path("../config/application", __dir__)
3
+ require_relative "../config/boot"
4
+ require "rails/commands"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "rake"
4
+ Rake.application.run
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
3
+
4
+ APP_ROOT = File.expand_path("..", __dir__)
5
+
6
+ def system!(*args)
7
+ system(*args, exception: true)
8
+ end
9
+
10
+ FileUtils.chdir APP_ROOT do
11
+ # This script is a way to set up or update your development environment automatically.
12
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
13
+ # Add necessary setup steps to this file.
14
+
15
+ puts "== Installing dependencies =="
16
+ system("bundle check") || system!("bundle install")
17
+
18
+ # puts "\n== Copying sample files =="
19
+ # unless File.exist?("config/database.yml")
20
+ # FileUtils.cp "config/database.yml.sample", "config/database.yml"
21
+ # end
22
+
23
+ puts "\n== Preparing database =="
24
+ system! "bin/rails db:prepare"
25
+ system! "bin/rails db:reset" if ARGV.include?("--reset")
26
+
27
+ puts "\n== Removing old logs and tempfiles =="
28
+ system! "bin/rails log:clear tmp:clear"
29
+
30
+ unless ARGV.include?("--skip-server")
31
+ puts "\n== Starting development server =="
32
+ STDOUT.flush # flush the output before exec(2) so that it displays
33
+ exec "bin/dev"
34
+ end
35
+ end
@@ -0,0 +1,42 @@
1
+ require_relative "boot"
2
+
3
+ require "rails"
4
+ # Pick the frameworks you want:
5
+ require "active_model/railtie"
6
+ # require "active_job/railtie"
7
+ require "active_record/railtie"
8
+ # require "active_storage/engine"
9
+ require "action_controller/railtie"
10
+ # require "action_mailer/railtie"
11
+ # require "action_mailbox/engine"
12
+ # require "action_text/engine"
13
+ require "action_view/railtie"
14
+ # require "action_cable/engine"
15
+ # require "rails/test_unit/railtie"
16
+
17
+ # Require the gems listed in Gemfile, including any gems
18
+ # you've limited to :test, :development, or :production.
19
+ Bundler.require(*Rails.groups)
20
+
21
+ module Testapp
22
+ class Application < Rails::Application
23
+ # Initialize configuration defaults for originally generated Rails version.
24
+ config.load_defaults 8.1
25
+
26
+ # Please, add to the `ignore` list any other `lib` subdirectories that do
27
+ # not contain `.rb` files, or that should not be reloaded or eager loaded.
28
+ # Common ones are `templates`, `generators`, or `middleware`, for example.
29
+ config.autoload_lib(ignore: %w[assets tasks])
30
+
31
+ # Configuration for the application, engines, and railties goes here.
32
+ #
33
+ # These settings can be overridden in specific environments using the files
34
+ # in config/environments, which are processed later.
35
+ #
36
+ # config.time_zone = "Central Time (US & Canada)"
37
+ # config.eager_load_paths << Rails.root.join("extras")
38
+
39
+ # Don't generate system test files.
40
+ config.generators.system_tests = nil
41
+ end
42
+ end
@@ -0,0 +1,3 @@
1
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
2
+
3
+ require "bundler/setup" # Set up gems listed in the Gemfile.
@@ -0,0 +1,14 @@
1
+ # Run using bin/ci
2
+
3
+ CI.run do
4
+ step "Setup", "bin/setup --skip-server"
5
+
6
+
7
+ # Optional: set a green GitHub commit status to unblock PR merge.
8
+ # Requires the `gh` CLI and `gh extension install basecamp/gh-signoff`.
9
+ # if success?
10
+ # step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff"
11
+ # else
12
+ # failure "Signoff: CI failed. Do not merge or deploy.", "Fix the issues and try again."
13
+ # end
14
+ end