0.0
The project is in a healthy, maintained state
A Ruby gem to filter and check for profanity in strings, with support for custom word lists
2005
2006
2007
2008
2009
2010
2011
2012
2013
2014
2015
2016
2017
2018
2019
2020
2021
2022
2023
2024
2025
 Dependencies

Development

Runtime

 Project Readme

NaughtyWords

A Ruby gem for filtering profanity from text. Features include:

  • Built-in deny and allow lists
  • Database integration for custom word lists
  • Runtime word overrides
  • Word boundary matching (for checks)
  • Case insensitive matching
  • Optional severity-based filtering for DB deny words

Installation

Add this line to your application's Gemfile:

gem 'naughty_words'

And then execute:

bundle install

Or install it yourself as:

gem install naughty_words

Basic Usage

# Check if a string contains profanity
NaughtyWords.check(string: "hello world")  # => false
NaughtyWords.check(string: "fuck this")    # => true

# Filter profanity from a string
NaughtyWords.filter(string: "hello world")  # => "hello world"
NaughtyWords.filter(string: "fuck this")    # => "**** this"

# Use custom replacement character
NaughtyWords.filter(string: "fuck this", replacement: "@")  # => "@@@@ this"

Configuration

Configure the gem's behavior:

NaughtyWords.configure do |config|
  # Match whole words only (default: true)
  # When true: "fuck" matches "fuck" but not "fuckthis"
  # When false: "fuck" matches both "fuck" and "fuckthis"
  config.word_boundaries = true

  # Use built-in word lists (default: true)
  # Set to false to use only database or runtime overrides
  config.use_built_in_lists = true

  # Only consider DB deny words at or above this severity (optional)
  # One of: "high", "medium", "low". nil means all severities.
  config.minimum_severity = nil
end

# For tests or to return to defaults
NaughtyWords::Config.reset!

Database Integration

The gem can use a database (ActiveRecord) to store custom word lists.

  1. Install migration, model, and initializer (recommended):
rails generate naughty_words:install

This adds:

  • db/migrate/create_naughty_words_lists.rb
  • app/models/naughty_words/word_list.rb
  • config/initializers/naughty_words.rb
  1. Add optional columns if you need them

If you plan to use category, severity ("high" | "medium" | "low"), or metadata (JSON), add these columns to your migration before running it. Example:

change_table :naughty_words_lists do |t|
  t.string :category        # optional
  t.string :severity        # optional, one of "high", "medium", "low"
  t.json   :metadata, default: {}  # optional
end

Then run:

rails db:migrate
  1. Add words to your lists:
# Basic usage
NaughtyWords::WordList.create!(word: "badword", list_type: "deny")
NaughtyWords::WordList.create!(word: "scunthorpe", list_type: "allow")

# With optional metadata (requires columns above)
NaughtyWords::WordList.create!(
  word: "badword",
  list_type: "deny",
  context: "Added due to user complaints",
  added_by: "john@example.com",
  severity: "high",
  category: "insults",
  metadata: { reported_by: "forum_moderator" }
)
  1. View your lists:
# Get just the words
NaughtyWords.show_list(list: "deny")
# => ["badword", "otherbadword", ...]

# Get full records with metadata
records = NaughtyWords.show_list(list: "deny", include_metadata: true)
records.first
# => #<NaughtyWords::WordList ... word: "badword", list_type: "deny", ...>

# Query with metadata (if you use it)
words = NaughtyWords::WordList.where(list_type: "deny")
                             .where(added_by: "john@example.com")
                             .where("metadata->>'category' = ?", "insults")

View only the built-in default lists

If you want to see the gem's built-in lists (ignoring anything in your database):

# Ensure built-ins are enabled (default: true)
NaughtyWords::Config.use_built_in_lists = true

# If you have no DB entries, this already shows the built-ins
NaughtyWords.show_list(list: "deny")   # => built-in deny words
NaughtyWords.show_list(list: "allow")  # => built-in allow words

