{"record":{"id":"880c94814f7f45e4","repo":"instructure/canvas-lms","slug":"set-the-collection-owner-first","errorCode":null,"errorMessage":"set the collection owner first","messagePattern":"set the collection owner first","errorType":"exception","errorClass":"RuntimeError","httpStatus":null,"severity":"error","filePath":"app/models/master_courses/collection_restrictor.rb","lineNumber":39,"sourceCode":"  # this is super similar to the normal Restrictor - the main difference is that these models are locked by some parent object\n  # e.g. this is a quiz question locked by a quiz\n  # even though they do a lot of the same things, i couldn't figure out a way to keep them together that didn't make things super convoluted\n\n  # FYI you'll have to directly hook code that replicates the behavior of\n  # `check_before_overwriting_child_content_on_import` into the respective importers if you add this to new objects\n\n  def self.included(klass)\n    klass.include MasterCourses::Restrictor::CommonMethods\n    klass.extend ClassMethods\n\n    klass.cattr_accessor :collection_owner_association # this is the association to find the quiz\n\n    klass.after_update :mark_downstream_changes, if: -> { klass != Lti::LineItem || Account.site_admin.feature_enabled?(:blueprint_line_item_support) }\n  end\n\n  module ClassMethods\n    def restrict_columns(edit_type, columns)\n      raise \"set the collection owner first\" unless collection_owner_association\n\n      super\n      owner_class = reflections[collection_owner_association.to_s].klass\n      owner_class.restrict_columns(edit_type, pseudocolumn_for_type(edit_type)) # e.g. add \"assessment_questions_content\" as a restricted column\n    end\n\n    def pseudocolumn_for_type(type) # prepend with table name because it looks better than \"Quizzes::QuizQuestion\"\n      \"#{table_name}_#{type}\"\n    end\n  end\n\n  def check_restrictions?\n    true\n  end\n\n  def owner_for_restrictions\n    send(self.class.base_class.collection_owner_association)\n  end","sourceCodeStart":21,"sourceCodeEnd":57,"githubUrl":"https://github.com/instructure/canvas-lms/blob/1c9f0bb8013ed69c4f2efe11fd483025469b7e6c/app/models/master_courses/collection_restrictor.rb#L21-L57","documentation":"MasterCourses::CollectionRestrictor extends the blueprint course restrictor to collections whose owner is a separate model (e.g. AssessmentQuestion banks). restrict_columns requires that the collection owner association was declared beforehand via set_collection_owner_association; without it the pseudocolumn changes cannot be forwarded to the owner class, so it raises immediately.","triggerScenarios":"Calling klass.restrict_columns(edit_type, columns) on a model that includes MasterCourses::CollectionRestrictor before calling set_collection_owner_association (e.g. in a model's included block ordering issue).","commonSituations":"Adding the restrictor to a new model and forgetting the owner association declaration; refactoring model concerns so restrict_columns runs before the association is set; copy-pasting restrictor setup from another model without its owner declaration.","solutions":["Call set_collection_owner_association (with the owner association symbol) before any restrict_columns call in the model","Ensure the included-hook order puts the association declaration above restrict_columns calls","Compare with an existing restrictor model (e.g. AssessmentQuestion) to copy the correct setup"],"exampleFix":"# before\nincluded do\n  restrict_columns(:content, [...])\nend\n# after\nincluded do\n  set_collection_owner_association :assessment_question\n  restrict_columns(:content, [...])\nend","handlingStrategy":"validation","validationCode":"raise 'owner not set' unless Model.reflections.key?(Model.collection_owner_association.to_s) # run in an initializer/spec check before restrict_columns","typeGuard":"def collection_restrictor_configured?(klass)\n  klass.respond_to?(:collection_owner_association) && !!klass.collection_owner_association\nend","tryCatchPattern":null,"preventionTips":["Always call set_collection_owner_association as the first statement in the included block","Add a spec asserting the model's restrictor setup loads"],"tags":["ruby","blueprint-courses","configuration-order"],"backgroundTag":"missing-required-config-field","analyzedSha":"1c9f0bb8013ed69c4f2efe11fd483025469b7e6c","analyzedAt":"2026-09-15T20:33:18.891Z","contentChangedAt":"2026-09-15T20:33:18.891Z","schemaVersion":2},"datasetVersion":"2026-09-23T02:17:17.105Z"}