DEV Community

Daveyon Mayne 😻
Daveyon Mayne 😻

Posted on • Edited on

1

Partials used for turbo streaming must be free of global references.

A flower in the sand

It's never a good idea for partials to reference a global variable, such variable would be current_user. DHH mentioned this already and I'll show you how to gain access to the current user's information. I do it by storing information of the current_user in a meta:


<head>
  <meta name="current_user" content="<%= current_user.json_info %>">
</head>

Enter fullscreen mode Exit fullscreen mode

json_info would contain the information that you need:

# user.rb

[..]

def json_info
  {
    id: self.id, 
    name: <NAME>,
    # Maybe full url to their avatar?
    avatar_url: Rails.application.routes.url_helpers.rails_blob_path(self.avatar, only_path: true)
  }.to_json
end

Enter fullscreen mode Exit fullscreen mode

Basic stuff. With that, our current_user object lives inside the meta of the document head.

This partial will be a Stimulus controller and our partial is called _message.html.erb that lives inside the shared folder:

<article 
  data-controller="message" 
  id="<%= dom_id(message) %>" 
  data-author-id="<%= message.user.id %>">
   <%= message.body %>
</article>

Enter fullscreen mode Exit fullscreen mode

Notice I have here data-author-id, very important, and I've connected a Stimulus controller. Every time this partial get's rendered in the DOM, the connect function gets triggered:

// message_controller.js

import { Controller } from '@hotwired/stimulus'

export default class extends Controller {

  connect() {
    const currentUserBlob = this._getMetaValue('current_user');
    const id = JSON.parse(currentUserBlob).id

    const authorId = this.element.getAttribute('data-author-id')

    // Do something when authorId matches/does not match id.
    // This could be hiding/removing an element or something else.
  }

  _getMetaValue = (name) => {
    const element = document.head.querySelector(`meta[name="${name}"]`)
    return element?.getAttribute("content")
  }

}

Enter fullscreen mode Exit fullscreen mode

Our message model message.rb would look something like this:


[..]

belongs_to :user
belongs_to :message_thread
has_rich_text :body

after_create_commit do
  # We cannot pass current user
  broadcast_append_to(self.message_thread, target: self.message_thread, partial: "shared/message", locals: { message: self })
end

Enter fullscreen mode Exit fullscreen mode

message.rb is just an high-level example showing only the message object that was passed in and nothing else. Depending on what you're doing with "current_user", you'd implement that logic in the Stimulus controller's connect function.

The entire page may look like this:


<%= turbo_stream_from @thread %>

<section id="<%= dom_id(@thread) %>">
  <% @messages.each do |message|
    <%= render "shared/message", message: message %>
  <% end %>
</section>

Enter fullscreen mode Exit fullscreen mode

As a rule of thumb, partials should be free from global references/objects but where a partial is used with ApplicationRenderer, you must connect it with a Stimulus controller to gain access to the current_user.

🎉 Self-promotion 🎉

I'm building Farm Swop, a platform for farmers where farmers can connect with nearby farmers to share/exchange support, machinery etc. It's in active development. Feel free to join to see my progress while I BuildInPublic.

Photo by Jill Heyer on Unsplash

Sentry image

Hands-on debugging session: instrument, monitor, and fix

Join Lazar for a hands-on session where you’ll build it, break it, debug it, and fix it. You’ll set up Sentry, track errors, use Session Replay and Tracing, and leverage some good ol’ AI to find and fix issues fast.

RSVP here →

Top comments (0)

AWS Security LIVE!

Join us for AWS Security LIVE!

Discover the future of cloud security. Tune in live for trends, tips, and solutions from AWS and AWS Partners.

Learn More

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay