Jay Killeen


4,340 Experience
27 Lessons Completed
0 Questions Solved


Posted in How do I search Action Text with Ransack?

I second @miguel's recommendation for the post from Thoughtbot. Insanely great implementation!

Posted in Dynamic Nested Forms with Turbo Discussion

Totally agree with this comment and also feedback for all the publishers (ie Chris could do this also).

Such a great tutorial. Small enhancement for the form validation errors.

In the _inline_fields.html.erb I added my render for the validation_errors (similar to other tutorials) so that the flash message is shown on an invalid save.

<% frame_id = dom_id(f.object, "#{ attribute }_turbo_frame") %>
  <%= turbo_frame_tag frame_id, class: "contents" do %>
    <%= render "validation_errors", model: f.object %>
    <div class="flex flex-row items-center mt-4">
      <%= yield %>
      <%= f.button class: "ml-2 btn-primary inline-action" do %>
        Save <%= f.object.class.human_attribute_name(attribute) %>
      <% end %>
      <%= link_to "Cancel", polymorphic_path(f.object), class: "cancel inline-action" %>
  <% end %>

Using tailwindcss in the new rails 7, turbo and stimulus world. I was able to achieve this like:

Note the use of group on the parent object (the submit button) and then the hidden, group-disabled:hidden and group-disabled:flex on the child spans.

<%= f.button class: "btn-primary group" do %>
  <span class="group-disabled:hidden h-4">Go!</span>
  <span class="hidden group-disabled:flex">
    <svg aria-hidden="true" class="mr-2 w-4 h-4 text-yellow-500 animate-spin fill-white" viewBox="0 0 100 101" fill="none" xmlns="http://www.w3.org/2000/svg">
      <path d="M100 50.5908C100 78.2051 77.6142 100.591 50 100.591C22.3858 100.591 0 78.2051 0 50.5908C0 22.9766 22.3858 0.59082 50 0.59082C77.6142 0.59082 100 22.9766 100 50.5908ZM9.08144 50.5908C9.08144 73.1895 27.4013 91.5094 50 91.5094C72.5987 91.5094 90.9186 73.1895 90.9186 50.5908C90.9186 27.9921 72.5987 9.67226 50 9.67226C27.4013 9.67226 9.08144 27.9921 9.08144 50.5908Z" fill="currentColor"/>
       <path d="M93.9676 39.0409C96.393 38.4038 97.8624 35.9116 97.0079 33.5539C95.2932 28.8227 92.871 24.3692 89.8167 20.348C85.8452 15.1192 80.8826 10.7238 75.2124 7.41289C69.5422 4.10194 63.2754 1.94025 56.7698 1.05124C51.7666 0.367541 46.6976 0.446843 41.7345 1.27873C39.2613 1.69328 37.813 4.19778 38.4501 6.62326C39.0873 9.04874 41.5694 10.4717 44.0505 10.1071C47.8511 9.54855 51.7191 9.52689 55.5402 10.0491C60.8642 10.7766 65.9928 12.5457 70.6331 15.2552C75.2735 17.9648 79.3347 21.5619 82.5849 25.841C84.9175 28.9121 86.7997 32.2913 88.1811 35.8758C89.083 38.2158 91.5421 39.6781 93.9676 39.0409Z" fill="currentFill"/>
<% end %>

I grabbed the spinner svg from this example at https://flowbite.com/docs/components/spinner/

I referenced this screencast on this Trix Github issue where comments disappear if the user mentioned is a link and is clicked. Required a data turbo false tag to be filtered from trix content divs as well as allowing the data turbo false html content to be an allowed_attributes of action text.

Thank you so much! Was wrestling with this and having a solution with an explanation is pure gold!

Posted in Realtime Nested Comments: Part 3 Discussion

But isn't the problem that, when comparing comment.user to current_user and current_user is not available through the renderer??

Interested to know what OuttaSpaceTime was doing to successfully pass the current_user around. Currently struggling to access it via the comment.rb callbacks like after_create_commit.

