Daniel Weaver

Joined

4,310 Experience
13 Lessons Completed
2 Questions Solved

Activity

As I understand it, author_ids is one of those Rails built-in magic merhods that gives you all the IDs of associated records. For example, if I have a School that has_many :teachers I can do School.find(1).teacher_ids to get all the IDs of all the teachers associated with that school. Super handy and saves writing some gnarly SQL.

The name_with_initial method is generating the text shown alongside the checkbox. It could just be a plain text column from Author, like first_name but this is showing you how you can spruce it up a bit and use a method to generate fotmatted text instead.

Post your whole form code if possible.

I have a Family model that has_many Kids. I'm using dependent: :destroy on the family so when it is destroyed the associated kids are destroyed too. All good.

Now I need to destroy the family when the last associated kid is deleted. This is to prevent stray families in the database that have no kids.

Should I do it in students_controller#destroy method? Or is there a better way?

Posted in How to quiet Delayed Job logs in development?

For now I've changed the timer from default 5 seconds to 60 seconds with this:

# config/initializers/delayed_job_config.rb
Delayed::Worker.sleep_delay = 60

Posted in How to quiet Delayed Job logs in development?

Rails 3.2.14 / Ruby 2.0.0 / Delayed Job 4.1.4 / Foreman

I'm running a worker with Delayed Job, starting the process with Foreman. It all works fine but my dev logs are full of Delayed Job SQL statements. It makes debugging anything in development tiresome. Every 5 seconds I get a big chunk like this:

17:40:26 worker.1 | Delayed::Backend::ActiveRecord::Job Load (0.9ms) UPDATE "delayed_jobs" SET locked_at = '2020-03-25 00:40:26.874025', locked_by = 'host:MacBook-Pro-8.local pid:67410' WHERE id IN (SELECT id FROM "delayed_jobs" WHERE ((run_at <= '2020-03-25 00:40:26.873540' AND (locked_at IS NULL OR locked_at < '2020-03-24 20:40:26.873551') OR locked_by = 'host:MacBook-Pro-8.local pid:67410') AND failed_at IS NULL) ORDER BY priority ASC, run_at ASC LIMIT 1 FOR UPDATE) RETURNING *

How do quiet these SQL logs from Delayed Job? I tried a few solutions form the interwebs but nothing worked so far 😕

Mostly from this thread: https://github.com/collectiveidea/delayed_job/issues/886

Posted in Moving from STI to roles

Found this SO answer suggesting using the strategy pattern to give methods different behaviour based on the user role. Looks very interesting but quite complex.

https://stackoverflow.com/questions/38669599/multiple-role-based-classes-inherited-from-user-class-rails-4

Posted in Moving from STI to roles

Finding it tricky to move from STI (Single Table Inheritance) to user roles because associations between the various user roles to other resources mean different things to different user types.

Teacher and Family models both inherit from User table. Separately we also have Student and Lesson resources.

Teachers teach (has_many) students, so when I call @teacher.students I get all the students taught by that teacher. Likewise, a family has_many students, so when I call @family.students I get all the student who belong to that family.

Similar for lessons - teachers make lessons and families view their students lessons. When I call @teacher.lessons I get a list of all lessons created by that teacher. When I call @family.lessons I get a list of all lessons created for studnets that belong to that family.

So if I were to get rid of STI and just use one User model, what would calling @user.students or @user.lessons return?

I could create some new association names and specify a foreign key, something like:

class User < ActiveRecord::Base

 # for Teachers
 has_many :students, foreign_key: "teacher_id", class_name: "Student"
 has_many :lessons, foreign_key: "teacher_id", class_name: "Lesson"

 # for Families
 has_many :kids, foreign_key: "family_id", class_name: "Student"
 has_many :kids_lessons, foreign_key: "family_id", class_name: "Lesson"

  ...

Is this a reasonable way to go or is there another more Rails-y way to do this?

Posted in How to handle long-running external services?

Hey Ivan, thanks for your ideas. Good stuff.

Unfortunately scheduling won't really help here because the long-running job is user-initiated. Nor will breaking it up into smaller pieces since it's a single FFMPEG video creation process.

A dedicated worker that just handles these requests would help but I'll still be limited to one job per minute. I guess multiple dedicated workers would multiply that but it's early stage and I want to keep costs down. Running the whole app on a $15/mo Digital Ocean server right now.

Having the external service ping my app when the job is done was my original approach. I always wanted to porcess to be asynchronous and started by passing in a webhook URL for the job to call when finished but...

Calling the job via HTTP is the issue. The basis of HTTP requests is that they wait for a response and this gets to the crux of my issue - the job is a serverless function running on Zeit Now platform. These functions stop processing as soon as they send an HTTP response. So I can't make it respond right away with some kind of 'ACCEPTED' message because then the rest of the function won't run.

One hacky option is to set a short timeout on the HTTP request in my Rails app. Then just accept the error and move on. That way the external job doesn't send any response at all. It just calls the webhook URL in app with the result. Super hacky!

Posted in How to write System Tests in Rails Discussion

Nice! 🙌

Just in case anyone finds this helpful, here's how I solved this with Stimulus. If you see any improvements let me know!

