Jason Chee

Background

I am a fullstack web developer with 7+ years experience building web products while being hands-on in each layer of the product stack—from planning to execution and shipping. I’ve worked exclusively with Ruby and Ruby on Rails throughout my career.

Read more about me

Notes

March 25, 2025

Polymorphic associations checklist

When you’re unsure to use a polymorphic association:

Does this table belong to multiple, unrelated models?

👍 good use case: a Notification might belong to a Task, Comment, or a User.

👎 bad use case: a Book belongs to an Author…this is a regular belongs_to relationship

Do I need a flexible relationship?

You want a single association that can reference different models dynamically. You don’t want to add a new foreign key each time you need a new type of association.

example: instead of adding post_id, product_id, and event_id to Taggings, you use taggable_type and taggable_id.

Is this relationship likely to grow in complexity over time?

You anticipate that this association will be needed by many different models now or in the future.

example: a Tag might eventually need to attach to a Post, Photo, Video, or Event.

Do I want to avoid schema changes?

You want to avoid altering the schema every time a new model needs to use the association. Polymorphic associations let you add new relationships without additional migrations

When to avoid polymorphic associations

• If the associated models are closely related or known to be limited in number.

• If you need JOIN queries across polymorphic types.
example: since commentable_type varies row-to-row(ex: a Post commentable_type, or a Photo commentable_type) you are unable to use a single SQL JOIN.

• If you need foreign key constraints.

Lets create a polymorphic association!

create migration:

# db/migrate/20240411123456_create_comments.rb
class CreateComments < ActiveRecord::Migration[7.0]
  def change
    create_table :comments do |t|
      t.text :body
      t.references :commentable, polymorphic: true, null: false

      t.timestamps
    end
  end
end

model setup:

# models/comment.rb
class Comment < ApplicationRecord
  belongs_to :commentable, polymorphic: true
end
# models/post.rb
class Post < ApplicationRecord
  has_many :comments, as: :commentable, dependent: :destroy
end
# models/photo.rb
class Photo < ApplicationRecord
  has_many :comments, as: :commentable, dependent: :destroy
end

usage example:

post = Post.create(title: "First Post")
photo = Photo.create(caption: "Nice view!")

# add comments to each
post.comments.create(body: "Great post!")
photo.comments.create(body: "Awesome picture!")

# fetch all comments
Comment.all.each do |comment|
  puts "#{comment.body} on #{comment.commentable.class.name} ##{comment.commentable.id}"
end

March 13, 2024

JavaScript libaries, Vue.js, Ruby on Rails and importmaps.

Here is an example how you can import a JavaScript library to your Vue component when using importmaps in Ruby on Rails

Step 1 - Pin the library in importmaps

Pin your libaray in config/importmaps.rb

pin "axios", to: "https://cdn.skypack.dev/pin/axios@v1.6.7-CtiTWk1RHZKnFCXj0sDG/mode=imports,min/optimized/axios.js", preload: true

Step 2 - Reference the library in your component

// app/javascript/components/HelloWorld.js
import axios from "axios"

const HelloWorld = {
  template: `
    <div>
      <h1></h1>
    </div>
  `,
  data() {
    return {
      message: 'Loading...'
    };
  },
	methods: {
    async fetchData() {
      try {
        const response = await axios.get('https://pokeapi.co/api/v2/pokemon/ditto');
        console.log(response);
        this.message = response.data.name
        // Update your component's data with the fetched data
      } catch (error) {
        console.error('There was an error fetching the data:', error);
        this.message = error
      }
    }
  },
	mounted() {
	  this.fetchData();
	},      
}

export default HelloWorld;

March 10, 2024

How to add Vue.js to your Ruby on Rails app with importmaps

Step 1 - Pin Vue to your project

in config/importmap.rb add the following line

pin "vue", to: "https://ga.jspm.io/npm:vue@3.2.26/dist/vue.esm-browser.js", preload: true

Step 2 - Initialize Vue app

create app/javascript/vue.js

import * as Vue from "vue"
import HelloWorld from "./components/HelloWorld"

document.addEventListener("DOMContentLoaded", () => {
  const element = document.querySelector("#app");
  if (element !== null) {
    const app = Vue.createApp({});
    
    // register your components
    app.component('hello-world', HelloWorld);

    app.mount("#app");
  }
});

Step 3 - Import Vue.js in application.js

in app/javascript/application.js add the following line

import "./vue"

Step 4 - Create components folder

create app/javascript/components

Then add your Vue component app/javascript/components/HelloWorld.js

const HelloWorld = {
  template: `
    <div>
      <h1></h1>
    </div>
  `,
  data() {
    return {
      message: 'Hello, Vue!'
    };
  }
}

export default HelloWorld;

Step 5 - Add to component to view

in home/index.html.erb

<div id="app">
  <hello-world></hello-world>
</div>

November 09, 2023

Using scope module in routes

Use namespace when you want to group routes under a common module (which will also affect the URL and helper methods). Use scope when you need more flexibility in terms of paths and controller organization, allowing you to keep the controller in the global namespace or in a different module than the URL might imply. The scope is often used when the URL structure doesn’t neatly map to the controller structure, which can sometimes be the case with APIs or user-facing URLs that need to be more SEO-friendly.

resources :posts do
	scope module: :posts do
		resources :publishes, only: :create
	end
end
# app/controllers/posts/publishes_controller.rb
class Posts::PublishesController < ApplicationController
	def create
		
	end
end
Prefix         Verb   URI Pattern          Controller#Action
post_publishes POST   /posts/:post_id/publishes(.:format)     posts/publishes#create

If we were to use namespace, it could look something like this.

namespace :posts do
	post `publishes/create`
end

October 13, 2023

Weird Rails 7 build break due to outdated npm

I am working on a new Ruby on Rails project with a friend. We bumped into a few development blockers that I have never experienced before. It turns out that if you are using an outdated version on npm, the pacakage.json which gets generated when you create a new Rails application will not include the necessary scripts needed in order to get ./bin/dev working.

Assuming your Ruby on Rails application is using cssbundling and jsbundling–if you see any build errors when trying to run the server with ./bin/dev you may want to try the following:

  • First update your node version to a newer stable version. I set mine to 18.17.1. I use asdf to manage ruby and nodejs versions. asdf local nodejs 18.17.1
  • Then update your npm version. I am using version 8.19.4. npm install -g npm@8.19.4
  • re-install cssbundling rails css:install:bootstrap
  • re-install jsbundling rails javascript:install:esbuild

In package.json, check to see if the scripts key includes five items for the value. Your Procfile.dev should look like the following:

web: env RUBY_DEBUG_OPEN=true bin/rails server
js: yarn build --watch
css: yarn watch:css

After updating npm and node version, yarn commands should work and you should be able to run the server with ./bin/dev.