Module: Mbeditor::RailsRelatedFilesService

Defined in:
app/services/mbeditor/rails_related_files_service.rb

Overview

Given a workspace-relative file path, finds related Rails files grouped by type: controller, model, views, helper, tests, and custom directories.

Only groups with at least one existing file are included in the result. All returned paths are workspace-relative strings.

Class Method Summary collapse

Class Method Details

.find(workspace_root, relative_path, custom_paths: []) ⇒ Object

Returns a hash of groups. Example:

{
  controller: [{path: "app/controllers/users_controller.rb", name: "users_controller.rb"}],
  model:      [{path: "app/models/user.rb",                  name: "user.rb"}],
  views:      [{path: "app/views/users/index.html.erb",      name: "index.html.erb"}],
  helper:     [{path: "app/helpers/users_helper.rb",         name: "users_helper.rb"}],
  tests:      [{path: "test/controllers/users_controller_test.rb", name: "users_controller_test.rb"}],
  custom:     {"app/assets/javascripts/app" => [{path: "...", name: "..."}]}
}

Returns {} when the path does not match any known Rails convention.



24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
# File 'app/services/mbeditor/rails_related_files_service.rb', line 24

def find(workspace_root, relative_path, custom_paths: [])
  plural, singular = extract_resource_names(relative_path)
  return {} unless plural && singular

  result = {}

  # ── controller ────────────────────────────────────────────────────────────
  controller_path = "app/controllers/#{plural}_controller.rb"
  if file_exists?(workspace_root, controller_path)
    result[:controller] = [entry(controller_path)]
  end

  # ── model ─────────────────────────────────────────────────────────────────
  model_path = "app/models/#{singular}.rb"
  if file_exists?(workspace_root, model_path)
    result[:model] = [entry(model_path)]
  end

  # ── views ─────────────────────────────────────────────────────────────────
  views_dir = File.join(workspace_root, "app", "views", plural)
  if File.directory?(views_dir)
    children = dir_children(workspace_root, "app/views/#{plural}")
    result[:views] = children unless children.empty?
  end

  # ── helper ────────────────────────────────────────────────────────────────
  helper_path = "app/helpers/#{plural}_helper.rb"
  if file_exists?(workspace_root, helper_path)
    result[:helper] = [entry(helper_path)]
  end

  # ── tests ─────────────────────────────────────────────────────────────────
  test_candidates = [
    "test/controllers/#{plural}_controller_test.rb",
    "test/models/#{singular}_test.rb",
    "spec/controllers/#{plural}_controller_spec.rb",
    "spec/models/#{singular}_spec.rb"
  ]
  tests = test_candidates.select { |p| file_exists?(workspace_root, p) }.map { |p| entry(p) }
  result[:tests] = tests unless tests.empty?

  # ── custom paths ──────────────────────────────────────────────────────────
  custom_result = {}
  Array(custom_paths).each do |base|
    base = base.to_s.strip
    next if base.empty?

    [plural, singular].uniq.each do |name|
      rel_dir = "#{base}/#{name}"
      abs_dir = File.join(workspace_root, rel_dir)
      next unless File.directory?(abs_dir)

      begin
        real_ws = File.realpath(workspace_root)
        real_dir = File.realpath(abs_dir)
        next unless real_dir.start_with?("#{real_ws}/") || real_dir == real_ws
      rescue Errno::ENOENT, Errno::EACCES
        next
      end

      children = dir_children(workspace_root, rel_dir)
      next if children.empty?

      custom_result[base] ||= []
      custom_result[base].concat(children)
    end
  end
  result[:custom] = custom_result unless custom_result.empty?

  result
end