diff --git a/CHANGELOG.md b/CHANGELOG.md index 09ca1f5..13d3a07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,14 @@ # Changelog -## 1.7.0 (UNRELEASED) +## 1.7.0 (2025-01-30) * Introduce new `:before_attr_assign` policy chain which allows to access the `model` instance before the parameters are assigned. +* Introduce new `model_includes` DSL method which can be used to eager load + associations in Model operations. See the [corresponding section in the README](README.md#including-associated-records) + for more information. + * Fix bug with lazy authorization in `RailsOps::Operation::Model::Update` operation which used a version of the `model` which was missing some attributes. diff --git a/README.md b/README.md index ee31d24..d2f3c18 100644 --- a/README.md +++ b/README.md @@ -1326,6 +1326,34 @@ end As this base class is very minimalistic, it is recommended to fully read and comprehend its source code. +### Including associated records + +Normaly, when inheriting from `RailsOps::Operation::Model::Load` (as well as from the +`Update` and the `Destroy` operations respectively), RailsOps only loads the instance +of the model specified by the `id` parameter. In some cases, you'd want to eagerly load +associations of the model, e.g. when you need to access associated records. + +For this, RailsOps provides the `model_includes` DSL method, with which you can +pass-in associations to eager load (the value will simply be passed on to an `includes` +call). See the following code snipped for an example: + +```ruby +class Operations::User::Load < RailsOps::Operation::Model::Load + schema3 do + int! :id, cast_str: true + end + + model ::User + + # This will result in RailsOps eagerly loading the `posts` + # association, as well as the comments and authors of the + # comments. + # The call that RailsOps will create is: + # User.includes(posts: { comments: :author }).find_by(id: params[:id]) + model_includes posts: { comments: :author } +end +``` + ### Parameter extraction for create and update As mentioned before, the `Create` and `Update` base classes provide an diff --git a/lib/rails_ops/operation/model/load.rb b/lib/rails_ops/operation/model/load.rb index bcbacd4..63ecd77 100644 --- a/lib/rails_ops/operation/model/load.rb +++ b/lib/rails_ops/operation/model/load.rb @@ -2,6 +2,7 @@ class RailsOps::Operation::Model::Load < RailsOps::Operation::Model class_attribute :_lock_model_at_build class_attribute :_load_model_authorization_action class_attribute :_lock_mode + class_attribute :_model_includes policy :on_init do model @@ -64,6 +65,10 @@ def model_id_field :id end + def self.model_includes(includes) + self._model_includes = includes + end + def find_model unless params[model_id_field] fail "Param #{model_id_field.inspect} must be given." @@ -75,6 +80,9 @@ def find_model # Express intention to lock if required relation = lock_relation(relation) + # Apply includes if given in the operation + relation = relation.includes(self.class._model_includes) if self.class._model_includes.present? + # Fetch (and possibly lock) model model = relation.find_by!(model_id_field => params[model_id_field])