# If you DO have DB entries and want to isolate the built-ins only:
built_in_deny  = NaughtyWords.show_list(list: "deny")  - NaughtyWords::WordList.deny_list.pluck(:word)
built_in_allow = NaughtyWords.show_list(list: "allow") - NaughtyWords::WordList.allow_list.pluck(:word)

built_in_deny.first(10)
  1. Severity-based checks from DB (optional)
# Only consider DB deny words at or above "medium"
NaughtyWords.configure { |c| c.minimum_severity = "medium" }

# Or query explicitly via scopes
NaughtyWords::WordList.by_severity("high").pluck(:word)
NaughtyWords::WordList.by_severity("high").by_category("insults").pluck(:word)

If you don't install the DB table, the gem will work using only the built-in lists.

Runtime Overrides

Override word lists temporarily during runtime:

# Allow a word that's normally blocked
NaughtyWords::Config.allow_word("someword")

# Block a word that's normally allowed
NaughtyWords::Config.deny_word("otherword")

# Remove an override
NaughtyWords::Config.remove_override("someword")

Overrides:

  • Take precedence over both built-in lists and database lists
  • Are case insensitive
  • Reset when your application restarts
  • Perfect for testing or temporary customizations

How It Works

  1. When checking/filtering text:

    • First checks runtime overrides
    • Then checks built-in lists (if enabled)
    • Then checks database lists (if present)
  2. Word matching is:

    • Always case insensitive
    • Configurable for word boundaries (affects checks)
    • Handles special characters and spaces
  3. Priority order:

    1. Runtime overrides (highest)
    2. Built-in lists
    3. Database lists (lowest)
  4. Filtering details:

    • Replacement proceeds longest-to-shortest denied words.
    • Filtering masks any occurrence of denied words (case-insensitive), regardless of word_boundaries.
    • Example: with boundaries on, check may pass for “scunthorpe”, but if “cunt” is denied, filter will still mask “cunt” inside “scunthorpe”.

Default list philosophy

The built-in deny list is intentionally minimal and neutral by default.

  • Focuses on single-word profanities and slurs only
  • Avoids medical/sexual terms and general sexual content
  • Avoids multi-word phrases and “moral policing”

Customize to your community via the database layer:

  • Add phrases (e.g., “eat my ass”) to naughty_words_lists with category/severity
  • Use minimum_severity to tune strictness globally
  • Use runtime overrides for temporary exceptions

Examples

Basic Filtering

# Simple profanity check
NaughtyWords.check(string: "hello world")  # => false
NaughtyWords.check(string: "fuck this")    # => true

# Filter with default replacement (*)
NaughtyWords.filter(string: "fuck this")  # => "**** this"

# Custom replacement character
NaughtyWords.filter(string: "fuck this", replacement: "@")  # => "@@@@ this"

Word Boundaries

# With word_boundaries = true (default)
NaughtyWords.check(string: "fuck")      # => true
NaughtyWords.check(string: "fuckthis")  # => false

# With word_boundaries = false
NaughtyWords.configure { |c| c.word_boundaries = false }
NaughtyWords.check(string: "fuckthis")  # => true

Database Integration

# Add custom words
NaughtyWords::WordList.create!(word: "badword", list_type: "deny")
NaughtyWords::WordList.create!(word: "goodword", list_type: "allow")

# View lists with metadata
NaughtyWords.show_list(list: "deny", include_metadata: true)
# => [#<NaughtyWords::WordList id: 1, word: "badword", list_type: "deny", created_at: ...>]

Runtime Overrides

# Override the built-in lists
NaughtyWords::Config.allow_word("fuck")  # Allow this word
NaughtyWords.check(string: "fuck")       # => false

# Multiple overrides
NaughtyWords::Config.allow_word("fuck")
NaughtyWords::Config.allow_word("shit")
NaughtyWords.filter(string: "fuck this shit")  # => "fuck this shit"

# Remove overrides
NaughtyWords::Config.remove_override("fuck")
NaughtyWords.check(string: "fuck")  # => true (back to default)

Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/jaarnie/naughty_words.

License

The gem is available as open source under the terms of the MIT License.