Class: Tng::Services::FileTypeDetector

Inherits:
Object
  • Object
show all
Defined in:
lib/tng/services/file_type_detector.rb

Constant Summary collapse

TYPE_PATTERNS =
{
  /app\/controllers\// => 'controller',
  /app\/models\// => 'model',
  /app\/services?\// => 'service',
  /app\/jobs\// => 'job',
  /app\/helpers\// => 'helper',
  /(?:app\/)?libs?\// => 'lib',
  /app\/mailers\// => 'mailer',
  /app\/channels\// => 'channel',
  /app\/decorators\// => 'decorator',
  /app\/presenters\// => 'presenter',
  /app\/serializers\// => 'serializer',
  /app\/policies\// => 'policy',
  /app\/forms\// => 'form',
  /app\/queries\// => 'query',
  /app\/graphql\/resolvers\// => 'resolver',
  /app\/graphql\/types\// => 'type',
  /app\/graphql\/mutations\// => 'mutation',
  /app\/graphql\/loaders\// => 'loader',
  /app\/graphql\/schemas\// => 'schema',
  /app\/graphql\// => 'graphql'
}.freeze
PATH_MAPPINGS =
{
  'controller' => 'app/controllers',
  'model' => 'app/models',
  'service' => 'app/services?',
  'job' => 'app/jobs',
  'helper' => 'app/helpers',
  'lib' => 'lib',
  'mailer' => 'app/mailers',
  'channel' => 'app/channels',
  'decorator' => 'app/decorators',
  'presenter' => 'app/presenters',
  'serializer' => 'app/serializers',
  'policy' => 'app/policies',
  'form' => 'app/forms',
  'query' => 'app/queries',
  'resolver' => 'app/graphql/resolvers',
  'type' => 'app/graphql/types',
  'mutation' => 'app/graphql/mutations',
  'loader' => 'app/graphql/loaders',
  'schema' => 'app/graphql/schemas',
  'graphql' => 'app/graphql'
}.freeze
SEARCH_PATHS =
%w[
  app/controllers app/models app/services app/service
  app/jobs app/helpers app/mailers app/channels
  app/decorators app/presenters app/serializers
  app/policies app/forms app/queries app/graphql
  lib app/lib
].freeze

Class Method Summary collapse

Class Method Details

.build_file_object(file_path, type) ⇒ Object



163
164
165
166
167
168
169
170
171
172
173
# File 'lib/tng/services/file_type_detector.rb', line 163

def build_file_object(file_path, type)
  actual_file_path = File.expand_path(file_path)
  relative_path = extract_relative_path(actual_file_path, type)
  namespaced_name = relative_path.split('/').map(&:camelize).join('::')

  {
    name: namespaced_name,
    path: actual_file_path,
    type: type
  }
end

.candidate_paths_for(file_name) ⇒ Object



81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# File 'lib/tng/services/file_type_detector.rb', line 81

def candidate_paths_for(file_name)
  file_with_ext = file_name.end_with?('.rb') ? file_name : "#{file_name}.rb"
  candidates = []

  if File.exist?(file_with_ext)
    candidates << File.expand_path(file_with_ext)
  end

  unless Tng::Utils.rails_project?(Dir.pwd)
    root = Dir.pwd
    files = Tng::Services::FileDiscovery.list_ruby_files(root)
    files.each do |file|
      candidates << file if File.basename(file) == File.basename(file_with_ext)
    end
    return candidates.uniq
  end

  rails_root = defined?(::Rails) && ::Rails.root ? ::Rails.root.to_s : Dir.pwd

  SEARCH_PATHS.each do |dir|
    full_path = File.join(rails_root, dir, file_with_ext)
    candidates << full_path if File.exist?(full_path)

    candidates.concat(Dir.glob(File.join(rails_root, dir, '**', file_with_ext)))
  end

  candidates.uniq
end

.detect_type(file_path) ⇒ Object



61
62
63
64
65
66
# File 'lib/tng/services/file_type_detector.rb', line 61

def detect_type(file_path)
  return "other" unless Tng::Utils.rails_project?(Dir.pwd)

  normalized_path = normalize_path(file_path)
  TYPE_PATTERNS.find { |pattern, _| normalized_path.match?(pattern) }&.last || 'other'
end

.extract_relative_path(file_path, type) ⇒ Object



155
156
157
158
159
160
161
# File 'lib/tng/services/file_type_detector.rb', line 155

def extract_relative_path(file_path, type)
  base_path = PATH_MAPPINGS[type]
  return File.basename(file_path, '.rb') unless base_path

  pattern = base_path == 'lib' ? %r{^.*/#{base_path}/} : %r{^.*#{base_path}/}
  file_path.gsub(pattern, '').gsub('.rb', '')
end

.find_file_in_project(file_name) ⇒ Object



110
111
112
# File 'lib/tng/services/file_type_detector.rb', line 110

def find_file_in_project(file_name)
  candidate_paths_for(file_name).find { |candidate| !Tng::Utils.ignored_path?(candidate) }
end

.find_ignored_in_project(file_name) ⇒ Object



114
115
116
# File 'lib/tng/services/file_type_detector.rb', line 114

def find_ignored_in_project(file_name)
  candidate_paths_for(file_name).find { |candidate| Tng::Utils.ignored_path?(candidate) }
end

.normalize_path(file_path) ⇒ Object



68
69
70
71
# File 'lib/tng/services/file_type_detector.rb', line 68

def normalize_path(file_path)
  path = file_path.sub(/\.rb$/, '')
  path.start_with?('/') ? path : (find_file_in_project(path) || path)
end

.resolve_file_path(file_path) ⇒ Object



118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
# File 'lib/tng/services/file_type_detector.rb', line 118

def resolve_file_path(file_path)
  if file_path.start_with?('/')
    candidates = []
    candidates << file_path if File.exist?(file_path)
    candidates << "#{file_path}.rb" if File.exist?("#{file_path}.rb")

    ignored_candidate = nil
    candidates.each do |candidate|
      expanded = File.expand_path(candidate)
      if Tng::Utils.ignored_path?(expanded)
        ignored_candidate ||= expanded
        next
      end

      return { path: expanded, ignored: false }
    end

    return { path: ignored_candidate, ignored: true } if ignored_candidate
    return nil
  end

  ignored_candidate = nil
  candidate_paths_for(file_path).each do |candidate|
    expanded = File.expand_path(candidate)
    if Tng::Utils.ignored_path?(expanded)
      ignored_candidate ||= expanded
      next
    end

    return { path: expanded, ignored: false }
  end

  return { path: ignored_candidate, ignored: true } if ignored_candidate

  nil
end