Module: ConcernsOnRails::Controllers::Includable
- Extended by:
- ActiveSupport::Concern
- Defined in:
- lib/concerns_on_rails/controllers/includable.rb
Overview
Whitelisted association sideloading + sparse fieldsets for JSON APIs. Same allow-list philosophy as Controllers::Sortable: a client can only ask for associations/fields you’ve explicitly permitted, so ‘?include=` can never trigger an arbitrary `.includes` (N+1 / data-exposure risk).
class ArticlesController < ApplicationController
include ConcernsOnRails::Controllers::Includable
includable :author, :comments,
fields: { articles: %i[id title], authors: %i[id name] }
def index
render json: with_includes(Article.all),
include: requested_includes,
fields: requested_fields
end
end
URL params:
?include=author,comments -> eager-loads only whitelisted associations
?fields[articles]=id,title -> sanitized down to the allowed columns
‘requested_includes` / `requested_fields` return sanitized values you can hand to your serializer; they never mutate the rendered output themselves.
Instance Method Summary collapse
-
#requested_fields ⇒ Object
Sanitized sparse fieldsets: { table => [cols] }, each intersected with the allowed columns for that table.
-
#requested_includes ⇒ Object
Sanitized association list: requested ∩ allow-list.
-
#with_includes(relation) ⇒ Object
Eager-load only the whitelisted associations requested via ?include=.
Instance Method Details
#requested_fields ⇒ Object
Sanitized sparse fieldsets: { table => [cols] }, each intersected with the allowed columns for that table. Unknown tables/columns are dropped.
63 64 65 66 67 68 69 70 71 72 73 74 75 |
# File 'lib/concerns_on_rails/controllers/includable.rb', line 63 def requested_fields raw = params[:fields] return {} unless raw.respond_to?(:each_pair) allowed = self.class.includable_fields raw.each_with_object({}) do |(table, cols), memo| key = table.to_sym next unless allowed.key?(key) permitted = split_field_list(cols) & allowed[key] memo[key] = permitted unless permitted.empty? end end |
#requested_includes ⇒ Object
Sanitized association list: requested ∩ allow-list.
56 57 58 59 |
# File 'lib/concerns_on_rails/controllers/includable.rb', line 56 def requested_includes requested = params[:include].to_s.split(",").map { |token| token.strip.to_sym } requested & self.class.includable_associations end |
#with_includes(relation) ⇒ Object
Eager-load only the whitelisted associations requested via ?include=. Returns the relation unchanged when nothing valid was requested.
50 51 52 53 |
# File 'lib/concerns_on_rails/controllers/includable.rb', line 50 def with_includes(relation) associations = requested_includes associations.empty? ? relation : relation.includes(*associations) end |