Skip to content

Add classifier-rails gem or Rails generator for ActiveRecord integration #91

@cardmagic

Description

@cardmagic

Summary

Provide first-class Rails integration via either a classifier-rails gem or a Rails generator that sets up ActiveRecord-based storage automatically.

Motivation

After #90 ships, Rails users who want database persistence will need to:

  1. Create a migration manually
  2. Create a model manually
  3. Write their own ARStorage class
  4. Configure it in an initializer

This is fine for power users, but Rails is about convention over configuration. We should make the common case trivial:

rails generate classifier:install
rails db:migrate

# Done. It just works.

Option A: Rails Generator (Simpler)

Ship a generator in the main gem that's only loaded when Rails is detected.

$ rails generate classifier:install

      create  db/migrate/20250101000000_create_trained_classifiers.rb
      create  app/models/trained_classifier.rb
      create  config/initializers/classifier.rb

Generated migration:

class CreateTrainedClassifiers < ActiveRecord::Migration[7.0]
  def change
    create_table :trained_classifiers do |t|
      t.string :key, null: false, index: { unique: true }
      t.text :data, null: false
      t.timestamps
    end
  end
end

Generated model:

class TrainedClassifier < ApplicationRecord
  validates :key, presence: true, uniqueness: true
  validates :data, presence: true
end

Generated storage class:

# app/models/classifier_storage.rb
class ClassifierStorage < Classifier::Storage::Base
  def initialize(key:)
    @key = key
  end

  def write(data)
    TrainedClassifier.upsert({ key: @key, data: data }, unique_by: :key)
  end

  def read
    TrainedClassifier.find_by(key: @key)&.data
  end

  def delete
    TrainedClassifier.where(key: @key).delete_all
  end

  def exists?
    TrainedClassifier.exists?(key: @key)
  end
end

Generated initializer:

# config/initializers/classifier.rb
# Classifier is ready to use with ActiveRecord storage:
#
#   bayes = Classifier::Bayes.new('Spam', 'Ham')
#   bayes.storage = ClassifierStorage.new(key: "spam_filter")
#   bayes.save
#
# Or create a helper method in ApplicationController:
#
#   def spam_classifier
#     @spam_classifier ||= Classifier::Bayes.load(
#       storage: ClassifierStorage.new(key: "spam_filter")
#     )
#   end

Option B: Separate classifier-rails Gem

Create a classifier-rails gem that:

  1. Provides the generator
  2. Ships Classifier::Storage::ActiveRecord built-in
  3. Adds Rails-specific conveniences
# Gemfile
gem 'classifier-reborn'
gem 'classifier-rails'
# This class ships with the gem
bayes.storage = Classifier::Storage::ActiveRecord.new(key: "spam_filter")

Recommendation

Start with Option A (generator in main gem). It's simpler, no new gem to maintain, and covers 90% of Rails use cases.

If demand grows, extract to classifier-rails later.

Usage After Install

# Train and save
bayes = Classifier::Bayes.new('Spam', 'Ham')
bayes.storage = ClassifierStorage.new(key: "email_filter")
bayes.train_spam("Buy now!")
bayes.save

# Load in another request
bayes = Classifier::Bayes.load(storage: ClassifierStorage.new(key: "email_filter"))
bayes.classify("Free money")  # => "Spam"

Acceptance Criteria

  • rails generate classifier:install creates migration, model, storage class, initializer
  • Generator only available when Rails is loaded
  • Generated code follows Rails conventions
  • Works with Rails 6.1+
  • Documentation in README

Dependencies

Non-Goals

  • ❌ Auto-configuring storage globally (explicit is better)
  • ❌ ActiveRecord adapter shipped in core gem (users own generated code)
  • ❌ Supporting non-ActiveRecord Rails ORMs (Sequel, ROM, etc.)

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or request

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions