About Rhino
Rhino is a framework built around Model-Driven Development (MDD), which is the core of its "vibe-coding" friendly approach. Instead of making you write repetitive boilerplate code, Rhino automatically generates secure API endpoints and routes directly from your data models. This allows you to just focus on defining your application's logic—like your Category, Blog, and BlogPost models—and trust the framework to handle the rest. This model-first philosophy is also naturally AI-friendly, as the well-defined models give AI tools the precise context they need to understand your intent and help you build features with minimal friction.
Overview
The Rhino Notifications module (rhino_project_notifications) is a Rails engine that wraps and extends the activity_notification gem (v2.3.3) to provide a complete notification system for Rhino-based applications. It provides both backend (Rails API) and frontend (React) components for managing user notifications.
This guide will first explain the base activity_notification gem capabilities, then detail what Rhino adds on top, and finally walk through setup and usage.
  
  
  Part 1: The Base activity_notification Gem
Before understanding what Rhino adds, let's understand the underlying gem's capabilities.
Core Concepts
The activity_notification gem provides a flexible notification system with these key features:
1. Target (Receiver of Notifications)
Models that can receive notifications use acts_as_target:
class User < ApplicationRecord
  acts_as_target email: :email
end
This makes User a notification recipient and provides methods like:
- 
user.notifications- All notifications
- 
user.unopened_notification_count- Count of unread
- 
user.notification_opened?(notification)- Check if opened
- 
user.open_notification(notification)- Mark as read
2. Notifiable (Trigger of Notifications)
Models that trigger notifications use acts_as_notifiable:
class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :post
  acts_as_notifiable :users,
    targets: ->(comment, key) { [comment.post.user] },
    group: :post,
    printable_name: ->(comment) { comment.body.truncate(30) }
end
3. Automatic Triggering
Notifications can be created automatically on model actions:
acts_as_notifiable :users,
  tracked: { only: [:create] }  # Auto-notify on creation
Or manually triggered:
comment.notify :users, key: "comment.create"
4. Grouping & Keys
- 
Keys: String identifiers for notification types (e.g., "comment.create")
- Grouping: Consolidate related notifications to prevent spam
acts_as_notifiable :users,
  group: :post,  # Group all comments on same post
  dependent_notifications: :update_group_and_destroy
Result: Multiple comments on the same post = 1 notification
5. Multiple Delivery Channels
- Database (default) - Store in DB
- Email - Send email notifications
- Push - Web push notifications
- ActionCable - Real-time WebSocket updates
Example: Rails App Without Rhino
Here's how you'd use activity_notification in a standard Rails app:
Step 1: Installation
bundle add activity_notification
rails generate activity_notification:install
rails db:migrate
Step 2: Configuration
# config/initializers/activity_notification.rb
ActivityNotification.configure do |config|
  config.email_enabled = true
  config.mailer = "ActivityNotification::Mailer"
  config.action_cable_enabled = true
  config.group_expiry_threshold = 5.minutes
end
Step 3: Models
# app/models/user.rb
class User < ApplicationRecord
  acts_as_target email: :email
  has_many :posts
  has_many :comments
end
# app/models/comment.rb
class Comment < ApplicationRecord
  belongs_to :user
  belongs_to :post
  acts_as_notifiable :users,
    targets: ->(comment, key) { [comment.post.user] },
    notifier: :user,
    group: :post,
    printable_name: ->(comment) { comment.body.truncate(30) }
end
Step 4: Controller
# app/controllers/comments_controller.rb
class CommentsController < ApplicationController
  def create
    @comment = @post.comments.build(comment_params.merge(user: current_user))
    if @comment.save
      @comment.notify :users, key: "comment.create"
      redirect_to @post, notice: "Comment added!"
    else
      render :new
    end
  end
end
Step 5: Views
<!-- app/views/layouts/application.html.erb -->
<% current_user.notifications.unopened.each do |notification| %>
  <%= render_notification notification %>
