Class: RuboCop::Cop::Flexport::GlobalModelAccessFromEngine

Inherits:
RuboCop::Cop
  • Object
show all
Includes:
EngineNodeContext
Defined in:
lib/rubocop/cop/flexport/global_model_access_from_engine.rb

Overview

This cop checks for engines reaching directly into app/ models.

With an ActiveRecord object, engine code can perform arbitrary reads and arbitrary writes to models located in the main ‘app/` directory. This cop helps isolate Rails Engine code to ensure that modular boundaries are respected.

Checks for both access via ‘MyGlobalModel.foo` and associations.

Examples:


# bad

class MyEngine::MyService
  m = SomeGlobalModel.find(123)
  m.any_random_attribute = "whatever i want"
  m.save
end

# good

class MyEngine::MyService
  ApiServiceForGlobalModels.perform_a_supported_operation("foo")
end

# bad

class MyEngine::MyModel < ApplicationModel
  has_one :some_global_model, class_name: "SomeGlobalModel"
end

# good

class MyEngine::MyModel < ApplicationModel
  # No direct association to global models.
end

Constant Summary collapse

MSG =
'Direct access of global model `%<model>s` ' \
'from within Rails Engine.'

Instance Method Summary collapse

Methods included from EngineNodeContext

#in_module_or_class_declaration?

Instance Method Details

#external_dependency_checksumObject

Because this cop’s behavior depends on the state of external files, we override this method to bust the RuboCop cache when those files change.



83
84
85
# File 'lib/rubocop/cop/flexport/global_model_access_from_engine.rb', line 83

def external_dependency_checksum
  Digest::SHA1.hexdigest(model_dir_paths.join)
end

#on_const(node) ⇒ Object



58
59
60
61
62
63
64
65
66
# File 'lib/rubocop/cop/flexport/global_model_access_from_engine.rb', line 58

def on_const(node)
  return unless in_enforced_engine_file?
  return unless global_model_const?(node)
  # The cop allows access to e.g. MyGlobalModel::MY_CONST.
  return if child_of_const?(node)
  return if in_module_or_class_declaration?(node)

  add_offense(node, message: message(node.source))
end

#on_send(node) ⇒ Object



68
69
70
71
72
73
74
75
76
77
78
# File 'lib/rubocop/cop/flexport/global_model_access_from_engine.rb', line 68

def on_send(node)
  return unless in_enforced_engine_file?

  rails_association_hash_args(node) do |assocation_hash_args|
    class_name_node = extract_class_name_node(assocation_hash_args)
    class_name = class_name_node&.value
    next unless global_model?(class_name)

    add_offense(class_name_node, message: message(class_name))
  end
end