Jay Killeen

Joined

4,200 Experience
26 Lessons Completed
0 Questions Solved

Activity

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(
              :food_id,
              options_from_collection_for_select(
                @foods,
                :id,
                :name
              ),
              { 
                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
  end

and not

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

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.

comments_helper.rb

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

comments_controller.rb

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 }
        format.js
      end
    else
      redirect_to @commentable, alert: "Something went wrong."
    end
  end

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

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

  private

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

end

comment.rb

class Comment < ApplicationRecord
  acts_as_paranoid
  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)
  end

  def self.max_nesting
    3
  end

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

end

_comment.html.erb

<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."%>
          </div>
        </div>
      </div>
    <% 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."%>
          </div>
        </div>
      </div>
    <% 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"
    } %>
  </div>
  <%= tag.div id: "#{dom_id(comment)}_comments" do %>
    <%= render comment.comments, nesting: comment.nesting %>
  <% end %>
</div>

create.js.erb

<% 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")
form.reset()

<% if @comment.parent_id? %>
  form.classList.add("hidden")
<% end %>

show.html.erb

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

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

  <%= render @comments %>
</div>

20200901061626_add_nesting_to_comments.rb

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

Posted in Deploy Ubuntu 18.04 Bionic Beaver Discussion

Hmmm tried this from a fresh rails 6 app.

cap production deploy is failing with

/home/userme/.rbenv/versions/2.6.3/lib/ruby/gems/2.6.0/gems/sshkit-1.20.0/lib/sshkit/runners/parallel.rb:15:in `rescue in block (2 levels) in execute': Exception while executing as userme@123.456.789.10: rake exit status: 1 (SSHKit::Runner::ExecuteError) 
$HOME/.rbenv/bin/rbenv exec bundle exec rake assets:precompile
Compiling…
Compilation failed:
error Command "webpack" not found.

Webpacker docs is saying Make sure you have public/packs and node_modules in :linked_dirs for capistrano deployments. Then If you have node_modules added to :linked_dirs you'll need to run yarn install before deploy:assets:precompile and they have a snippet to add to your deploy.rb...

before "deploy:assets:precompile", "deploy:yarn_install"

namespace :deploy do
  desc "Run rake yarn install"
  task :yarn_install do
    on roles(:web) do
      within release_path do
        execute("cd #{release_path} && yarn install --silent --no-progress --no-audit --no-optional")
      end
    end
  end
end

Testing this now to see if it works... 10 mins later... nope... still busted... will try again later :/

I believe I may need to add capistrano-npm to my Gemfile so npm install can be run in the capistrano deploy scripts.

Posted in Test Driven Development Basics Discussion

I really like that you come at it fairly 'bare bones'... as in, no Rspec / FactoryBot / Fixtures / Cucumber / Faker / shoulda-matchers /guard etc etc. I recall when learning for the first time, it was just so much to take in for testinig. Your PORO approach makes it so simple and not as intimidating.

I did and it worked pretty well. I'll hunt down a sample of the code and share back here soon. Might take me a while though as I am away for a few days. I'll dump a bit below but I don't have time to cut out the sensitive info.

It was a little more to it than I first thought it would be. Here is a bit of stimulus. Notice the stuff with the Rails.Ajax that is querying my rails controller and returning json. So a bit is needed to be done in the controller to respond to that ajax request and only return the json in a format for stimulus to use.

I'm running it all from a new.html.erb that is requesting to create and then returning a show partial which allows the dropdown to be updated based on the selection of the previous dropdown. It really needs a demo of the whole thing I put together in a more generic way than what I had done in my application (ie do a simple country / state / city selector from Rails api all the way through to stimulus).

import { Controller } from "stimulus"

export default class extends Controller {

  static targets = [ "material", "price", "from", "query", "result", "button"]

  initialize() {
    console.log("Stimulus at your service!")
    this.updateQueryParams()
    this.toggleLoading()
  }

  get from () {
    return this.targets.find("from").value
  }

  get material() {
    return this.targets.find("material").value
  }

  get price() {
    return this.targets.find("price").value
  }

  toggleLoading() {
    this.targets.find("button").classList.toggle("is-loading")
  }

  updateToUnitOptions() {
    this.clearResult()

    Rails.ajax({
      type: "GET",
      url: "/alt_units.json",
      data: "material=" + this.material,
      success: (data) => {
        console.log('Alt Units were got!')
        this.refreshDropdownValues(data)
      }
    })
  }

  refreshDropdownValues(data) {
    let fromBefore = this.from
    this.fromTarget.innerHTML = ""
    for(var i = 0; i < data.length; i++) {
      var opt = data[i]
      this.fromTarget.innerHTML += "<option value=\"" + opt.name + "\">" + opt.name + "</option>"
    }
    this.fromTarget.value = fromBefore
    this.updateQueryParams()
  }

  clearResult() {
    this.queryTarget.innerHTML = ""
  }
}

Posted in Populate dropdowns based on selection with Stimulus JS

Wow. I was just deep diving in the console and wondering why this inside the callback was only referencing the Rails.ajax object... OK! Back on the road again!

I am now at the point where the dropdown menu 'toTarget' options need to be updated. Thanks for your help.

  updateToUnitOptions() {
    const material = this.material

    Rails.ajax({
      type: "GET",
      url: "/alt_units.json",
      data: "material=" + material,
      success: (data) => {
        console.log('Alt Units were got!')
        this.refreshDropdownValues(data)
      }
    })
  }

  refreshDropdownValues(data) {
    // update a Stimulus Target
    this.result = this.material
    this.toTarget <<<<< here is where I need to update selection option values.
    console.log(data)
  }
    ```
logo Created with Sketch.

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

© 2022 GoRails, LLC. All rights reserved.