### Installation and Setup Source: https://github.com/rameerez/organizations/blob/main/README.md Install the gem, run migrations, and mount the engine in your Rails routes to enable the organization management system. ```bash bundle install rails g organizations:install rails db:migrate ``` ```ruby # config/routes.rb mount Organizations::Engine => '/' ``` -------------------------------- ### Configure Test Helpers Source: https://context7.com/rameerez/organizations/llms.txt Setup instructions for including organization test helpers in ActiveSupport::TestCase and example usage for authentication and organization context. ```ruby require "organizations/test_helpers" class ActiveSupport::TestCase include Organizations::TestHelpers end test "admin can invite members" do user = users(:admin) org = organizations(:acme) sign_in_as_organization_member(user, org, role: :admin) set_current_organization(org) assert user.is_admin_of?(org) assert user.has_organization_permission_to?(:invite_members) end ``` -------------------------------- ### Create Organizations Source: https://context7.com/rameerez/organizations/llms.txt Provides examples of how users can create new organizations, either with just a name or with additional attributes. It also demonstrates how to handle the `OrganizationLimitReached` exception. ```ruby # Create an organization with just a name org = current_user.create_organization!("Acme Corp") # User automatically becomes the owner ``` ```ruby # Create with additional attributes org = current_user.create_organization!( name: "Acme Corp", support_email: "support@acme.com" ) ``` ```ruby # Check organization limits begin current_user.create_organization!("Another Org") rescue Organizations::Models::Concerns::HasOrganizations::OrganizationLimitReached => e puts e.message # "Maximum number of organizations (5) reached" end ``` -------------------------------- ### Install Organizations Gem Source: https://context7.com/rameerez/organizations/llms.txt Instructions for adding the Organizations gem to your Rails application's Gemfile, installing it, and running the necessary generators and migrations. ```ruby # Gemfile gem "organizations" ``` ```bash bundle install rails g organizations:install rails db:migrate ``` -------------------------------- ### Development Setup and Testing (Bash) Source: https://github.com/rameerez/organizations/blob/main/README.md Provides the bash commands necessary to clone the organizations gem repository, set up the development environment, and run the test suite. ```bash git clone https://github.com/rameerez/organizations cd organizations bin/setup bundle exec rake test ``` -------------------------------- ### Optimize Queries and Permissions Source: https://github.com/rameerez/organizations/blob/main/README.md Provides examples for eager loading associations to prevent N+1 queries and explains that permission checks are performed in-memory. ```ruby # Eager loading to avoid N+1 org.memberships.includes(:user).each do |membership| membership.user.name end # Permission checks (in-memory, no DB query) user.has_organization_permission_to?(:invite_members) org.memberships.includes(:user).each do |m| m.has_permission_to?(:invite_members) end ``` -------------------------------- ### Configure User Model for Organizations Source: https://context7.com/rameerez/organizations/llms.txt Demonstrates how to enable organization support on your User model using the `has_organizations` macro. Includes examples with and without configuration options like `max_organizations`, `create_personal_org`, and `require_organization`. ```ruby # app/models/user.rb class User < ApplicationRecord has_organizations end ``` ```ruby # With configuration options class User < ApplicationRecord has_organizations do max_organizations 5 # Limit how many orgs a user can own (nil = unlimited) create_personal_org true # Auto-create org on signup (default: false) require_organization true # Require users to have at least one org (default: false) end end ``` -------------------------------- ### Use Rails Fixtures for Organizations and Memberships Source: https://github.com/rameerez/organizations/blob/main/README.md Configure Rails fixtures to define organizations and their memberships. This allows for easy setup of test data, ensuring consistent states for your tests. ```yaml # test/fixtures/organizations.yml acme: name: Acme Corp # test/fixtures/memberships.yml john_at_acme: user: john organization: acme role: admin ``` -------------------------------- ### Handle Row-Level Locking for Data Consistency in Ruby Source: https://github.com/rameerez/organizations/blob/main/README.md Examples of using row-level locking (SELECT FOR UPDATE) to prevent race conditions during sensitive operations like invitation acceptance, ownership transfer, and role changes. ```ruby invitation.accept! org.transfer_ownership_to!(new_owner) user.leave_organization!(org) membership.promote_to!(:admin) ``` -------------------------------- ### Get Current User's Organization Role Source: https://context7.com/rameerez/organizations/llms.txt Retrieves the current user's role within the active organization. This is a direct attribute access. ```ruby current_user.current_organization_role # => :owner ``` -------------------------------- ### Perform Efficient Existence Checks in Ruby Source: https://github.com/rameerez/organizations/blob/main/README.md Illustrates the use of optimized SQL EXISTS queries for checking memberships and invitations, minimizing data transfer. ```ruby user.belongs_to_any_organization? user.has_pending_organization_invitations? org.has_any_members? ``` -------------------------------- ### Organizations Gem Basic Configuration Source: https://context7.com/rameerez/organizations/llms.txt This Ruby code snippet shows the basic configuration for the Organizations gem within a Rails initializer. It covers authentication methods, auto-creation settings, invitation defaults, and redirect paths. ```ruby # config/initializers/organizations.rb Organizations.configure do |config| # === Authentication === config.current_user_method = :current_user config.authenticate_user_method = :authenticate_user! # === Auto-creation === config.always_create_personal_organization_for_each_user = false config.default_organization_name = ->(user) { "#{user.name}'s Workspace" } # === Invitations === config.invitation_expiry = 7.days config.default_invitation_role = :member config.invitation_mailer = "Organizations::InvitationMailer" # === Limits === config.max_organizations_per_user = nil # unlimited # === Redirects === config.redirect_path_when_no_organization = "/organizations/new" config.redirect_path_after_invitation_accepted = "/dashboard" config.redirect_path_after_organization_switched = "/dashboard" # === Organizations Controller === config.additional_organization_params = [:support_email, :billing_email] # === Handlers === config.on_unauthorized do |context| redirect_to root_path, alert: "You don't have permission." end config.on_no_organization do |context| redirect_to new_organization_path, notice: "Please create an organization." end end ``` -------------------------------- ### Set Up Test Helpers for Organizations Gem Source: https://github.com/rameerez/organizations/blob/main/README.md Include the `Organizations::TestHelpers` module in your `ActiveSupport::TestCase` to gain access to helpful methods for testing organization-related functionality. This includes methods for signing in users and setting the current organization context. ```ruby # test/test_helper.rb require "organizations/test_helpers" class ActiveSupport::TestCase include Organizations::TestHelpers end ``` -------------------------------- ### Integrate acts_as_tenant Source: https://context7.com/rameerez/organizations/llms.txt Configures automatic query scoping by including the Organizations controller mixins and applying acts_as_tenant to models. ```ruby class ApplicationController < ActionController::Base include Organizations::Controller include Organizations::ActsAsTenantIntegration end class Project < ApplicationRecord acts_as_tenant :organization end ``` -------------------------------- ### Accept Organization Invitations Source: https://context7.com/rameerez/organizations/llms.txt Details how to handle invitation acceptance, including direct controller acceptance and automatic acceptance after signup/signin. It also shows manual acceptance with more control over the outcome. ```ruby # In controller - accepting invitation directly invitation = Organizations::Invitation.find_by(token: params[:token]) if invitation.pending? membership = invitation.accept!(current_user) redirect_to root_path, notice: "Welcome to #{invitation.organization.name}!" end ``` ```ruby # Auto-accept pending invitation after signup/signin # Add to ApplicationController def after_sign_in_path_for(resource) if (path = pending_invitation_acceptance_redirect_path_for(resource)) return path end super end def after_sign_up_path_for(resource) if (path = pending_invitation_acceptance_redirect_path_for(resource)) return path end super end ``` ```ruby # Manual acceptance with more control result = accept_pending_organization_invitation!(current_user) if result result.accepted? # => true if freshly accepted result.already_member? # => true if user was already a member result.switched? # => true if org context was switched result.invitation # => the invitation record result.membership # => the membership record end ``` -------------------------------- ### Mount Organizations Engine Routes Source: https://context7.com/rameerez/organizations/llms.txt Shows how to mount the Organizations engine in your Rails application's `config/routes.rb` file to integrate its routing. ```ruby # config/routes.rb Rails.application.routes.draw do mount Organizations::Engine => '/' # Your other routes... end ``` -------------------------------- ### Send Organization Invitations Source: https://context7.com/rameerez/organizations/llms.txt Illustrates how to send invitations to users via email, either to the current organization or a specific one. It covers the organization-centric API and checking invitation status. ```ruby # Invite to current organization (most common) invitation = current_user.send_organization_invite_to!("teammate@example.com") # Sends email: "John invited you to join Acme Corp" # When accepted, user joins as :member (default role) ``` ```ruby # Invite to a specific organization invitation = current_user.send_organization_invite_to!( "teammate@example.com", organization: other_org ) ``` ```ruby # Organization-centric API alternative invitation = org.send_invite_to!( "teammate@example.com", invited_by: current_user ) ``` ```ruby # Check invitation status invitation.pending? # => true invitation.accepted? # => false invitation.expired? # => false invitation.status # => :pending ``` ```ruby # Resend expired invitation invitation.resend! if invitation.expired? ``` -------------------------------- ### Organizations Gem Lifecycle Callbacks Source: https://context7.com/rameerez/organizations/llms.txt This Ruby code snippet illustrates how to hook into various organization lifecycle events using the Organizations gem. It includes callbacks for organization creation, member invitations, member joining/removal, role changes, and ownership transfers, allowing for custom actions like notifications and analytics tracking. ```ruby # config/initializers/organizations.rb Organizations.configure do |config| config.on_organization_created do |ctx| SlackNotifier.notify("New org: #{ctx.organization.name}") Analytics.track(ctx.user, "organization_created") end # Strict callback - can veto invitations (e.g., seat limits) config.on_member_invited do |ctx| org = ctx.organization limit = org.current_plan.limit_for(:organization_members) if limit && org.member_count >= limit raise Organizations::InvitationError, "Member limit reached." end end config.on_member_joined do |ctx| WelcomeMailer.send_team_welcome(ctx.user, ctx.organization).deliver_later Analytics.track(ctx.user, "joined_organization", org: ctx.organization.name) end config.on_member_removed do |ctx| AuditLog.record( action: :member_removed, organization: ctx.organization, user: ctx.user, actor: ctx.removed_by ) end config.on_role_changed do |ctx| Notification.send(ctx.user, "Your role changed to #{ctx.new_role}") end config.on_ownership_transferred do |ctx| AuditLog.record( action: :ownership_transferred, organization: ctx.organization, old_owner: ctx.old_owner, new_owner: ctx.new_owner ) end end ``` -------------------------------- ### Optimize Role Checks with Preloaded Data in Ruby Source: https://github.com/rameerez/organizations/blob/main/README.md Demonstrates how to check user roles against organizations efficiently by leveraging preloaded membership data to avoid redundant database queries. ```ruby user.organizations.includes(:memberships).each do |org| user.is_admin_of?(org) end user.is_admin_of?(some_org) ``` -------------------------------- ### Configure Custom Roles and Permissions (Ruby) Source: https://github.com/rameerez/organizations/blob/main/README.md Shows how to define custom roles and permissions within the organizations gem initializer. This allows for fine-grained control over what actions users can perform based on their assigned role. ```ruby # config/initializers/organizations.rb Organizations.configure do |config| config.roles do role :viewer do can :view_organization can :view_members end role :member, inherits: :viewer do can :create_resources can :edit_own_resources can :delete_own_resources end role :admin, inherits: :member do can :invite_members can :remove_members can :edit_member_roles can :manage_settings end role :owner, inherits: :admin do can :manage_billing can :transfer_ownership can :delete_organization end end end ``` -------------------------------- ### ERB Template for Organization Switcher and Permissions Source: https://context7.com/rameerez/organizations/llms.txt This ERB template demonstrates how to implement an organization switcher and conditionally render UI elements based on user roles and permissions. It utilizes data provided by `organization_switcher_data` and checks user permissions like `has_organization_permission_to?` and `is_organization_admin?`. ```erb <% data = organization_switcher_data %>
<% if current_user.has_organization_permission_to?(:invite_members) %> <%= link_to "Invite teammate", new_organization_invitation_path %> <% end %> <% if current_user.is_organization_admin? %> <%= link_to "Settings", organization_settings_path %> <% end %> <%= organization_invitation_badge(current_user) %> <%= organization_role_label(:admin) %> <% organization_members_data(@org).each do |member| %>