<% end %>
<!-- app/views/activity_notification/notifications/_comment_create.html.erb -->
<%= link_to notification.notifier.name, user_path(notification.notifier) %>
commented on your
<%= link_to "post", post_path(notification.group) %>:
"<%= notification.notifiable.body.truncate(20) %>"
How Everything Connects
| Step | What Happens | 
|---|---|
| 1 | User adds a comment | 
| 2 | Commenttriggersnotify :users, key: "comment.create" | 
| 3 | Gem finds targets (post author via lambda) | 
| 4 | Notification record created in DB | 
| 5 | Delivery channels execute (email, ActionCable) | 
| 6 | Author sees it in their notifications list | 
| 7 | When viewed, opened_attimestamp is set | 
Key Features Summary
| Feature | Description | 
|---|---|
| Target (receiver) | acts_as_targetin User | 
| Notifiable (trigger) | acts_as_notifiablein Comment | 
| Grouping | group: :postconsolidates notifications | 
| Key-based templates | "comment.create"maps to view partial | 
| Channels | DB, email, ActionCable configurable | 
| Persistence | Stored in notificationstable | 
| Read state | Managed via .open!,.unopened,.opened | 
| Deletion | Manual (not automatic) | 
  
  
  Part 2: What Rhino Adds to activity_notification
Rhino wraps activity_notification to provide a modern SPA architecture with these additions:
1. 🎯 RESTful JSON API Endpoints
Base gem: Provides HTML views and controllers for traditional Rails apps
Rhino adds: Automatic JSON API endpoints
# rhino/rhino-project/gems/rhino_project_notifications/config/routes.rb
Rails.application.routes.draw do
  scope Rhino.namespace do
    notify_to :users, api_mode: true, with_devise: :users
  end
end
Creates these endpoints:
- 
GET /api/users/:user_id/notifications- List with filtering
- 
GET /api/users/:user_id/notifications/:id- Show single
- 
PUT /api/users/:user_id/notifications/:id/open- Mark as opened
- 
POST /api/users/:user_id/notifications/open_all- Mark all opened
- 
DELETE /api/users/:user_id/notifications/:id- Delete
JSON Response Format:
{
  "data": {
    "count": 3,
    "notifications": [
      {
        "id": 1,
        "notifiable_type": "Comment",
        "notifiable_id": 42,
        "notifiable_path": "/articles/10/comments/42",
        "printable_notifiable_name": "Comments on Article Title",
        "key": "comment.create",
        "group_owner_id": 10,
        "opened_at": null,
        "created_at": "2025-10-17T10:30:00.000Z",
        "parameters": {
          "article_id": 10
        }
      }
    ]
  }
}
Query Parameters:
- 
filter=unopened- Only unopened notifications
- 
filter=opened- Only opened notifications
- 
filter=all- All notifications
- 
limit=10- Limit results
2. 🎨 Complete Frontend React Integration
Base gem: No frontend - you build your own
Rhino provides:
React Query Hooks
// rhino/rhino-project/packages/core/src/queries/notifications.js
import { useQuery, useMutation } from '@tanstack/react-query';
import { networkApiCall } from '../lib/networking';
import { useUserId } from '../hooks/auth';
const basePath = (userId) => `/api/users/${userId}/notifications`;
const fullPath = (userId, queryPath) => `${basePath(userId)}/${queryPath}`;
// Fetch unopened notifications
export const useNotifications = () => {
  const userId = useUserId();
  return useQuery({
    queryKey: ['notifications-index'],
    queryFn: ({ signal }) =>
      networkApiCall(fullPath(userId, '?filter=unopened&limit=10'), { signal }),
    enabled: !!userId
  });
};
// Mark all as opened
export const useNotificationsOpenAll = () => {
  const userId = useUserId();
  return useMutation({
    mutationFn: () =>
      networkApiCall(fullPath(userId, 'open_all'), { method: 'post' })
  });
};
// Mark single notification as opened
export const useNotificationsOpen = () => {
  const userId = useUserId();
  return useMutation({
    mutationFn: (notificationId) =>
      networkApiCall(fullPath(userId, `${notificationId}/open`), {
        method: 'put'
      })
  });
};
Pre-built NotificationMenu Component
// rhino/rhino-project/packages/core/src/components/app/NotificationMenu.js
import { NavLink } from 'react-router-dom';
import {
  Badge,
  DropdownItem,
  DropdownMenu,
  DropdownToggle,
  UncontrolledDropdown
} from 'reactstrap';
import { NavIcon } from '../icons';
import {
  useNotifications,
  useNotificationsOpen,
  useNotificationsOpenAll
} from '../../queries/notifications';
export const NotificationMenu = () => {
  const { data: { data: notifications } = {}, refetch } = useNotifications();
  const { mutate: openAll } = useNotificationsOpenAll();
  const { mutate: openOne } = useNotificationsOpen();
  const hasNotifications = notifications?.count > 0;
  const handleItemClick = (notificationId) =>
    openOne(notificationId, {
      onSuccess: () => refetch()
    });
  const handleClick = () => openAll({ onSuccess: () => refetch() });
  return (
    <UncontrolledDropdown nav direction="up">
      <DropdownToggle nav caret className="d-flex align-items-center text-light no-arrow">
        <NavIcon icon="bell" extraClass="flex-shrink-0" />
        <span className="d-block ms-2 overflow-hidden flex-grow-1">
          Notifications
        </span>
        {hasNotifications && <Badge pill>{notifications?.count}</Badge>}
      </DropdownToggle>
      <DropdownMenu dark end>
        {notifications?.notifications?.map((n) => (
          <DropdownItem
            key={n.id}
            tag={NavLink}
            to={n.notifiable_path}
            onClick={() => handleItemClick(n.id)}
          >
            {n.printable_notifiable_name}
          </DropdownItem>
        ))}
        {hasNotifications ? (
          <>
            <DropdownItem divider />
            <DropdownItem disabled={!hasNotifications} onClick={handleClick}>
              Mark All Opened
            </DropdownItem>
          </>
        ) : (
          <DropdownItem disabled>
            <em>No unread notifications</em>
          </DropdownItem>
        )}
      </DropdownMenu>
    </UncontrolledDropdown>
  );
};
3. 🔗 Rhino Routing Integration
Base gem: You manually define notifiable_path
Rhino adds: route_frontend helper that auto-generates hierarchical frontend paths
class Comment < ApplicationRecord
  belongs_to :article
  belongs_to :user
  # Rhino ownership integration
  rhino_owner :article
  rhino_references %i[article user]
  acts_as_notifiable :users,
    targets: ->(comment, _key) { [comment.article.user] },
    notifiable_path: :comment_notifiable_path,
    printable_name: ->(comment) { "Comment on #{comment.article.title}" }
  # Uses Rhino's routing helper
  def comment_notifiable_path
    route_frontend  # Auto-generates: "/123/articles/456/comments/789"
  end
