Groupify Build Status Dependency Status

Adds group and membership functionality to Rails models.

The following ORMs are supported: Mongoid 3.1 & 4.0, ActiveRecord 3.2 & 4.0

The following Rubies are supported: Ruby 1.9.3, 2.0.0 (MRI, REE, JRuby, and Rubinius).

Installation

Add this line to your application's Gemfile:

gem 'groupify'

And then execute:

$ bundle

Or install it yourself as:

$ gem install groupify

Getting Started

Active Record

Add a migration similar to the following:

class CreateGroups < ActiveRecord::Migration
  def change
    create_table :groups do |t|
      t.string     :type      # Only needed if using single table inheritence
    end

    create_table :group_memberships do |t|
      t.string     :member_type   # Needed to make polymorphic members work
      t.integer    :member_id   # The member that belongs to this group
      t.integer    :group_id    # The group to which the member belongs
      t.string     :group_name    # Links a member to a named group (if using named groups)
    end

    add_index :group_memberships, [:member_id, :member_type]
    add_index :group_memberships, :group_id
    add_index :group_memberships, :group_name
  end
end

In your group model:

class Group < ActiveRecord::Base  
  acts_as_group :members => [:users, :assignments], :default_members => :users
end

In your member models (i.e. User):

class User < ActiveRecord::Base
  acts_as_group_member
  acts_as_named_group_member
end

class Assignment < ActiveRecord::Base
  acts_as_group_member
end

Mongoid

In your group model:

class Group
  include Mongoid::Document

  acts_as_group :members => [:users], :default_members => :users
end

In your member models (i.e. User):

class User
  include Mongoid::Document

  acts_as_group_member
  acts_as_named_group_member
end

Basic Usage

Create groups and add members:

group = Group.new
user = User.new

user.groups << group
# or
group.add user

user.in_group?(group)
# => true

Add to named groups:

user.named_groups << :admin
user.in_named_group?(:admin)
# => true

Check if two members share any of the same groups:

user1.shares_any_group?(user2)
user2.shares_any_named_group?(user1)

Query for groups & members:

User.in_group(group)         # Find all users in this group
User.in_named_group(:admin)  # Find all users in this named group
Group.with_member(user)      # Find all groups with this user

User.shares_any_group(user)       # Find all users that share any groups with this user
User.shares_any_named_group(user) # Find all users that share any named groups with this user

Merge one group into another:

# Moves the members of source into destination, and destroys source
destination_group.merge!(source_group)

Check the specs for more details.

Using for Authorization

Groupify was originally created to help implement user authorization, although it can be used generically for much more than that. Here is how to do it.

With CanCan

class Ability
  include CanCan::Ability

  def initialize(user)
    
    # Implements group authorization
    # Users can only manage assignment which belong to the same group
    can [:manage], Assignment, Assignment.shares_any_group(user) do |assignment|
      assignment.shares_any_group?(user)
    end
  end
end

Contributing

  1. Fork it
  2. Create your feature branch (git checkout -b my-new-feature)
  3. Commit your changes (git commit -am 'Added some feature')
  4. Push to the branch (git push origin my-new-feature)
  5. Create new Pull Request