Class: Chats::Configuration

Inherits:
Object
  • Object
show all
Defined in:
lib/chats/configuration.rb

Overview

All of the gem’s knobs, with delightful defaults: a fresh ‘Configuration` is fully working out of the box for the classic Devise + `User` Rails app.

Two design rules, shared across the gem ecosystem (moderate, api_keys, …):

1. Class names are stored as STRINGS and constantized lazily, so the
   initializer can reference app classes before they're loaded and
   everything survives Zeitwerk reloads.
2. Cross-gem seams are PROCS with no-op defaults, so `chats` runs
   standalone and lights up when the host wires moderate / Noticed /
   goodmail in.

Constant Summary collapse

ATTACHMENT_MODES =
[false, :images, :any].freeze
DELETION_MODES =
[false, :soft, :hard].freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initializeConfiguration

Returns a new instance of Configuration.



133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
# File 'lib/chats/configuration.rb', line 133

def initialize
  @messager_class = "User"
  @parent_controller = "::ApplicationController"
  @current_messager_method = :current_user
  @authenticate_method = :authenticate_user!
  @layout = nil

  @groups = true
  @reactions = true
  @read_receipts = true
  @typing_indicators = true
  @editing = true
  @deletion = :soft
  @attachments = :images
  @search = true

  @messages_per_page = 30
  @max_message_length = 5_000
  @max_group_size = 32
  @max_attachment_size = 10 * 1024 * 1024 # 10 MB
  @max_attachments_per_message = 4
  @send_rate_limit = { to: 60, within: 60 } # 60 messages per minute per sender

  @encrypt_messages = false

  @can_message = ->(_sender, _recipient) { true }
  @can_create_group = ->(_creator) { true }

  @blocked_messager_ids = ->(_messager) { [] }
  @notifier = ->(_event, **_payload) {}

  @messager_display_name = lambda do |messager|
    messager.try(:display_name) || messager.try(:name) ||
      messager.try(:full_name) || messager.try(:username) ||
      messager.try(:email) || "#{messager.class.model_name.human} #{messager.id}"
  end
  @messager_avatar = ->(messager) { messager.try(:avatar) }
end

Instance Attribute Details

#attachmentsObject

Message attachments: ‘false` (none), `:images` (images only — the default), or `:any` (any content type). Backed by ActiveStorage’s ‘has_many_attached`, so the host must have ActiveStorage installed to enable this.



77
78
79
# File 'lib/chats/configuration.rb', line 77

def attachments
  @attachments
end

#authenticate_methodObject

Method called as a ‘before_action` to require authentication (`:authenticate_user!` works with Devise out of the box).



39
40
41
# File 'lib/chats/configuration.rb', line 39

def authenticate_method
  @authenticate_method
end

#blocked_messager_idsObject

->(messager) { ids } — every messager id that can’t talk with the given one (bidirectional). Wire to the moderate gem with one line:

config.blocked_messager_ids = ->(user) { Moderate.blocked_ids_for(user) }


117
118
119
# File 'lib/chats/configuration.rb', line 117

def blocked_messager_ids
  @blocked_messager_ids
end

#can_create_groupObject

May creator create a group conversation?



110
111
112
# File 'lib/chats/configuration.rb', line 110

def can_create_group
  @can_create_group
end

#can_messageObject

Host authorization on top of block enforcement: may sender open a conversation with / send to recipient? Blocking is ALWAYS enforced underneath this (see Chats.can_message?) so a permissive or buggy policy can never let a blocked pair talk.



107
108
109
# File 'lib/chats/configuration.rb', line 107

def can_message
  @can_message
end

#current_messager_methodObject