end
The route_frontend method integrates with Rhino's ownership model (rhino_owner) to build hierarchical URLs based on your base owner (e.g., Organization).
4. 🚀 Simplified Installation
Base gem: Multiple manual steps
Rhino provides: One-command generator
rails generate rhino_notifications:install
This single command:
- ✅ Runs activity_notification:install(creates initializer)
- ✅ Runs activity_notification:migration(creates DB tables)
- ✅ Adds acts_as_target email: :emailto User model
5. ⚙️ API-Optimized Defaults
Base gem defaults:
- Email enabled by default
- Subscriptions enabled by default
- ActionCable optional
Rhino defaults (API-first):
# config/initializers/activity_notification.rb
config.email_enabled = false              # Disabled for API-only
config.subscription_enabled = false       # Disabled for simplicity
config.action_cable_enabled = false       # Disabled by default
config.action_cable_api_enabled = false   # Disabled by default
This creates a cleaner API-only setup without email/WebSocket complexity unless you explicitly enable them.
6. 🔐 Authorization Integration
The notify_to :users, with_devise: :users route configuration integrates with Rhino's authentication system to ensure users can only access their own notifications.
Comparison: Base Gem vs. Rhino
| Feature | Base activity_notification | Rhino Addition | 
|---|---|---|
| Model configuration | ✅ acts_as_target,acts_as_notifiable | ✅ Same (manual) | 
| Grouping | ✅ Yes | ✅ Same | 
| Notification keys | ✅ Yes | ✅ Same | 
| Parameters | ✅ Custom JSON data | ✅ Same | 
| HTML Views | ✅ Provided | ❌ Not used (API-only) | 
| JSON API | ⚠️ Basic (optional) | ⭐️ Full RESTful API | 
| Frontend | ❌ Build your own | ⭐️ React components + hooks | 
| Routing Helper | ❌ Manual | ⭐️ route_frontendintegration | 
| Ownership Model | ❌ None | ⭐️ Rhino owner hierarchy | 
| Installation | ⚠️ Multiple steps | ⭐️ One-command generator | 
| Email Delivery | ✅ Enabled by default | ⚠️ Disabled by default | 
| ActionCable | ✅ Optional | ⚠️ Disabled by default | 
| Subscriptions | ✅ Enabled by default | ⚠️ Disabled by default | 
Part 3: Setup and Usage in a Rhino Project
Installation
Step 1: Run the Generator
rails generate rhino_notifications:install
This creates:
File 1: config/initializers/activity_notification.rb
- Configuration for the notification system
- Contains ~100+ configuration options
- Rhino sets sensible API-first defaults
File 2: db/migrate/TIMESTAMP_create_activity_notification_tables.rb
- Creates notificationstable (stores all notifications)
- Creates subscriptionstable (for subscription management if enabled)
File 3: Modifies app/models/user.rb
- Adds acts_as_target email: :email
Step 2: Run Migrations
rails db:migrate
This creates the database tables.
Database Schema
Notifications Table
create_table :notifications do |t|
  t.belongs_to :target,     polymorphic: true, index: true, null: false  # Who receives it (User)
  t.belongs_to :notifiable, polymorphic: true, index: true, null: false  # What it's about (Comment, etc.)
  t.string     :key,                                        null: false  # Notification type identifier
  t.belongs_to :group,      polymorphic: true, index: true              # Groups related notifications
  t.integer    :group_owner_id,                index: true              # Owner of notification group
  t.belongs_to :notifier,   polymorphic: true, index: true              # Who triggered it
  t.text       :parameters                                              # Custom data (JSON)
  t.datetime   :opened_at                                               # Read/unread tracking
  t.timestamps null: false
