DEV Community

Abdelkader Boudih
Abdelkader Boudih

Posted on

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

Top comments (0)