Posted in Realtime Nested Comments: Part 3 Discussion

Same problem here. Did you end up finding any solution to this? Right now I am investigating the use of view_components gem but seems heavy handed :/

Found that this is harder if using options_from_collection_for_select so gave up and ended up plucking my main value (in this case name) from the collection and using options_for_select then using a lookup on the name when sending the data back to the controller on submission.

Haha cheers... after reading on :)

Were you able to find a solution here? I am having the same problem with include_blank and any of the other values initially set before the turbo_stream updates the id.

<%= f.select(
                include_blank: true, 
                required: false
                class: "form-input-group-text-field",
                multiple: false,
                data: {
                  filteredforms_target: "foodSelect"

Great video. Instantly love this gem! Had a task running in less than a few minutes which is great. Only problem I can't seem to find info on.

How do I authenticate/authorise this with pundit?

Appears to just set the route to be publicly facing with no documentation on integrating into the gems controllers...

But maybe something to learn, so on Stackoverflow I am reading through https://stackoverflow.com/questions/2881093/override-a-rails-engine-controller-action

I've also done the upgrade from Turbolinks to Turbo and had to make this change. It was the only detail that I could find to store (ie those other two options had to be removed (title and pagename or something).

Posted in How to use Devise with Hotwire & Turbo.js Discussion

If, like me, you rushed through this video and are now are seeing the error from warden Invalid strategy some_external_strategy ... it could be because you uncommented the entire block in devise.rb initializer file. I didn't want to post this comment because I might look silly but... in case someone else makes my mistake :D

config.warden do |manager|
    manager.failure_app = TurboFailureApp
    # manager.intercept_401 = false
    # manager.default_strategies(scope: :user).unshift :some_external_strategy

and not

config.warden do |manager|
    manager.failure_app = TurboFailureApp
    manager.intercept_401 = false
    manager.default_strategies(scope: :user).unshift :some_external_strategy

Posted in How to use Devise with Hotwire & Turbo.js Discussion

I can also confirm that creating a ./app/controllers/turbo_controller.rb instead of adding it to the top of the devise.rb initializers fixed my undefined local variable or method 'turbo_include_tags' error.

Posted in How to use Hotwire in Rails Discussion

I'd also like the answer to that :)

Posted in Comments With Polymorphic Associations Discussion

To make it as commenters you'd need to set an alias on the users association on comments. Hope this helps. Sorry I don't have exact code for it.

Posted in Comments With Polymorphic Associations Discussion

@post.comments.users would work if you had your comments has_many: users association on your comment model.

Posted in Nested Comment Threads in Rails - Part 2 Discussion

Pundit is my preference for this type of functionality. I've followed this tutorial and added the appropriate pundit policies on posts and comments along the way. +1 to pundit :)

Posted in Nested Comment Threads in Rails - Part 3 Discussion

I have fixed this issue but it requires quite a few changes. First of all, the cause is that the ajax is only rerendering the partial, which means the nesting value is not being incremented. That much is fairly obvious.

To fix this, I moved the max_nesting into the Comment model as a class variable. ie def self.max_nesting 3 end. I then replace all references as Comment.max_nesting. You can then move that part of the logic into the comments helper.

The second fix was to take the nesting value for the comment and add it as a field on the Comment model. So you know that the @comment.nesting value is stored with the comment itself.

It is worth noting I have the paranoia/soft delete function set which I think has reduced my chances of the nesting becoming broken as comments are deleted.

In my comment controller, I am storing the comment nesting value through a Comment model method called set_nesting. This increments from the parent comment OR sets it to 1.


  def reply_to_comment_id(comment, nesting)
    nesting = 1 unless nesting.present?
    max_nesting = Comment.max_nesting
    if max_nesting.blank? || nesting < max_nesting


class CommentsController < ApplicationController
  before_action :authenticate_user!

  def create
    @comment = @commentable.comments.new(comment_params)
    @comment.nesting = @comment.set_nesting
    @comment.user = current_user
    if @comment.save
      respond_to do |format|
        format.html { redirect_to @commentable }
      redirect_to @commentable, alert: "Something went wrong."

  def destroy
    @comment = @commentable.comments.find(params[:id])
    redirect_to @commentable

  def restore
    @comment = @commentable.comments.with_deleted.find(params[:id])
    redirect_to @commentable


    def comment_params
      params.require(:comment).permit(:body, :parent_id)



class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :commentable, polymorphic: true
  belongs_to :parent, optional: true, class_name: "Comment"

  validates :body, presence: true
  validates_length_of :body, maximum: 140

  def comments
    Comment.with_deleted.where(commentable: commentable, parent_id: id).order(created_at: :asc)

  def self.max_nesting

  def set_nesting
    if self.parent.present? && self.parent.nesting.present?
      self.nesting = self.parent.nesting + 1
      self.nesting = 1



<div class="border-gray-300 border-l p-4 my-4 mt-2 ml-2">
  <div class="flex"><%= comment.user.name %> says..</div>
    <% if comment.deleted? %>
      <div class="border-gray-300 border-l p-2 italic text-gray-500">
        <%= simple_format "This comment has since been deleted..." %>
        <div class="italic text-gray-500 text-sm">
          <div class="flex">
            <%= comment.created_at.strftime("%I:%M %p") %> • <%= comment.created_at.strftime("%d %b %y") %> <%= "~" + time_ago_in_words(comment.created_at) + " ago."%>
    <% else %>
      <div class="border-gray-300 border-l p-2">
        <%= simple_format comment.body %>
        <div class="italic text-gray-500 text-sm">
          <div class="flex">
            <%= comment.created_at.strftime("%I:%M %p") %> • <%= comment.created_at.strftime("%d %b %y") %> <%= "~" + time_ago_in_words(comment.created_at) + " ago."%>
    <% end %>

  <div class="mt-2" data-controller="reply">
    <% if policy(comment).create? %>
      <%= link_to "Reply", "#", class: "text-green-700", data: { action: "click->reply#show" } %> 
    <% end %>
    <% if policy(comment).destroy? %>
      <%= link_to "Delete", comment_path(comment, post_id: comment.commentable), method: :delete, class: "text-red-700", data: { confirm: "Are you sure?" } %>
    <% end %>
    <% if policy(comment).restore? %>
      <%= link_to "Undo", restore_comment_path(comment, post_id: comment.commentable), method: :patch, class: "text-red-700", data: { confirm: "Restore this comment?" } %>
    <% end %>
    <%= render partial: "comments/form", locals: {
      commentable: comment.commentable,
      parent_id: reply_to_comment_id(comment, comment.nesting),
      method: "post",
      class: "hidden",
      target: "reply.form"
    } %>
  <%= tag.div id: "#{dom_id(comment)}_comments" do %>
    <%= render comment.comments, nesting: comment.nesting %>
  <% end %>


<% if @comment.parent_id? %>
    var comments = document.querySelector("#<%= dom_id(@comment.parent) %>_comments")
<% else %>
    var comments = document.querySelector("#comments")
<% end %>

comments.insertAdjacentHTML('beforeend', '<%=j render partial: "comments/comment", locals: { comment: @comment, nesting: @comment.nesting }, format: :html %>')

var form = comments.parentElement.querySelector("form")

<% if @comment.parent_id? %>
<% end %>


  <h2 class="title-2">Comments</h2>

  <div class="mt-4">
    <div id="comments">
      <%= render partial: "comments/form", locals: {commentable: @post, method: "post" } %>

  <%= render @comments %>


class AddNestingToComments < ActiveRecord::Migration[6.0]
  def change
    add_column :comments, :nesting, :integer

Screencast tutorials to help you learn Ruby on Rails, Javascript, Hotwire, Turbo, Stimulus.js, PostgreSQL, MySQL, Ubuntu, and more. Icons by Icons8

© 2023 GoRails, LLC. All rights reserved.