@@ -241,7 +241,7 @@ Here's what [@nshki](https://github.com/nshki) found when they tried it:
241241
242242Let's look at testing, then we'll get to passing these POROs to jobs like the quotes mentioned!
243243
244- ### A Quick Aside: Testing Associated Objects
244+ ### Testing Associated Objects
245245
246246Follow the ` app/ models/ post.rb` and ` app/ models/ post/ publisher.rb` naming structure in your tests and add ` test / models/ post/ publisher_test.rb` .
247247
@@ -261,6 +261,87 @@ class Post::PublisherTest < ActiveSupport::TestCase
261261end
262262` ` `
263263
264+ ### Active Model integration
265+
266+ Associated Objects quack like ` ActiveModel ` s because we:
267+
268+ - [` extend ActiveModel ::Naming ` ](https://api.rubyonrails.org/classes/ActiveModel/Naming.html)
269+ - [` include ActiveModel ::Conversion ` ](https://api.rubyonrails.org/classes/ActiveModel/Conversion.html)
270+
271+ This means you can pass them to helpers like ` form_with` and route helpers like ` url_for` too.
272+
273+ > [!NOTE]
274+ > We don't ` include ActiveModel ::Model ` since we don't need ` assign_attributes` and validations really.
275+
276+ ` ` ` ruby
277+ # app/controllers/post/publishers_controller.rb
278+ class Post ::PublishersController < ApplicationController
279+ before_action :set_publisher
280+
281+ def new
282+ end
283+
284+ def create
285+ @publisher .publish params.expect(publisher: :toast )
286+ redirect_back_or_to root_url, notice: " Out it goes!"
287+ end
288+
289+ private
290+ def set_publisher
291+ # Associated Objects are POROs, so behind the scenes we're really doing `Post.find(…).publisher`.
292+ @publisher = Post ::Publisher .find(params[:id ])
293+ end
294+ end
295+ ```
296+
297+ And then on the view side, you can pass it into ` form_with ` :
298+
299+ ``` erb
300+ <%# app/views/post/publishers/new.html.erb %>
301+ <%# Here `form_with` calls `url_for(@publisher)` which calls `post_publisher_path(@publisher)`. %>
302+ <%= form_with model: @publisher do |form| %>
303+ <%= form.text_field :toast %>
304+ <%= form.submit "Publish with toast" %>
305+ <% end %>
306+ ```
307+
308+ Finally, the routing is pretty standard fare:
309+
310+ ``` ruby
311+ namespace :post do
312+ resources :publishers
313+ end
314+ ```
315+
316+ #### Rendering Associated Objects
317+
318+ Associated Objects respond to ` to_partial_path ` , so you can pass them directly to ` render ` .
319+
320+ We're using Rails' conventions here, so view paths look like this:
321+
322+ ``` erb
323+ <%# With a Post::Publisher, this renders app/views/post/publishers/_publisher.html.erb %>
324+ <%= render publisher %>
325+
326+ <%# With a Post::Comment::Rating, this renders app/views/post/comment/ratings/_rating.html.erb %>
327+ <%= render rating %>
328+ ```
329+
330+ We've also got full support for fragment caching, so this is possible:
331+
332+ ``` erb
333+ <%# app/views/post/publishers/_publisher.html.erb %>
334+ <%= cache publisher do %>
335+ <%# More publishing specific view logic. %>
336+ <% end %>
337+ ```
338+
339+ > [ !NOTE]
340+ > We only support recyclable cache keys which has been the default since Rails 5.2.
341+ > This means the Active Record you associate with must have ` SomeModel.cache_versioning = true ` enabled.
342+ >
343+ > Associated Objects respond to ` cache_key ` , ` cache_version ` and ` cache_key_with_version ` like Active Records.
344+
264345### Polymorphic Associated Objects
265346
266347If you want to share logic between associated objects, you can do so via standard Ruby modules:
0 commit comments