module RuboCop::Cop::EngineApi

Functionality for reading Rails Engine API declaration files.

Constants

API_FILE_DETAILS

Public Instance Methods

engine_api_files_modified_time_checksum(engines_path) click to toggle source
# File lib/rubocop/cop/mixin/engine_api.rb, line 44
def engine_api_files_modified_time_checksum(engines_path)
  api_files = Dir.glob(File.join(engines_path, '**/app/api/**/api/**/*'))
  mtimes = api_files.sort.map { |f| File.mtime(f) }
  Digest::SHA1.hexdigest(mtimes.join)
end
extract_api_list(engines_path, engine, api_file) click to toggle source
# File lib/rubocop/cop/mixin/engine_api.rb, line 27
def extract_api_list(engines_path, engine, api_file)
  key = cache_key(engine, api_file)
  @cache ||= {}
  cached = @cache[key]
  return cached if cached

  details = API_FILE_DETAILS[api_file]

  path = full_path(engines_path, engine, details)
  return [] unless File.file?(path)

  list = extract_array(path, details[:array_matcher])

  @cache[key] = list
  list
end

Private Instance Methods

api_path(engines_path, engine) click to toggle source
# File lib/rubocop/cop/mixin/engine_api.rb, line 60
def api_path(engines_path, engine)
  raw_name = ActiveSupport::Inflector.underscore(engine.to_s)
  File.join(engines_path, "#{raw_name}/app/api/#{raw_name}/api/")
end
cache_key(engine, api_file) click to toggle source
# File lib/rubocop/cop/mixin/engine_api.rb, line 56
def cache_key(engine, api_file)
  "#{engine}-#{api_file}"
end
extract_array(path, array_matcher) click to toggle source
# File lib/rubocop/cop/mixin/engine_api.rb, line 122
def extract_array(path, array_matcher)
  list = []
  root_node = extract_module_root(path)
  root_node.children.each do |module_child|
    array_node = send(array_matcher, module_child)
    next if array_node.nil?

    array_node.children.map do |item|
      list << item.source
    end
  end
  list
end
extract_module_root(path) click to toggle source
# File lib/rubocop/cop/mixin/engine_api.rb, line 70
def extract_module_root(path)
  # The AST for the whitelist definition looks like this:
  #
  # (:module,
  #   (:const,
  #     (:const, nil, :Trucking), :Api),
  #   (:casgn, nil, :PUBLIC_SERVICES,
  #     (:array,
  #       (:const,
  #         s(:const, nil, :Trucking), :CancelDeliveryOrderService),
  #       (:const,
  #         s(:const, nil, :Trucking), :FclFulfillmentDetailsService))
  #
  # Or, in the case of two separate whitelists:
  #
  # (:module,
  #   (:const,
  #     (:const, nil, :Trucking), :Api),
  #   s(:begin,
  #     s(:casgn, nil, :PUBLIC_SERVICES,
  #       s(:send,
  #         s(:array,
  #           s(:const,
  #             s(:const, nil, :Trucking), :CancelDeliveryOrderService),
  #           s(:const,
  #             s(:const, nil, :Trucking), :ContainerUseService))),
  #     s(:casgn, nil, :PUBLIC_CONSTANTS,
  #       s(:send,
  #         s(:array,
  #           s(:const,
  #             s(:const, nil, :Trucking), :DeliveryStatuses),
  #           s(:const,
  #             s(:const, nil, :Trucking), :LoadTypes)), :freeze)))
  #
  # We want the :begin in the 2nd case, the :module in the 1st case.
  module_node = parse_ast(File.read(path))
  module_block_node = module_node&.children&.[](1)
  if module_block_node&.begin_type?
    module_block_node
  else
    module_node
  end
end
full_path(engines_path, engine, details) click to toggle source
# File lib/rubocop/cop/mixin/engine_api.rb, line 52
def full_path(engines_path, engine, details)
  api_path(engines_path, engine) + details[:file_basename]
end
parse_ast(source_code) click to toggle source
# File lib/rubocop/cop/mixin/engine_api.rb, line 65
def parse_ast(source_code)
  source = RuboCop::ProcessedSource.new(source_code, RUBY_VERSION.to_f)
  source.ast
end