end
Key Fields:
- 
target: Who receives the notification (polymorphic, typically User)
- 
notifiable: What the notification is about (polymorphic, can be any model)
- 
key: String identifier (e.g., "comment.create")
- 
group: Optional grouping to consolidate related notifications
- 
notifier: Who/what caused the notification
- 
parameters: JSON text field for custom data
- 
opened_at:NULL= unread,NOT NULL= read
Subscriptions Table
create_table :subscriptions do |t|
  t.belongs_to :target,     polymorphic: true, index: true, null: false
  t.string     :key,                           index: true, null: false
  t.boolean    :subscribing,                                null: false, default: true
  t.boolean    :subscribing_to_email,                       null: false, default: true
  t.datetime   :subscribed_at
  t.datetime   :unsubscribed_at
  t.datetime   :subscribed_to_email_at
  t.datetime   :unsubscribed_to_email_at
  t.text       :optional_targets
  t.timestamps null: false
end
add_index :subscriptions, [:target_type, :target_id, :key], unique: true
Configuration
After installation, review and customize:
# config/initializers/activity_notification.rb
ActivityNotification.configure do |config|
  config.enabled = true
  config.orm = :active_record
  config.notification_table_name = "notifications"
  config.subscription_table_name = "subscriptions"
  # Email notifications (disabled by default in Rhino)
  config.email_enabled = false
  # Subscription management (disabled by default in Rhino)
  config.subscription_enabled = false
  # ActionCable/WebSocket (disabled by default in Rhino)
  config.action_cable_enabled = false
  config.action_cable_api_enabled = false
  config.opened_index_limit = 10
end
Note on acts_as_target email: :email:
This parameter tells the gem where to find the email field (field mapping), NOT whether to send emails. The config.email_enabled = false is what actually disables email sending.
Model Configuration
Target Model (User)
The generator already configured this:
# app/models/user.rb
class User < Rhino::User
  acts_as_target email: :email
  # This provides methods:
  # - user.notifications
  # - user.notification_index(filter: 'unopened', limit: 10)
  # - user.unopened_notification_count
  # - user.notification_opened?(notification)
  # - user.open_notification(notification)
  # - user.open_all_notifications
end
Notifiable Models (Manual Configuration)
Configure any model that should trigger notifications:
# app/models/comment.rb
class Comment < ApplicationRecord
  belongs_to :article
  belongs_to :user
  # Rhino ownership (if using organizations/multi-tenancy)
  rhino_owner :article
  rhino_references %i[article user]
  # Notification configuration
  acts_as_notifiable :users,
    # Who receives notifications (lambda returns array of Users)
    targets: ->(comment, _key) {
      ([comment.article.user] + comment.article.reload.commented_users.to_a).uniq
    },
    # Group notifications by article (prevents spam)
    group: :article,
    # Which actions trigger notifications
    tracked: { only: [:create] },  # or { except: [:update] }
    # How to handle deletion
    dependent_notifications: :update_group_and_destroy,
    # Options: :delete_all, :destroy, :restrict_with_error, :update_group_and_destroy
    # Frontend path (uses Rhino routing)
    notifiable_path: :comment_notifiable_path,
    # Display name
    printable_name: :comment_printable_name,
    # Enable real-time updates (if ActionCable enabled)
    action_cable_api_allowed: true,
    # Additional data to store
    parameters: { article_id: :article_id }
  # Method for frontend routing
  def comment_notifiable_path
    route_frontend  # Rhino helper
    # Returns: "/123/articles/456/comments/789"
  end
  # Method for display name
  def comment_printable_name
    "Comments on #{article.title}"
  end