<%= member[:name] %> - <%= member[:role_label] %>

<% end %> ``` -------------------------------- ### Handle Existing Organization Invitations (Ruby) Source: https://github.com/rameerez/organizations/blob/main/README.md Demonstrates how the organizations gem handles scenarios where an email is already invited or an invitation has already been accepted. It returns the existing pending invitation or membership without raising an error. ```ruby current_user.send_organization_invite_to!("already@invited.com") # => Returns existing pending invitation (doesn't raise) invitation.accept! # => Returns existing membership (doesn't raise) ``` -------------------------------- ### Database Schema Definitions Source: https://github.com/rameerez/organizations/blob/main/README.md Outlines the structure of the three core tables required by the gem: organizations_organizations, organizations_memberships, and organizations_invitations. ```sql organizations_organizations - id (primary key) - name (string, required) - metadata (jsonb, default: {}) - created_at - updated_at organizations_memberships - id (primary key) - user_id (foreign key, indexed) - organization_id (foreign key, indexed) - role (string, default: 'member') - invited_by_id (foreign key, nullable) - created_at - updated_at organizations_invitations - id (primary key) - organization_id (foreign key, indexed) - email (string, required, indexed) - role (string, default: 'member') - token (string, unique, indexed) - invited_by_id (foreign key, nullable) - accepted_at (datetime, nullable) - expires_at (datetime) - created_at - updated_at ``` -------------------------------- ### Send Organization Invitations (Ruby) Source: https://github.com/rameerez/organizations/blob/main/README.md Demonstrates how to send invitations to users to join an organization. It covers inviting to the current organization or a specific one, and mentions that invitees default to the `:member` role. Authorization checks are enforced. ```ruby # Invite to your current organization (most common) current_user.send_organization_invite_to!("teammate@example.com") # Invite to a specific organization current_user.send_organization_invite_to!("teammate@example.com", organization: other_org) # All invitees join as :member by default. Admins can promote after joining. # Organization-centric API org.send_invite_to!("teammate@example.com", invited_by: current_user) > **Note:** Both APIs enforce authorization. The inviter must be a member of the organization with the `:invite_members` permission. If not, `Organizations::NotAMember` or `Organizations::NotAuthorized` is raised. ``` -------------------------------- ### Check Roles in Current Organization Source: https://context7.com/rameerez/organizations/llms.txt Explains how to perform role checks for a user within the context of the current organization. This includes quick boolean checks for owner, admin, member, and viewer roles, as well as a general check for having at least a specific role. ```ruby # Quick role checks (in current organization) current_user.is_organization_owner? # => true/false current_user.is_organization_admin? # => true/false (owners inherit admin) current_user.is_organization_member? # => true/false current_user.is_organization_viewer? # => true/false ``` ```ruby # Check if user has at least a specific role current_user.has_organization_role?(:admin) # => true if admin or owner ``` -------------------------------- ### Execute Scoped Associations with SQL JOINs in Ruby Source: https://github.com/rameerez/organizations/blob/main/README.md Demonstrates how the gem uses SQL JOINs to fetch related records like admins or owned organizations efficiently. ```ruby org.admins user.owned_organizations ``` -------------------------------- ### Manage Organization Members (Ruby) Source: https://context7.com/rameerez/organizations/llms.txt Provides methods to programmatically manage members of an organization, including adding, removing, changing roles, and transferring ownership. It also allows querying member lists and counts, and checking for the existence of specific members. ```ruby # Add a user as member org.add_member!(user, role: :member) org.add_member!(user, role: :admin) # Remove a member org.remove_member!(user, removed_by: current_user) # Change a user's role org.change_role_of!(user, to: :admin, changed_by: current_user) # Transfer ownership (new owner must be admin) org.transfer_ownership_to!(admin_user) # Old owner becomes admin automatically # Membership role changes membership = user.memberships.find_by(organization: org) membership.promote_to!(:admin, changed_by: current_user) membership.demote_to!(:member, changed_by: current_user) # Query members org.owner # => User (the owner) org.admins # => [User, User] (admins including owner) org.members # => all users org.member_count # => 5 org.has_member?(user) # => true/false org.has_any_members? # => true/false ``` -------------------------------- ### Switch Active Organization (Ruby) Source: https://context7.com/rameerez/organizations/llms.txt Handles scenarios where a user belongs to multiple organizations. It allows retrieving all of a user's organizations, switching the active organization, and checking the current active organization. It also includes functionality for a user to leave an organization. ```ruby # Get all user's organizations current_user.organizations # => [acme, startup_co, personal] # Switch active organization switch_to_organization!(startup_co) # In controller - switching def switch org = current_user.organizations.find(params[:id]) switch_to_organization!(org) redirect_to dashboard_path end # Check current organization current_organization # => Active organization from session organization_signed_in? # => Is there an active organization? # Leave an organization current_user.leave_organization!(org) current_user.leave_current_organization! ``` -------------------------------- ### Manage Organization Context and Sessions Source: https://github.com/rameerez/organizations/blob/main/README.md Demonstrates how to access a user's organizations and switch the active organization within the current session. ```ruby current_user.organizations # => [acme, startup_co, personal] switch_to_organization!(startup_co) # Changes active org in session ``` -------------------------------- ### Build Organization Switcher UI (Ruby) Source: https://github.com/rameerez/organizations/blob/main/README.md Provides a Ruby helper to build a user interface for switching between organizations. It requires data containing the current organization and a list of other organizations, along with a path for switching. The output is an HTML structure for a dropdown switcher. ```ruby <% data = organization_switcher_data %>
``` -------------------------------- ### Handle User Organization State in Views Source: https://github.com/rameerez/organizations/blob/main/README.md Demonstrates how to conditionally render partials based on whether a user belongs to an organization using ERB templates. ```erb <% if current_user.belongs_to_any_organization? %> <%= render "dashboard" %> <% else %> <%= render "onboarding/create_or_join_organization" %> <% end %> ``` -------------------------------- ### Integrate Pricing Plans for Member Limits Source: https://github.com/rameerez/organizations/blob/main/README.md Integrate with the `pricing_plans` gem to enforce member limits based on subscription tiers. This involves including `PricingPlans::PlanOwner` in the Organization model and defining plan limits in an initializer. ```ruby # In your Organization model class Organization < ApplicationRecord include PricingPlans::PlanOwner end # In config/initializers/pricing_plans.rb plan :starter do limits :members, to: 5 end plan :pro do limits :members, to: 50 end ``` -------------------------------- ### Enforce Plan Limits via Callbacks Source: https://github.com/rameerez/organizations/blob/main/README.md Integrates with pricing_plans to define member limits per plan and uses the on_member_invited callback to enforce these limits before an invitation is persisted. ```ruby # config/initializers/pricing_plans.rb plan :hobby do limits :organization_members, to: 3 end plan :growth do limits :organization_members, to: 25 end # config/initializers/organizations.rb Organizations.configure do |config| config.on_member_invited do |ctx| org = ctx.organization limit = org.current_plan.limit_for(:organization_members) if limit && org.member_count >= limit raise Organizations::InvitationError, "Member limit reached. Please upgrade your plan." end end end ``` -------------------------------- ### Database Indexes for Organizations Gem (SQL) Source: https://github.com/rameerez/organizations/blob/main/README.md Defines the SQL database indexes automatically created by the organizations gem for efficient lookups of memberships and invitations. These include unique indexes for user/organization pairs and tokens, as well as indexes for organization ID, role, and email. ```sql -- Fast membership lookups CREATE UNIQUE INDEX index_organizations_memberships_on_user_and_org ON organizations_memberships (user_id, organization_id); CREATE INDEX index_organizations_memberships_on_organization_id ON organizations_memberships (organization_id); CREATE INDEX index_organizations_memberships_on_role ON organizations_memberships (role); -- Fast invitation lookups CREATE UNIQUE INDEX index_organizations_invitations_on_token ON organizations_invitations (token); CREATE INDEX index_organizations_invitations_on_email ON organizations_invitations (email); CREATE UNIQUE INDEX index_organizations_invitations_pending ON organizations_invitations (organization_id, LOWER(email)) WHERE accepted_at IS NULL; ``` -------------------------------- ### Configure Organization Settings Source: https://github.com/rameerez/organizations/blob/main/README.md Customize the gem's behavior, including redirect paths, mailer classes, and invitation expiration periods via an initializer. ```ruby Organizations.configure do |config| config.redirect_path_when_invitation_requires_authentication = "/users/sign_up" config.redirect_path_after_invitation_accepted = ->(inv, user) { "/org/#{inv.organization_id}/welcome" } config.invitation_mailer = "Organizations::InvitationMailer" config.invitation_expiry = 7.days end ``` -------------------------------- ### Rails Helper for Organization View Logic Source: https://context7.com/rameerez/organizations/llms.txt This Ruby helper module integrates view-specific organization functionalities, such as rendering an organization switcher and conditionally displaying links based on user permissions. It relies on `Organizations::ViewHelpers` and Rails view helpers. ```ruby module ApplicationHelper include Organizations::ViewHelpers end ``` -------------------------------- ### Retrieve Lightweight Organization Switcher Data in Ruby Source: https://github.com/rameerez/organizations/blob/main/README.md Provides a helper method to fetch minimal organization data for UI navigation, memoizing the result within the request cycle. ```ruby organization_switcher_data ``` -------------------------------- ### User Organization Actions Source: https://github.com/rameerez/organizations/blob/main/README.md Defines actions users can perform related to organizations, such as creating, leaving, and sending invitations. Supports both positional and keyword arguments for organization creation. ```ruby # Actions user.create_organization!("Acme") # Positional arg user.create_organization!(name: "Acme") # Keyword arg (both work) user.leave_organization!(org) user.leave_current_organization! # Leave the active org user.send_organization_invite_to!(email) # Invite to current org user.send_organization_invite_to!(email, organization: org) # Invite to specific org ``` -------------------------------- ### Configure Unauthorized Access Handling Source: https://github.com/rameerez/organizations/blob/main/README.md Define global behavior for unauthorized access or missing organization context within the gem's configuration initializer. ```ruby Organizations.configure do |config| config.on_unauthorized do |context| redirect_to root_path, alert: "You don't have permission to do that." end config.on_no_organization do |context| redirect_to new_organization_path, alert: "Please create or join an organization first." end end ``` -------------------------------- ### Check Specific Organization Roles (Ruby) Source: https://context7.com/rameerez/organizations/llms.txt Allows checking a user's role within a particular organization instance. It supports direct role comparison and checks for specific roles like owner, admin, member, and viewer. It also enables checking if the user's role meets a minimum level. ```ruby # Check role in a specific organization current_user.role_in(@org) # => :admin # Role checks for specific organization current_user.is_owner_of?(@org) # => true/false current_user.is_admin_of?(@org) # => true/false current_user.is_member_of?(@org) # => true/false current_user.is_viewer_of?(@org) # => true/false # Check minimum role level current_user.is_at_least?(:admin, in: @org) # => true if admin or owner ``` -------------------------------- ### Manage Organizations and Invitations Source: https://github.com/rameerez/organizations/blob/main/README.md Perform core organization actions such as creating new organizations, inviting teammates via email, and switching the active organization context. ```ruby current_user.create_organization!("Acme Corp") current_user.send_organization_invite_to!("teammate@acme.com") switch_to_organization!(@org) ``` -------------------------------- ### Extend Organization Model Source: https://context7.com/rameerez/organizations/llms.txt Demonstrates how to use class_eval to add associations, validations, and custom methods to the Organizations::Organization model. ```ruby Organizations::Organization.class_eval do has_many :projects has_many :documents validates :support_email, format: { with: URI::MailTo::EMAIL_REGEXP }, allow_blank: true def active_projects projects.where(archived: false) end end ``` -------------------------------- ### Extend Organization Model Source: https://github.com/rameerez/organizations/blob/main/README.md Demonstrates how to extend the base Organizations model using class_eval for dynamic modification or by creating a subclass for domain-specific logic. ```ruby Organizations::Organization.class_eval do has_many :projects has_many :documents validates :support_email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP } def active_projects projects.where(archived: false) end end ``` ```ruby class Organization < Organizations::Organization has_many :projects has_many :documents validates :support_email, presence: true end ``` -------------------------------- ### Organization Associations and Queries Source: https://github.com/rameerez/organizations/blob/main/README.md Provides methods for accessing organization members, users, invitations, and performing queries like checking for members or counting them. Includes class methods for organization scopes. ```ruby # Associations org.memberships # All memberships org.members # All users (alias for org.users) org.users # All users (through memberships) org.invitations # All invitations (pending + accepted) org.pending_invitations # Invitations not yet accepted # Queries org.owner # User who owns this org org.admins # Users with admin role or higher org.has_member?(user) # "Does org have this user as a member?" org.has_any_members? # "Does org have any members?" org.member_count # Number of members # Class methods / Scopes Organizations::Organization.with_member(user) # Find all orgs where user is a member ``` -------------------------------- ### Access Member Counts via Counter Cache in Ruby Source: https://github.com/rameerez/organizations/blob/main/README.md Shows how to retrieve organization member counts efficiently using the built-in memberships_count counter cache. ```ruby org.member_count ``` -------------------------------- ### Organization Actions and Invitations Source: https://github.com/rameerez/organizations/blob/main/README.md Enables managing organization members, changing roles, transferring ownership, and sending invitations. Invitation sending can auto-infer the inviter or specify it explicitly. ```ruby # Actions org.add_member!(user, role: :member) org.remove_member!(user) org.change_role_of!(user, to: :admin) org.transfer_ownership_to!(other_user) # Invitations (inviter must be a member with :invite_members permission) org.send_invite_to!(email) # Auto-infers invited_by from Current.user org.send_invite_to!(email, invited_by: user) # Explicit inviter ``` -------------------------------- ### Integrate Invitation Acceptance into Auth Flow Source: https://github.com/rameerez/organizations/blob/main/README.md Use these helpers in your ApplicationController to handle pending invitations after a user signs in or signs up. This ensures that users who clicked an invitation link while logged out are correctly associated with the organization upon authentication. ```ruby def after_sign_in_path_for(resource) if (path = pending_invitation_acceptance_redirect_path_for(resource)) return path end super end def after_sign_up_path_for(resource) if (path = pending_invitation_acceptance_redirect_path_for(resource)) return path end super end ``` -------------------------------- ### Invitation Details and Status Checks Source: https://github.com/rameerez/organizations/blob/main/README.md Exposes details of an invitation, including the invited email, organization, role, and inviter. Allows checking the status of an invitation (pending, accepted, expired). ```ruby invitation.email # => "teammate@example.com" invitation.organization # The organization invitation.role # Role they'll have when accepted invitation.invited_by # User who sent the invitation invitation.from # Alias for invited_by invitation.pending? # => true (not yet accepted) invitation.accepted? # => true (has accepted_at) invitation.expired? # => true (past expires_at) ``` -------------------------------- ### Integrate with acts_as_tenant for Scoping Source: https://github.com/rameerez/organizations/blob/main/README.md Include the `Organizations::Controller` and `Organizations::ActsAsTenantIntegration` concerns in your `ApplicationController` to automatically scope queries based on the current organization. This integration simplifies tenant-specific data management. ```ruby class ApplicationController < ActionController::Base include Organizations::Controller include Organizations::ActsAsTenantIntegration # Automatically calls: set_current_tenant(current_organization) end ``` -------------------------------- ### Configure Organizations Engine Source: https://github.com/rameerez/organizations/blob/main/README.md The primary configuration block for the Organizations gem. It defines authentication methods, invitation settings, redirect paths, and lifecycle hooks for organization management. ```ruby Organizations.configure do |config| config.current_user_method = :current_user config.authenticate_user_method = :authenticate_user! config.invitation_expiry = 7.days config.redirect_path_when_no_organization = "/organizations/new" config.after_organization_created_redirect_path = "/dashboard" config.on_organization_created do |ctx| # Handle organization creation logic end end ``` -------------------------------- ### Configure Authentication Methods Source: https://github.com/rameerez/organizations/blob/main/README.md Configure custom user methods for authentication when using Rodauth, Sorcery, or other auth systems. This involves setting the `current_user_method` and `authenticate_user_method` in the Organizations configuration. ```ruby Organizations.configure do |config| config.current_user_method = :current_account config.authenticate_user_method = :require_login end ``` -------------------------------- ### Integrate with Rameerez Gem Ecosystem Source: https://github.com/rameerez/organizations/blob/main/README.md Integrate the Organizations gem with other gems in the rameerez ecosystem, such as `api_keys`, `usage_credits`, and `pricing_plans`. This allows organizations to manage API keys, credits, and pricing plans directly. ```ruby # Organization owns API keys (api_keys gem) class Organization < ApplicationRecord has_api_keys do max_keys 10 end end # Organization has credits (usage_credits gem) class Organization < ApplicationRecord has_credits end # Organization has pricing plan (pricing_plans gem) class Organization < ApplicationRecord include PricingPlans::PlanOwner end # Accessing through current_organization current_organization.api_keys current_organization.credits current_organization.current_pricing_plan current_organization.memberships ``` -------------------------------- ### Organizations Gem Custom Role Definitions Source: https://context7.com/rameerez/organizations/llms.txt This Ruby code snippet defines custom roles and their associated permissions within the Organizations gem. It demonstrates role inheritance and how to check for specific permissions using `has_organization_permission_to?` and `require_organization_permission_to!`. ```ruby # config/initializers/organizations.rb Organizations.configure do |config| config.roles do role :viewer do can :view_organization can :view_members end role :member, inherits: :viewer do can :create_resources can :edit_own_resources can :delete_own_resources end role :admin, inherits: :member do can :invite_members can :remove_members can :edit_member_roles can :manage_settings can :view_billing can :manage_api_keys # Custom permission can :export_data # Custom permission end role :owner, inherits: :admin do can :manage_billing can :transfer_ownership can :delete_organization end end end # Check custom permissions current_user.has_organization_permission_to?(:manage_api_keys) require_organization_permission_to!(:export_data) ``` -------------------------------- ### Add Custom Permissions to Roles (Ruby) Source: https://github.com/rameerez/organizations/blob/main/README.md Illustrates how to add custom permissions to existing roles, such as `:admin`. It also shows how to check for these custom permissions using `has_organization_permission_to?` and `require_organization_permission_to!`. ```ruby role :admin, inherits: :member do can :invite_members can :remove_members can :manage_api_keys # Your custom permission can :export_data # Your custom permission end current_user.has_organization_permission_to?(:manage_api_keys) require_organization_permission_to!(:export_data) ``` -------------------------------- ### Add Custom Fields to Organizations Migration Source: https://context7.com/rameerez/organizations/llms.txt A Rails migration to extend the organizations table with support email, billing address, and a JSONB settings column. ```ruby class AddCustomFieldsToOrganizations < ActiveRecord::Migration[8.0] def change add_column :organizations_organizations, :support_email, :string add_column :organizations_organizations, :billing_address, :text add_column :organizations_organizations, :settings, :jsonb, default: {} end end ``` -------------------------------- ### Implement Request-Level Memoization in Ruby Source: https://github.com/rameerez/organizations/blob/main/README.md Explains the memoization of the current_organization object within a controller request to prevent repeated database lookups. ```ruby current_organization ``` -------------------------------- ### Add Existing Member to Organization (Ruby) Source: https://github.com/rameerez/organizations/blob/main/README.md Shows how the organizations gem adds an existing user to an organization. If the user is already a member, it returns the existing membership without raising an error. ```ruby org.add_member!(existing_user) # => Returns existing membership (doesn't raise) ``` -------------------------------- ### Check Organization Permissions (Ruby) Source: https://context7.com/rameerez/organizations/llms.txt Verifies specific permissions for the current user within the active organization or a specific membership. It leverages a pre-computed permission system. Permissions can be checked directly on the user or through their membership object. It also allows checking permissions against predefined roles. ```ruby # Check permission in current organization current_user.has_organization_permission_to?(:invite_members) # => true/false current_user.has_organization_permission_to?(:manage_billing) # => true/false current_user.has_organization_permission_to?(:manage_settings) # => true/false # Membership-level permission check membership = current_user.current_membership membership.has_permission_to?(:invite_members) # => true/false membership.permissions # => [:view_members, :invite_members, ...] # Direct Roles module access Organizations::Roles.has_permission?(:admin, :invite_members) # => true Organizations::Roles.has_permission?(:member, :invite_members) # => false Organizations::Roles.permissions_for(:admin) # => [:view_organization, :invite_members, ...] ``` -------------------------------- ### Manage Atomic Transaction Boundaries in Ruby Source: https://github.com/rameerez/organizations/blob/main/README.md Shows how multi-step operations are wrapped in database transactions to ensure atomicity and data integrity. ```ruby user.create_organization!("Acme") invitation.accept! org.transfer_ownership_to!(user) ``` -------------------------------- ### Configure Organization Lifecycle Callbacks Source: https://github.com/rameerez/organizations/blob/main/README.md Configure callbacks to hook into organization lifecycle events such as creation, member joining/removal, role changes, and ownership transfers. These callbacks can trigger notifications, analytics, or validation. ```ruby Organizations.configure do |config| config.on_organization_created do |ctx| SlackNotifier.notify("New org: #{ctx.organization.name}") Analytics.track(ctx.user, "organization_created") end config.on_member_joined do |ctx| WelcomeMailer.send_team_welcome(ctx.user, ctx.organization).deliver_later Analytics.track(ctx.user, "joined_organization", org: ctx.organization.name) end config.on_member_removed do |ctx| AuditLog.record( action: :member_removed, organization: ctx.organization, user: ctx.user, actor: ctx.removed_by ) end end ``` -------------------------------- ### Configure Organization Auto-Creation Source: https://github.com/rameerez/organizations/blob/main/README.md Configures the engine to automatically create a personal organization upon user signup. It allows customization of the organization name via a lambda function. ```ruby Organizations.configure do |config| config.always_create_personal_organization_for_each_user = true config.default_organization_name = ->(user) { "#{user.email.split('@').first}'s Workspace" } end ``` -------------------------------- ### Controller Guards for Organization Context (Ruby) Source: https://context7.com/rameerez/organizations/llms.txt Provides controller concerns and helper methods to enforce organization context and perform role/permission checks within controllers. This includes guards for requiring an organization to be set, specific roles, or specific permissions before allowing controller actions. ```ruby # app/controllers/application_controller.rb class ApplicationController < ActionController::Base include Organizations::Controller # or Organizations::ControllerHelpers end # Protect controllers with before_action guards class ProjectsController < ApplicationController before_action :require_organization! before_action :require_organization_admin!, only: [:create, :destroy] def billing require_organization_owner! # Only owners can manage billing end end # Permission-based guards class SettingsController < ApplicationController before_action :require_organization! before_action -> { require_organization_permission_to!(:manage_settings) } end # Available guards # require_organization! - Requires current org to be set # require_organization_role!(:admin) - Requires at least admin role # require_organization_admin! - Shortcut for admin role # require_organization_owner! - Shortcut for owner role # require_organization_permission_to!(:action) - Requires specific permission ``` -------------------------------- ### Minitest Assertions for Organization Permissions Source: https://github.com/rameerez/organizations/blob/main/README.md Use provided Minitest assertions to easily check user roles and permissions within organizations. These assertions simplify common checks like membership, ownership, admin status, and specific permissions. ```ruby assert user.is_member_of?(org) assert user.is_owner_of?(org) assert user.is_organization_admin? assert user.has_organization_permission_to?(:invite_members) assert user.belongs_to_any_organization? ``` -------------------------------- ### Switch Organizations Manually Source: https://github.com/rameerez/organizations/blob/main/README.md Update the current organization context for a user by calling the switch helper within a controller action. This updates the session and ensures subsequent queries are scoped to the new organization. ```ruby def switch org = current_user.organizations.find(params[:id]) switch_to_organization!(org) redirect_to dashboard_path end ``` -------------------------------- ### Handle Invitation Acceptance Results Source: https://github.com/rameerez/organizations/blob/main/README.md Process the result of an invitation acceptance, which returns an object containing membership details or failure reasons. This allows for conditional logic based on whether the user was already a member or if the invitation was successfully accepted. ```ruby result = accept_pending_organization_invitation!(user, return_failure: true) if result.success? # InvitationAcceptanceResult else # InvitationAcceptanceFailure result.failure_reason # => :missing_token, :email_mismatch, :invitation_expired, etc. end ``` -------------------------------- ### Reset and Layout CSS for 404 Page Source: https://github.com/rameerez/organizations/blob/main/test/dummy/public/404.html This CSS snippet provides a global reset, typography settings, and a centered grid layout for the error page. It uses modern CSS features like clamp() for fluid typography and media queries for responsive adjustments. ```css *, *::before, *::after { box-sizing: border-box; } * { margin: 0; } html { font-size: 16px; } body { background: #FFF; color: #261B23; display: grid; font-family: ui-sans-serif, system-ui, -apple-system, BlinkMacSystemFont, Aptos, Roboto, "Segoe UI", "Helvetica Neue", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji"; font-size: clamp(1rem, 2.5vw, 2rem); -webkit-font-smoothing: antialiased; font-style: normal; font-weight: 400; letter-spacing: -0.0025em; line-height: 1.4; min-height: 100vh; place-items: center; text-rendering: optimizeLegibility; -webkit-text-size-adjust: 100%; } a { color: inherit; font-weight: 700; text-decoration: underline; text-underline-offset: 0.0925em; } b, strong { font-weight: 700; } i, em { font-style: italic; } main { display: grid; gap: 1em; padding: 2em; place-items: center; text-align: center; } main header { width: min(100%, 12em); } main header svg { height: auto; max-width: 100%; width: 100%; } main article { width: min(100%, 30em); } main article p { font-size: 75%; } main article br { display: none; @media(min-width: 48em) { display: inline; } } ``` -------------------------------- ### Configure Controller Integration Source: https://github.com/rameerez/organizations/blob/main/README.md Integrate the organization management functionality into your Rails application by including the controller concern in the ApplicationController. ```ruby class ApplicationController < ActionController::Base include Organizations::Controller end ``` -------------------------------- ### Perform Permission Checks in Views Source: https://github.com/rameerez/organizations/blob/main/README.md Use helper methods to conditionally display UI elements based on the current user's organization permissions or roles. ```ruby <% if current_user.has_organization_permission_to?(:invite_members) %> <%= link_to "Invite teammate", new_invitation_path %> <% end %> <% if current_user.is_organization_admin? %> <%= link_to "Settings", organization_settings_path %> <% end %> ``` -------------------------------- ### Display Organization Invitation Count Badge (Ruby) Source: https://github.com/rameerez/organizations/blob/main/README.md A Ruby helper function to display a badge indicating the number of pending organization invitations for the current user. If there are no pending invitations, it returns nil. This is typically used in a navigation bar. ```ruby # Show pending invitation count in your navbar <%= organization_invitation_badge(current_user) %> # => 3 (if 3 pending invitations) # => nil (if no pending invitations) ``` -------------------------------- ### Define User Model Organization Requirements Source: https://github.com/rameerez/organizations/blob/main/README.md Configures the User model to enforce organization membership and enable automatic personal organization creation during the signup process. ```ruby class User < ApplicationRecord has_organizations do create_personal_org true require_organization true end end ```