Module: Organizations

Defined in:
lib/organizations.rb,
lib/organizations/roles.rb,
lib/organizations/engine.rb,
lib/organizations/version.rb,
lib/organizations/callbacks.rb,
lib/organizations/join_flow.rb,
lib/organizations/join_state.rb,
lib/organizations/test_helpers.rb,
lib/organizations/view_helpers.rb,
lib/organizations/configuration.rb,
lib/organizations/models/domain.rb,
lib/organizations/metadata_flags.rb,
lib/organizations/callback_context.rb,
lib/organizations/email_normalizer.rb,
lib/organizations/models/join_code.rb,
lib/organizations/models/invitation.rb,
lib/organizations/models/membership.rb,
lib/organizations/controller_helpers.rb,
lib/organizations/models/join_request.rb,
lib/organizations/models/organization.rb,
lib/organizations/organization_scoped.rb,
lib/organizations/models/allowlist_entry.rb,
lib/organizations/current_user_resolution.rb,
app/mailers/organizations/invitation_mailer.rb,
lib/organizations/acts_as_tenant_integration.rb,
app/mailers/organizations/verification_mailer.rb,
lib/organizations/invitation_acceptance_result.rb,
app/controllers/organizations/public_controller.rb,
app/controllers/organizations/switch_controller.rb,
lib/organizations/invitation_acceptance_failure.rb,
lib/generators/organizations/views/views_generator.rb,
lib/organizations/models/concerns/has_organizations.rb,
app/controllers/organizations/application_controller.rb,
app/controllers/organizations/invitations_controller.rb,
app/controllers/organizations/memberships_controller.rb,
app/controllers/organizations/organizations_controller.rb,
lib/generators/organizations/install/install_generator.rb,
lib/generators/organizations/upgrade/upgrade_generator.rb,
app/controllers/organizations/public_invitations_controller.rb

Defined Under Namespace

Modules: ActsAsTenantIntegration, Callbacks, ControllerHelpers, CurrentUserResolution, EmailNormalizer, Generators, MetadataFlags, Models, OrganizationScoped, Roles, TestHelpers, ViewHelpers Classes: AllowlistEntry, ApplicationController, CallbackContext, CannotDeleteAsOrganizationOwner, CannotLeaveAsLastOwner, CannotLeaveLastOrganization, Configuration, ConfigurationError, Domain, Engine, Error, Invitation, InvitationAcceptanceFailure, InvitationAcceptanceResult, InvitationAlreadyAccepted, InvitationEmailMismatch, InvitationError, InvitationExpired, InvitationMailer, InvitationsController, JoinCode, JoinCodeError, JoinCodeExhausted, JoinCodeInvalid, JoinFlow, JoinRequest, JoinRequestAlreadyDecided, JoinRequestError, JoinRequestExpired, JoinState, Membership, MembershipVetoed, MembershipsController, NoCurrentOrganization, NotAMember, NotAuthorized, Organization, OrganizationLimitReached, OrganizationsController, PublicController, PublicInvitationsController, SwitchController, VerificationAttemptsExceeded, VerificationCodeExpired, VerificationCodeInvalid, VerificationEmailAlreadyClaimed, VerificationEmailNotEligible, VerificationError, VerificationMailer, VerificationThrottled

Constant Summary collapse

Controller =

Alias for README compatibility: include Organizations::Controller

ControllerHelpers
VERSION =
"0.5.0"

Class Attribute Summary collapse

Class Method Summary collapse

Class Attribute Details

.configurationConfiguration

Get the configuration instance

Returns:



161
162
163
# File 'lib/organizations.rb', line 161

def configuration
  @configuration ||= Configuration.new
end

Class Method Details

.compute_engine_mount_pathObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



220
221
222
223
224
225
226
227
228
229
230
231
232
233
# File 'lib/organizations.rb', line 220

def compute_engine_mount_path
  routes = host_application_routes
  return "" unless routes && defined?(Organizations::Engine)

  mount = routes.routes.detect do |route|
    route.app.respond_to?(:app) && route.app.app == Organizations::Engine
  end
  return "" unless mount

  # "/orgs(.:format)" → "/orgs"; a root mount "/" → "".
  mount.path.spec.to_s.delete_suffix("(.:format)").chomp("/")
rescue StandardError
  ""
end

.configure {|Configuration| ... } ⇒ Object

