Class: EagerEye::SerializerUsageParser
- Inherits:
-
Object
- Object
- EagerEye::SerializerUsageParser
- Defined in:
- lib/eager_eye/serializer_usage_parser.rb
Overview
Serializers are the worst false-positive source because the detector sees the serializer class in isolation: it cannot tell whether an association it flags is actually eager-loaded by the controller that renders it, nor whether the serializer is only ever handed a single record (no collection => no N+1).
This parser scans the whole app for render sites — ‘XxxBlueprint.render*(arg, view: :v)`, AMS `render json: arg, (each_)serializer: XxxSerializer` — and, per (serializer, view), records:
* preloaded_per_site : the associations eager-loaded on `arg` at each site
* any_collection : was `arg` ever a collection (vs a single record)?
An association preloaded at EVERY site, or a serializer only ever fed single records, cannot cause an N+1 — letting the detector stay silent there.
Constant Summary collapse
- PRELOAD_METHODS =
%i[includes preload eager_load].freeze
- RELATION_WRAPPERS =
%i[pagy paginate page kaminari with_pagy].freeze
- SINGLE_RECORD_METHODS =
%i[find find_by find_by! first first! last last! take take! sole find_sole_by new build current_user current_account].freeze
- RENDER_METHODS =
%i[render render_as_hash render_as_json render_as_json! serialize].freeze
- SERIALIZER_SUFFIXES =
%w[Blueprint Serializer Resource].freeze
Instance Attribute Summary collapse
-
#usages ⇒ Object
readonly
serializer_basename => [ { view: sym_or_nil, preloaded: Set, collection: bool }, … ].
Instance Method Summary collapse
-
#initialize ⇒ SerializerUsageParser
constructor
A new instance of SerializerUsageParser.
- #known_serializer?(serializer) ⇒ Boolean
- #parse_file(ast) ⇒ Object
-
#safe_access?(serializer, view, association) ⇒ Boolean
Whether an association read in ‘view` of `serializer` can be proven safe.
Constructor Details
#initialize ⇒ SerializerUsageParser
Returns a new instance of SerializerUsageParser.
29 30 31 |
# File 'lib/eager_eye/serializer_usage_parser.rb', line 29 def initialize @usages = Hash.new { |h, k| h[k] = [] } end |
Instance Attribute Details
#usages ⇒ Object (readonly)
serializer_basename => [ { view: sym_or_nil, preloaded: Set, collection: bool }, … ]
27 28 29 |
# File 'lib/eager_eye/serializer_usage_parser.rb', line 27 def usages @usages end |
Instance Method Details
#known_serializer?(serializer) ⇒ Boolean
56 57 58 |
# File 'lib/eager_eye/serializer_usage_parser.rb', line 56 def known_serializer?(serializer) @usages.key?(serializer) end |
#parse_file(ast) ⇒ Object
33 34 35 36 37 38 39 40 |
# File 'lib/eager_eye/serializer_usage_parser.rb', line 33 def parse_file(ast) return unless ast each_scope(ast) do |body| var_values = collect_assignments(body) find_render_sites(body, var_values) end end |
#safe_access?(serializer, view, association) ⇒ Boolean
Whether an association read in ‘view` of `serializer` can be proven safe. `view` is the Blueprinter view the field lives in (nil = a base/default field, rendered by every site). The field is safe when, at every render site that renders it, the association is eager-loaded OR the site passes a single record. To avoid hiding a genuine N+1 we never conclude “safe” when we cannot see the render sites for a named view (it may be rendered dynamically) — only an EXISTING, uniformly-safe set of sites suppresses.
49 50 51 52 53 54 |
# File 'lib/eager_eye/serializer_usage_parser.rb', line 49 def safe_access?(serializer, view, association) sites = sites_rendering(serializer, view) return false if sites.empty? sites.all? { |s| !s[:collection] || s[:preloaded].include?(association) } end |