Blog

Thoughts from my daily grind

ActsAsTaggableOn Error - #tagged_with no results

Posted by Ziyan Junaideen |Published: 28 March 2022 |Category: Ruby on Rails
Default Upload |

ActsAsTaggableOn is a popular Ruby on Rails library to effortlessly introduce tagging to ActiveRecord models. It is one of the most common libraries I have used over the years including in this blog. I encountered what seem to be an issue with ActsAsTaggable #tagged_with method not properly selecting the tagged resource class.

Background

A few months back I updated my blog. I wanted to add tutorials and product reviews. I decided to go STI (single-table inheritance). I ended up with Posts::Blog, Posts::Tutorial and Posts::ProductReview. But the tagged_with method wasn't returning any posts and I decide to inspect the SQL.

Posts::Blog.tagged_with "Rails 5" # => []
Posts::Blog.tagged_with("Rails 5").to_sql

# Note that the SQL is not of the exact query

 "SELECT \"posts\".* FROM \"posts\" INNER JOIN \"taggings\" \"post_taggings_86357b1\" ON \"post_taggings_86357b1\".\"taggable_id\" = \"posts\".\"id\" AND \"post_taggings_86357b1\".\"taggable_type\" = 'Post' AND \"post_taggings_86357b1\".\"tag_id\" IN (SELECT \"tags\".\"id\" FROM \"tags\" WHERE LOWER(\"tags\".\"name\") ILIKE 'rails 5' ESCAPE '!') WHERE \"posts\".\"type\" = 'Posts::Blog' AND \"posts\".\"aasm_state\" = 'published' AND \"posts\".\"child_post\" = FALSE AND \"posts\".\"aasm_state\" = 'published' ORDER BY \"posts\".\"published_at\" DESC LIMIT 30 OFFSET 0"

Notice that the taggable_type: 'Post' and not Posts::Blog. I looked for a solution and looks like the library in the past has had a similar issue. But I couldn't find a solution for the tagged_with method.

So I updated the query as follows:

# app/concepts/posts/operations/marketing/index.rb
module Post::Operation::Marketing
  class Index < Trailblazer::Operation
    # ...

    def filter(ctx, params:, **)
      model = ctx[:model]

      posts = resolve_policy(ctx).parent_posts
      posts = posts.includes(%i[featured_image])
      posts = posts.search(ctx[:model].query) if ctx[:model].query.present?
      posts = posts.published unless params[:all] == 'true'
      posts = posts.where(category_id: params[:category_id]) if params[:category_id]

      # Basically to join `tag` through `taggings` and query the tag name.
      posts = posts.joins(taggings: :tag).where(tag: { name: params[:tag] }) if params[:tag]

      posts = posts.order(published_at: :desc)

      ctx[:posts] = posts
    end

    # ...
end

Once the query was updated the tag filter is operational. I wish there was a way to fix it, but for now, this will do.

About the Author

Ziyan Junaideen -

Ziyan is an expert Ruby on Rails web developer with 8 years of experience specializing in SaaS applications. He spends his free time he writes blogs, drawing on his iPad, shoots photos.

Comments