Configure the gem ATOMIC on validation failure: a configure block that raises (typically via validate!) restores the previous configuration instead of leaving half-applied settings on the live object. Found the hard way: a validation-failure left its invalid assignment behind on the shared config, and every LATER configure call re-raised that stale error — an order-dependent heisenbug in test suites and a real hazard for hosts rescuing ConfigurationError in initializers. NOTE: restoration is a shallow dup — use SETTERS in configure blocks (config.x = [...]), never in-place mutation (config.x << ...), which is the documented contract anyway.

Examples:

Organizations.configure do |config|
  config.always_create_personal_organization_for_each_user = true
  config.invitation_expiry = 7.days
end

Yields:



184
185
186
187
188
189
190
191
# File 'lib/organizations.rb', line 184

def configure
  snapshot = configuration.dup
  yield(configuration)
  configuration.validate!
rescue StandardError
  @configuration = snapshot
  raise
end

.engine_mount_pathString

The path prefix the engine is mounted at in the HOST app ("" when mounted at root, "/orgs" when mounted there, "" outside Rails).

Why this exists: engine route helpers called WITHOUT a controller context (from models/mailers — e.g. building an invitation acceptance URL) don't know the mount point, so URLs built as "/invitations/" silently 404 for any host that mounts the engine anywhere but root. Both known hosts mount at root, which is exactly why nobody noticed. Memoized — the mount point is fixed at boot. Source on mounted helpers vs raw engine url_helpers: https://guides.rubyonrails.org/engines.html#routes

Returns:

  • (String)


213
214
215
216
217
# File 'lib/organizations.rb', line 213

def engine_mount_path
  return @engine_mount_path if defined?(@engine_mount_path)

  @engine_mount_path = compute_engine_mount_path
end

.full_rails_app?Boolean

True only under a real, booted Rails app — the ONE home for this guard (mailers and URL builders all need it). ⚠️ defined?(Rails) alone is NOT enough: several gems define a bare Rails module WITHOUT .application (globalid setups, railtie fragments, bare test harnesses), where Rails.application raises NoMethodError instead of returning nil. Found by the first tests to ever render the stock mails.

Returns:

  • (Boolean)


241
242
243
# File 'lib/organizations.rb', line 241

def full_rails_app?
  !!(defined?(Rails) && Rails.respond_to?(:application) && Rails.application)
end

.host_application_routesObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.



246
247
248
249
250
# File 'lib/organizations.rb', line 246

def host_application_routes
  return nil unless full_rails_app?

  Rails.application.routes
end

.reset_configuration!Object

Reset configuration to defaults Primarily used in tests



195
196
197
198
199
# File 'lib/organizations.rb', line 195

def reset_configuration!
  @configuration = nil
  remove_instance_variable(:@engine_mount_path) if defined?(@engine_mount_path)
  Roles.reset!
end

.rolesModule

Get the roles module

Returns:

  • (Module)


254
255
256
# File 'lib/organizations.rb', line 254

def roles
  Roles
end

.translate(key) ⇒ String Also known as: t

Resolve a gem string through I18n under the organizations. namespace. This is the ONE door every user-facing string the gem produces goes through — error messages, labels, mailer copy. en.yml is the catalog SSOT (no inline English defaults on purpose: a missing key renders as "Translation missing: …", a loud and findable bug, instead of silently drifting from the catalog).

Hosts override any key the standard Rails way — app locale files load after engine locale files, so the host's value wins.

Parameters:

  • key (String, Symbol)

    key under the organizations. scope, e.g. :"errors.join_code_invalid" or "roles.owner"

  • options (Hash)

    I18n options (interpolations, :locale, :default…)

Returns:

  • (String)


288
289
290
# File 'lib/organizations.rb', line 288

def translate(key, **)
  I18n.t(key, scope: :organizations, **)
end

.user_classClass

The host's user model class (constantized user_class_name).

Returns:

  • (Class)


270
271
272
# File 'lib/organizations.rb', line 270

def user_class
  user_class_name.constantize
end

.user_class_nameString

The host's user model class name (config.user_class, default "User"). Used as class_name: at association-definition time in the gem's models — they load AFTER initializers in Rails (Zeitwerk shims) and on first constant reference in plain Ruby, so a configured value is visible as long as hosts configure before touching the models.

Returns:

  • (String)


264
265
266
# File 'lib/organizations.rb', line 264

def user_class_name
  configuration.user_class
end