# _form.html.erb
...
<div class="form-group" data-controller="filters">
    <%= form.label "spectrum" %>
    <%= radio_button_tag :spectrum, "none", :checked, 
        data: { "action": "click->filters#update" } %>
    <%= label_tag :spectrum_none, "None" %>
    <%= radio_button_tag :spectrum, "white", false, 
        data: { "action": "click->filters#update" } %>
    <%= label_tag :spectrum_white, "White" %>
    <%= radio_button_tag :spectrum, "black", false, 
        data: { "action": "click->filters#update" } %>
    <%= label_tag :spectrum_black, "Black" %>

    <%= form.hidden_field :spectrum, 
        data: { "target": "filters.spectrum" } %>
    <%= form.hidden_field :spectrum_color, 
        data: { "target": "filters.spectrum_color" } %>
  </div>
...
# app/javascripts/controllers/filters_controller.js

import { Controller } from "stimulus"

export default class extends Controller {
  update(event) {
    const value = event.target.value
    const spectrum = value == "none" ? false : true
    const spectrum_color = value == "none" ? "" : value

    this.targets.find("spectrum").value = spectrum
    this.targets.find("spectrum_color").value = spectrum_color
  }
}

Posted in How to handle long-running external services?

My Rails app calls a long-running external service which can take over a minute to return a response. I'm calling the service from a Sidekiq job but I don't want to tie up my worker for over a minute on every job waiting for the response.

I looked into a 'fire and forget' approach - call the service with the params then don't wait for a response, just move on. If I did that I'd end up with more jobs and code to check if the external job was completed. Not ideal.

I can have the external service call a webhook endpoint in my app with the result but I can't make the external service respond to the original call any earlier.

What are some good approaches to handling long-running external services?

Posted in How to import data with many associations?

Chris - how will this work with the associations already set up in the app?

Posted in How to import data with many associations?

I have an upcoming situation where I'll be importing a lot of data with many associations into a Rails app. The data will show the links between the objects using the original native database IDs.

When I import the data (not sure how I'll do it yet, probably CSV) what would be a good way to set up the correct associations?

I'm thinking of writing the original IDs of each object to an old_id column and using that somehow to create the associations in the database.

I'm concerned about the depth of associations here. Less than a dozen objects but all heavily associated to each other.

Any thoughts? Have I explained this well enough?

Posted in How to write System Tests in Rails Discussion

Really loving this testing series. I've been using RSpec for years but it looks like Minitest now covers a lot of the functionality that made me choose RSpec back then.

One of the things I don't like about RSpec is the amount of setup and gems required. Going with the built-in testing framework avoids all that, which is a definite win for beginners and quick-starting projects.

Be great to see your comparison of Minitest and RSpec.

How about headless browsers for system tests? I see you can switch to :headless_chrome but do you still get screenshots with that? Also, is a headless browser any faster?

Posted in Parsing a Ruby hash sent to a Javascript API

The answer turned out to be very simple, of course 😉

Use to_json on the nested params in Ruby:
params = { str: "word", bool: true, hash: { number: 123 }.to_json }
which sends:
GET /test.js?str=word&bool=true&hash=%7B%22number%22%3A123%7D
resulting in hash as {"number":123} in JS.

Use regular JSON parse to get the value: JSON.parse(hash).number gives 123 🎉

Posted in Parsing a Ruby hash sent to a Javascript API

Sending data from Rails via HTTParty to a Node endpoint:

params = { str: "word", bool: true, hash: { number: 123 } }
result = HTTParty.get("http://localhost/test.js", query: params)

It works fine without the nested hash in the params but when I add that in things get hairy.

If I send it as shown above the Node endpoint receives:
GET /test.js?str=word&bool=true&hash[number]=123 and hash is undefined

If I convert the nested hash to a string:
params = { str: "word", bool: true, hash: { number: 123 }.to_s } then Node receives this:
GET /test.js?str=word&bool=true&hash=%7B%3Anumber%3D%3E123%7D and hash is available in JS as: "{:number=>123}" but I can't figure out how to convert this to a JS object.

If I convert the nested hash to query:
params = { str: "word", bool: true, hash: { number: 123 }.to_query } then Node receives this:
GET /test.js?str=word&bool=true&hash=number%3D123 and hash is available in JS as: "number=123".

How do I access that nested hash in JS? How do I convert it to a usable object? I don't mind which of the various ways to send the nested params, I just to access them easily.

Excellent, cheers. Good time for me to dive into Simulus then! 😀

Right, Javascript does sound like the better route. Would you hijack the form submit and set the values there? Or would you respond to changes on the radio button inputs as they happen then let the form submit normally?

I have a spectrum boolean column and a spectrum_color string column and I'd like to display the options to user as shown in this image. I'm using Tailwind CSS and the Jumpstart Pro Rails template so the visual side is easy, just not sure what form helpers to use to get the values from 3 radio buttons to fill one boolean field and a string field.

EDIT: can't get an image to display in this post. Here's a link: https://wmd.d.pr/AMZFsu

Posted in Is this a good fit for storing JSON in database?

Thanks Chris. I found your video on this right after I posted. https://gorails.com/episodes/preferences-settings-with-activerecord-store

Looking at the typed_store gem now 👍 https://github.com/byroot/activerecord-typedstore

logo Created with Sketch.

Ruby on Rails tutorials, guides, and screencasts for web developers learning Ruby, Rails, Javascript, Turbolinks, Stimulus.js, Vue.js, and more. Icons by Icons8

© 2020 GoRails, LLC. All rights reserved.