end
Group Model (Optional)
If using grouping, mark the group model:
# app/models/article.rb
class Article < ApplicationRecord
  belongs_to :user
  has_many :comments, dependent: :destroy
  has_many :commented_users, through: :comments, source: :user
  rhino_owner_base  # If Article is your base owner
  rhino_references [:user]
  acts_as_notification_group  # Marks as a group model
end
Usage Patterns
1. Automatic Notification Creation
With tracked: { only: [:create] }, notifications are created automatically:
# In your controller - no explicit notification call needed!
def create
  @comment = @article.comments.build(comment_params.merge(user: current_user))
  if @comment.save
    # Notification automatically created via acts_as_notifiable
    redirect_to @article, notice: "Comment added!"
  else
    render :new
  end
end
2. Manual Notification Creation
You can also trigger notifications manually:
# Basic manual trigger
comment.notify_to(users)
# With options
comment.notify_to(
  users,
  key: 'custom.notification.key',
  parameters: { custom_field: 'value' }
)
# Notify single user
comment.notify_to(user)
3. Dynamic Notification Targets
Complex target logic in the lambda:
acts_as_notifiable :users,
  targets: ->(comment, key) {
    targets = []
    # Notify article author
    targets << comment.article.user
    # Notify other commenters
    targets += comment.article.comments.map(&:user)
    # Notify mentioned users (custom method)
    targets += comment.extract_mentions
    # Remove duplicates and commenter themselves
    (targets.uniq - [comment.user])
  }
4. Custom Parameters
Store additional context with notifications:
acts_as_notifiable :users,
  parameters: {
    article_id: :article_id,
    comment_count: ->(comment) { comment.article.comments.count },
    is_urgent: ->(comment) { comment.body.include?('URGENT') }
  }
# Access in frontend/API
notification.parameters['article_id']
notification.parameters['comment_count']
notification.parameters['is_urgent']
5. Notification Grouping
Prevent notification spam by grouping:
acts_as_notifiable :users,
  group: :article,
  dependent_notifications: :update_group_and_destroy
# Result: Multiple comments on same article = 1 notification
# The notification updates with latest comment info
Frontend Integration
Using the Pre-built Component
// In your Rhino app layout/navigation
import { NotificationMenu } from '@rhino-project/core';
function AppNavigation() {
  return (
    <nav>
      {/* Your other nav items */}
      <NotificationMenu />
    </nav>
  );
}
That's it! The component handles:
- Fetching unopened notifications
- Displaying count badge
- Click-to-navigate to notification target
- Mark as opened
- Mark all as opened
Custom Frontend Implementation
If building your own frontend:
import { 
  useNotifications, 
  useNotificationsOpen 
} from '@rhino-project/core';
function CustomNotifications() {
  const { data: { data: notifications } = {}, refetch } = useNotifications();
  const { mutate: openOne } = useNotificationsOpen();
  const handleClick = (notificationId, path) => {
    openOne(notificationId, {
      onSuccess: () => {
        refetch();
        navigate(path);
      }
    });
  };
  return (
    <div>
      <h2>Notifications ({notifications?.count || 0})</h2>
      {notifications?.notifications?.map((n) => (
        <div key={n.id} onClick={() => handleClick(n.id, n.notifiable_path)}>
          {n.printable_notifiable_name}
        </div>
      ))}
    </div>
  );
}
API Endpoints
The following endpoints are automatically available:
List Notifications
GET /api/users/:user_id/notifications?filter=unopened&limit=10
Response:
{
  "data": {
    "count": 3,
    "notifications": [
      {
        "id": 1,
        "notifiable_type": "Comment",
        "notifiable_id": 42,
        "notifiable_path": "/articles/10/comments/42",
        "printable_notifiable_name": "Comments on Article Title",
        "key": "comment.create",
        "opened_at": null,
        "parameters": { "article_id": 10 }
      }
    ]
  }
}
Query Parameters:
- 
filter=unopened(default) - Only unopened
- 
filter=opened- Only opened
- 
filter=all- All notifications
- 
limit=10- Limit results
Show Single Notification
GET /api/users/:user_id/notifications/:id
Mark as Opened
PUT /api/users/:user_id/notifications/:id/open
Mark All as Opened
POST /api/users/:user_id/notifications/open_all
Delete Notification
DELETE /api/users/:user_id/notifications/:id
Advanced Features (Optional)
These features are disabled by default but can be enabled:
Email Notifications
# config/initializers/activity_notification.rb
config.email_enabled = true
config.mailer_sender = 'notifications@yourapp.com'
# app/models/user.rb
acts_as_target email: :email, email_allowed: true
# app/models/comment.rb
acts_as_notifiable :users,
  email_allowed: true,
  notifier: :user,
  email_subject: ->(notification) { 
    "New comment on #{notification.notifiable.article.title}" 
  }