Method called on the controller to fetch the current messager (‘:current_user` works with Devise out of the box).



35
36
37
# File 'lib/chats/configuration.rb', line 35

def current_messager_method
  @current_messager_method
end

#deletionObject

What “delete” means: ‘:soft` keeps a tombstone (“message deleted”, body gone — the WhatsApp model, and the safest for Trust & Safety evidence), `:hard` destroys the row, `false` disables deletion entirely.



71
72
73
# File 'lib/chats/configuration.rb', line 71

def deletion
  @deletion
end

#editingObject

Whether senders can edit their own messages after sending.



66
67
68
# File 'lib/chats/configuration.rb', line 66

def editing
  @editing
end

#encrypt_messagesObject

Encrypt message bodies at rest with ActiveRecord Encryption (‘encrypts :body`). Requires the host to have AR encryption keys configured (`bin/rails db:encryption:init`). Note: turning this on makes message-body search degrade to title/participant matching.



99
100
101
# File 'lib/chats/configuration.rb', line 99

def encrypt_messages
  @encrypt_messages
end

#groupsObject

Group conversations (3+ participants, a title, join/leave). Direct 1:1 conversations are always available.



51
52
53
# File 'lib/chats/configuration.rb', line 51

def groups
  @groups
end

#layoutObject

Optional explicit layout for the engine’s screens. ‘nil` (default) inherits whatever layout the parent controller resolves — usually the host’s ‘application` layout. Set to e.g. `“app”` if your host renders its logged-in surfaces with a different layout.



45
46
47
# File 'lib/chats/configuration.rb', line 45

def layout
  @layout
end

#max_attachment_sizeObject

— Limits —————————————————————



86
87
88
# File 'lib/chats/configuration.rb', line 86

def max_attachment_size
  @max_attachment_size
end

#max_attachments_per_messageObject

— Limits —————————————————————



86
87
88
# File 'lib/chats/configuration.rb', line 86

def max_attachments_per_message
  @max_attachments_per_message
end

#max_group_sizeObject

— Limits —————————————————————



86
87
88
# File 'lib/chats/configuration.rb', line 86

def max_group_size
  @max_group_size
end

#max_message_lengthObject

— Limits —————————————————————



86
87
88
# File 'lib/chats/configuration.rb', line 86

def max_message_length
  @max_message_length
end

#messager_avatarObject

->(messager) { url/attachment/nil } — an avatar for the messager. Return anything ‘image_tag` accepts (a URL, an ActiveStorage attachment or variant), or nil to render an initials placeholder.



131
132
133
# File 'lib/chats/configuration.rb', line 131

def messager_avatar
  @messager_avatar
end

#messager_classObject

The primary conversing model. Participants stay polymorphic regardless (any ‘acts_as_messager` model can join a conversation); this is the class the engine’s controllers resolve ‘current_messager` against and the default for docs/generators.



25
26
27
# File 'lib/chats/configuration.rb', line 25

def messager_class
  @messager_class
end

#messager_display_nameObject

->(messager) { String } — how a messager is named in inboxes, bubbles and typing indicators. The default tries the obvious candidates.



126
127
128
# File 'lib/chats/configuration.rb', line 126

def messager_display_name
  @messager_display_name
end

#messages_per_pageObject

— Limits —————————————————————



86
87
88
# File 'lib/chats/configuration.rb', line 86

def messages_per_page
  @messages_per_page
end

#notifierObject

->(event, **payload) — domain-moment fan-out (see Chats.notify).



120
121
122
# File 'lib/chats/configuration.rb', line 120

def notifier
  @notifier
end

#parent_controllerObject

The controller the engine inherits from. Pointing this at the host’s ‘ApplicationController` (the default) gives the engine the host’s layout, helpers, ‘current_user`, locale switching, etc. — the same pattern api_keys uses for its dashboard.



31
32
33
# File 'lib/chats/configuration.rb', line 31

def parent_controller
  @parent_controller
end

#reactionsObject

Emoji reactions on messages.



54
55
56
# File 'lib/chats/configuration.rb', line 54

def reactions
  @reactions
end

#read_receiptsObject

Read receipts (“Seen”) + unread tracking. Read state is stored on the participant (‘last_read_at`), not per-message — see Chats::Participant for the rationale.



59
60
61
# File 'lib/chats/configuration.rb', line 59

def read_receipts
  @read_receipts
end

#searchObject

Inbox search box (partial matching across participant names, conversation titles, subject labels, and message bodies — no extra dependencies; swap in pg_search & friends by overriding the controller if you outgrow it).



82
83
84
# File 'lib/chats/configuration.rb', line 82

def search
  @search
end

#send_rate_limitObject

Per-sender send throttle, enforced with Rails 8’s built-in controller ‘rate_limit` when available (feature-detected; on Rails 7.1 it’s a no-op). Shape: ‘{ to: Integer, within: ActiveSupport::Duration }`. Set to `nil` to disable.



93
94
95
# File 'lib/chats/configuration.rb', line 93

def send_rate_limit
  @send_rate_limit
end

#typing_indicatorsObject

Live “X is typing…” indicators (Turbo Stream custom action; no Action Cable channel of its own, see Chats::ConversationsController#typing).



63
64
65
# File 'lib/chats/configuration.rb', line 63

def typing_indicators
  @typing_indicators
end

Instance Method Details

#messager_modelObject

The constantized messager class (resolved lazily — see class comment).



266
267
268
# File 'lib/chats/configuration.rb', line 266

def messager_model
  messager_class.constantize
end

#validate!Object

Cross-field validation, run at the end of ‘Chats.configure`.



248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
# File 'lib/chats/configuration.rb', line 248

def validate!
  if max_message_length && max_message_length < 1
    raise ConfigurationError, "max_message_length must be positive (got #{max_message_length})"
  end

  if max_group_size && max_group_size < 3
    # 2 participants is a direct conversation; a "group" of 2 is a smell.
    raise ConfigurationError, "max_group_size must be at least 3 (got #{max_group_size})"
  end

  if messages_per_page.to_i < 1
    raise ConfigurationError, "messages_per_page must be positive (got #{messages_per_page.inspect})"
  end

  true
end