class RuboCop::Cop::Flexport::NewGlobalModel

This cop disallows adding new models to the `app/models` directory.

The goal is to encourage developers to put new models inside Rails Engines (or at least namespaces), where they can be more modularly isolated and ownership is clear.

Use RuboCop's standard `Exclude` file list parameter to exclude existing global model files from counting as violations for this cop.

If you have a monorepo with multiple Rails services, you may wish to set GlobalModelsPath to something more specific than `app/models` so that it only matches the main monolith service and allows global models in other, smaller services. To do this, just set GlobalModelsPath to e.g. `flexport/app/models` in your `.rubocop.yml`.

@example AllowNamespacedGlobalModels: true (default)

# When `AllowNamespacedGlobalModels` is true, the cop only forbids
# additions at the top-level directory.

# bad
# path: app/models/my_new_global_model.rb
class MyNewGlobalModel < ApplicationRecord
  # ...
end

# good
# path: app/models/my_namespace/my_new_global_model.rb
class MyNamespace::MyNewGlobalModel < ApplicationRecord
  # ...
end

# good
# path: engines/my_engine/app/models/my_engine/my_new_engine_model.rb
class MyEngine::MyNewEngineModel < ApplicationRecord
  # ...
end

@example AllowNamespacedGlobalModels: false

# When `AllowNamespacedGlobalModels` is false, the cop forbids all
# new models in this directory and its descendants.

# bad
# path: app/models/my_new_global_model.rb
class MyNewGlobalModel < ApplicationRecord
  # ...
end

# bad
# path: app/models/my_namespace/my_new_global_model.rb
class MyNamespace::MyNewGlobalModel < ApplicationRecord
  # ...
end

# good
# path: engines/my_engine/app/models/my_engine/my_new_engine_model.rb
class MyEngine::MyNewEngineModel < ApplicationRecord
  # ...
end

Constants

ALLOW_NAMESPACES_MSG
DISALLOW_NAMESPACES_MSG

Public Instance Methods

investigate(processed_source) click to toggle source
# File lib/rubocop/cop/flexport/new_global_model.rb, line 74
def investigate(processed_source)
  return if processed_source.blank?

  path = processed_source.file_path
  return unless global_rails_model?(path)

  add_offense(processed_source.ast)
end

Private Instance Methods

allow_namespaced_global_models() click to toggle source
# File lib/rubocop/cop/flexport/new_global_model.rb, line 131
def allow_namespaced_global_models
  cop_config['AllowNamespacedGlobalModels']
end
allowed_namespace?(path) click to toggle source
# File lib/rubocop/cop/flexport/new_global_model.rb, line 100
def allowed_namespace?(path)
  return false unless allow_namespaced_global_models

  parts = path.split(global_models_path)
  parts.last.split('/').length > 1
end
global_models_path() click to toggle source
# File lib/rubocop/cop/flexport/new_global_model.rb, line 125
def global_models_path
  path = cop_config['GlobalModelsPath']
  path += '/' unless path.end_with?('/')
  path
end
global_rails_model?(path) click to toggle source
# File lib/rubocop/cop/flexport/new_global_model.rb, line 91
def global_rails_model?(path)
  return false unless path.include?(global_models_path)
  return false if path.include?('/concerns/')
  return false if in_engine?(path)
  return false if allowed_namespace?(path)

  true
end
in_engine?(path) click to toggle source
# File lib/rubocop/cop/flexport/new_global_model.rb, line 107
def in_engine?(path)
  return true if path.include?('/engines/')

  # Engines model dirs are structured like:
  #   my_engine/app/models/my_engine/my_model.rb.
  # We detect models whose directory structure matches
  # this pattern even if they aren't children of an
  # "/engines/" directory.
  parts = path.split(global_models_path)
  potential_engine_name = parts.last.split('/').first
  engine_models_path = File.join(
    potential_engine_name,
    global_models_path,
    potential_engine_name
  )
  path.include?(engine_models_path)
end
message(_node) click to toggle source
# File lib/rubocop/cop/flexport/new_global_model.rb, line 85
def message(_node)
  return ALLOW_NAMESPACES_MSG if allow_namespaced_global_models

  DISALLOW_NAMESPACES_MSG
end