DEV Community

Abdelkader Boudih
Abdelkader Boudih

Posted on

3

NoFlyList: Custom Tag Screening with NoFlyList

Tag parsing seems simple until you handle real user input.

Let's explore how NoFlyList's transformers handle messy tag data submitted.

Basic Setup

rails generate no_fly_list:transformer
Enter fullscreen mode Exit fullscreen mode

Default transformer:

module ApplicationTagTransformer
  module_function

  def parse_tags(tags)
    if tags.is_a?(Array)
      tags
    else
      tags.split(separator).map(&:strip).compact
    end
  end

  def recreate_string(tags)
    tags.join(separator)
  end

  def separator
    ','
  end
end
Enter fullscreen mode Exit fullscreen mode

Real-World Examples

Hashtag Transformer

module HashtagTransformer
  module_function

  def parse_tags(tags)
    return tags if tags.is_a?(Array)

    tags.scan(/#[\w-]+/).map { |tag| tag.delete('#') }
  end

  def recreate_string(tags)
    tags.map { |tag| "##{tag}" }.join(' ')
  end
end

class Post < ApplicationRecord
  include NoFlyList::TaggableRecord

  has_tags :hashtags, transformer: HashtagTransformer
end

post = Post.new(content: "Check out #rails #ruby #webdev")
post.hashtags_list = post.content  # Extracts: ["rails", "ruby", "webdev"]
Enter fullscreen mode Exit fullscreen mode

Multi-Language Transformer

module MultiLangTransformer
  module_function

  def parse_tags(tags)
    return tags if tags.is_a?(Array)

    tags.split('|').map do |tag_pair|
      lang, tag = tag_pair.split(':').map(&:strip)
      "#{lang}:#{tag}"
    end
  end

  def recreate_string(tags)
    tags.join(' | ')
  end
end

class Article < ApplicationRecord
  has_tags :keywords, transformer: MultiLangTransformer
end

article.keywords_list = "en:ruby | es:rubí | fr:rubis"
Enter fullscreen mode Exit fullscreen mode

Hierarchical Tag Transformer

module CategoryTransformer
  module_function

  def parse_tags(tags)
    return tags if tags.is_a?(Array)

    tags.split('>').map(&:strip)
  end

  def recreate_string(tags)
    tags.join(' > ')
  end
end

class Product < ApplicationRecord
  has_tags :categories, transformer: CategoryTransformer
end

product.categories_list = "Electronics > Computers > Laptops"
Enter fullscreen mode Exit fullscreen mode

Custom Normalization

module NormalizedTransformer
  module_function

  def parse_tags(tags)
    tags = tags.split(',') unless tags.is_a?(Array)

    tags.map do |tag|
      tag.strip
         .downcase
         .gsub(/[^a-z0-9\s-]/, '')  # Remove special chars
         .gsub(/\s+/, '-')          # Spaces to hyphens
    end.uniq.compact
  end

  def recreate_string(tags)
    tags.join(', ')
  end
end

class Photo < ApplicationRecord
  has_tags :labels, transformer: NormalizedTransformer
end

photo.labels_list = "Nature Photos, WILDLIFE shots, Outdoor-Photography"
# Transforms to: ["nature-photos", "wildlife-shots", "outdoor-photography"]
Enter fullscreen mode Exit fullscreen mode

Context-Specific Transformers

Mix transformers based on tag context:

class Article < ApplicationRecord
  include NoFlyList::TaggableRecord

  has_tags :categories, transformer: CategoryTransformer
  has_tags :hashtags, transformer: HashtagTransformer
  has_tags :keywords, transformer: MultiLangTransformer
end
Enter fullscreen mode Exit fullscreen mode

Testing Transformers

class TransformerTest < ActiveSupport::TestCase
  test "hashtag parsing" do
    input = "#ruby #rails doing #testing"
    expected = ["ruby", "rails", "testing"]
    assert_equal expected, HashtagTransformer.parse_tags(input)
  end

  test "hierarchical parsing" do
    input = "Tech > Software > Tools"
    expected = ["Tech", "Software", "Tools"]
    assert_equal expected, CategoryTransformer.parse_tags(input)
  end
end
Enter fullscreen mode Exit fullscreen mode

Remember: Transformers are your first line of defense against messy tag data. Like TSA agents, they ensure only properly formatted tags make it through to your system.

Part4

Speedy emails, satisfied customers

Postmark Image

Are delayed transactional emails costing you user satisfaction? Postmark delivers your emails almost instantly, keeping your customers happy and connected.

Sign up

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more