Skip to content

Support for Postgres Array type #45

@mhenrixon

Description

@mhenrixon

I love postgres array type. I tend to reach for it for any tagging system that isn't too complex. Most of all, I find it super fun to write code like this:

module Taggable
  extend ActiveSupport::Concern

  class Parser
    def parse(tags)
      case tags
      when String
        tags.split(/ *, */)
      else
        tags
      end
    end
  end

  def self.parser
    @parser ||= Parser.new
  end

  module ClassMethods
    def has_tags(*tag_def)
      parser = Taggable.parser
      tag_def.each do |tag_name|
        scope :"with_any_#{tag_name}", ->(tags) { where("#{tag_name} && ARRAY[?]::varchar[]", parser.parse(tags)) }
        scope :"with_all_#{tag_name}", ->(tags) { where("#{tag_name} @> ARRAY[?]::varchar[]", parser.parse(tags)) }
        scope :"without_any_#{tag_name}", ->(tags) { where.not("#{tag_name} && ARRAY[?]::varchar[]", parser.parse(tags)) }
        scope :"without_all_#{tag_name}", ->(tags) { where.not("#{tag_name} @> ARRAY[?]::varchar[]", parser.parse(tags)) }

        define_method :"#{tag_name}=" do |value|
          write_attribute tag_name,
            case value
            when String
              value.split(",").map(&:strip)
            else
              value
            end
        end

        define_method :"#{tag_name}_text" do
          (read_attribute(tag_name) || []).join ","
        end

        self.class.class_eval do
          define_method :"all_#{tag_name}" do |_options = {}, &block|
            subquery = unscoped.select("unnest(#{tag_name}) as tag")

            from(subquery).pluck(Arel.sql("distinct subquery.tag"))
          end

          define_method :"#{tag_name}_cloud" do |_options = {}, &block|
            subquery =
              unscoped
                .select("unnest(#{tag_name}) as tag, count(*) as count")
                .group(:tag)
                .order(count: :desc)

            from(subquery).pluck(Arel.sql("subquery.tag, subquery.count"))
          end
        end
      end
    end
  end
end

I just sloppy pasted this from the internet; it does get it done, even if it isn't pretty.

The idea is to tag, for example, blog posts.

When playing around with the possibilities of doing this in Administrate, I discovered a few limitations that would be absolutely amazing if they could be ironed out in madmin.

  1. I could use neither select nor create some multi-select because it still didn't allow me to add tags dynamically. In a back-office type of system like madmin, it doesn't need to be pretty. It would be to select tags but also have the ability to add new tags on the fly.
  2. While the code above takes both a comma-separated string and an array as input, it does display bizarre in administrate. I want better support for both editing and displaying array type built-in if possible.

If there is a way already, please let me know. Also, happy to contribute something if I can.

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions