Class: Railsmith::ArchChecks::DirectModelAccessChecker
- Inherits:
-
Object
- Object
- Railsmith::ArchChecks::DirectModelAccessChecker
- Defined in:
- lib/railsmith/arch_checks/direct_model_access_checker.rb
Overview
Scans controller source files for direct ActiveRecord model access.
Flags lines like User.find(params) or Post.where(active: true) that bypass the service layer. The check is heuristic: it detects CamelCase class names followed by common AR query/persistence methods, excluding well-known non-model classes (Rails, Time, JSON, etc.).
Usage:
checker = Railsmith::ArchChecks::DirectModelAccessChecker.new
violations = checker.check(path: "app/controllers")
Constant Summary collapse
- AR_METHODS =
%w[ find find_by find_by! find_or_create_by find_or_initialize_by where order select limit offset joins includes eager_load preload all first last count sum average minimum maximum create create! update update_all destroy destroy_all delete delete_all exists? any? none? many? pluck ids ].freeze
- NON_MODEL_CLASSES =
Well-known non-model CamelCase roots frequently seen in controllers.
%w[ Rails I18n Time Date DateTime ActiveRecord ApplicationRecord ActiveModel ActionController ApplicationController ActionDispatch AbstractController ActionView ActionMailer ActiveJob ActiveSupport ActiveStorage Integer Float String Hash Array Symbol Numeric BigDecimal File Dir IO URI URL Net HTTP JSON YAML CSV Logger Thread Fiber Mutex Proc Method Class Module Object BasicObject ].freeze
- AR_METHODS_PATTERN =
AR_METHODS.map { |m| Regexp.escape(m) }.join("|")
- DETECT_RE =
Matches: CamelCase class name, dot, AR method, not followed by identifier chars. The negative lookahead ‘(?=[^a-zA-Z0-9_]|$)` prevents `find` matching `finder`.
/ \b ([A-Z][A-Za-z0-9]*(?:::[A-Z][A-Za-z0-9]*)*) (?# class name, possibly namespaced) \. (#{AR_METHODS_PATTERN}) (?# AR method) (?=[^a-zA-Z0-9_]|$) (?# not followed by identifier chars) /x
Instance Method Summary collapse
Instance Method Details
#check(path:) ⇒ Array<Violation>
48 49 50 51 |
# File 'lib/railsmith/arch_checks/direct_model_access_checker.rb', line 48 def check(path:) Dir.glob(File.join(path, "**", "*_controller.rb")) .flat_map { |file| check_file(file) } end |
#check_file(file) ⇒ Array<Violation>
55 56 57 58 59 60 61 62 |
# File 'lib/railsmith/arch_checks/direct_model_access_checker.rb', line 55 def check_file(file) violations = [] File.foreach(file).with_index(1) do |raw_line, lineno| line = raw_line.strip violations.concat(line_violations(file, lineno, line)) unless comment_line?(line) end violations end |