ActionCable Real-time Updates
# config/initializers/activity_notification.rb
config.action_cable_enabled = true
config.action_cable_api_enabled = true
# app/models/comment.rb
acts_as_notifiable :users,
  action_cable_api_allowed: true
# Frontend will receive real-time notification updates
Subscription Management
# config/initializers/activity_notification.rb
config.subscription_enabled = true
# Users can manage subscriptions
user.create_subscription(key: 'comment.create')
user.subscribe_to_email('comment.create')
user.unsubscribe('comment.create')
Testing
Example test for notification creation:
# test/models/comment_test.rb
require 'test_helper'
class CommentTest < ActiveSupport::TestCase
  test "creates notification for article author" do
    article = articles(:one)
    author = article.user
    assert_difference 'author.notifications.count', 1 do
      Comment.create!(
        article: article,
        user: users(:commenter),
        body: "Great article!"
      )
    end
    notification = author.notifications.last
    assert_equal 'Comment', notification.notifiable_type
    assert_nil notification.opened_at  # Unopened
  end
  test "groups multiple comments on same article" do
    article = articles(:one)
    author = article.user
    # First comment creates notification
    Comment.create!(
      article: article,
      user: users(:commenter),
      body: "First comment"
    )
    initial_count = author.notifications.count
    # Second comment updates existing notification (grouped)
    Comment.create!(
      article: article,
      user: users(:another_commenter),
      body: "Second comment"
    )
    # Count should be same (grouped)
    assert_equal initial_count, author.notifications.count
  end
end
Best Practices
- 
Always define printable_name- Makes notifications readable in the UI
- 
Always define notifiable_path- Enables click-through navigation
- Use grouping for related notifications - Prevents notification spam
- Be specific with targets lambda - Only notify relevant users
- Use parameters for context - Store data needed for display/linking
- Track only necessary actions - Don't over-notify users
- Clean up old notifications - Archive or delete periodically
- Test notification creation - Verify targets and data
- Consider notification fatigue - Less is more
- Use meaningful keys - Follow convention like "model.action"
Troubleshooting
Notifications Not Creating
Check:
- ✅ config.enabled = truein initializer
- ✅ acts_as_notifiableconfigured correctly
- ✅ targetslambda returns array of users
- ✅ Action is in trackedlist (or manually callingnotify_to)
- ✅ Database migration ran successfully
Frontend Not Showing Notifications
Check:
- ✅ User is authenticated (useUserId()returns value)
- ✅ API endpoint permissions correct
- ✅ Network tab for API errors
- ✅ NotificationMenuincluded in layout
- ✅ React Query devtools for cache status
Too Many Notifications
Solutions:
- Use groupoption to consolidate
- Adjust trackedto limit actions
- Implement filtering logic in targetslambda
- Consider digest/summary notifications
Performance Issues
Solutions:
- Add database indexes on target_id,opened_at,notifiable_type
- Limit query results with limitparameter
- Use dependent_notifications: :delete_allfor bulk operations
- Archive old notifications to separate table
- Use database queries instead of loading all notifications
Summary
The Rhino Notifications module provides:
✅ Polymorphic notifications (any model can notify any target)
✅ RESTful JSON API endpoints
✅ React Query hooks and UI components
✅ Notification grouping and consolidation
✅ Read/unread tracking
✅ Custom notification paths and display names
✅ Optional email and real-time support
✅ Subscription management (optional)
✅ Flexible target and parameter configuration
✅ Integration with Rhino's ownership model
✅ One-command installation  
What you configure manually (same as base gem):
- 
acts_as_notifiableon notifiable models
- 
targetslambda defining who gets notified
- 
group,tracked,parametersoptions
- 
printable_nameandnotifiable_pathmethods
- Notification keys and types
What Rhino provides automatically:
- JSON API endpoints
- Frontend components and hooks
- Routing integration
- Authentication/authorization
- API-optimized defaults
- Simplified installation
The system is production-ready and follows Rails/React best practices for maintainability and extensibility.
 

 
    
Top comments (0)