Adhocracy <img src=“https://badge.fury.io/rb/adhocracy.png” alt=“Gem Version” /> <img src=“https://travis-ci.org/timothycommoner/adhocracy.png?branch=master” alt=“Build Status” /> <img src=“https://coveralls.io/repos/timothycommoner/adhocracy/badge.png” alt=“Coverage Status” /> <img src=“https://codeclimate.com/github/timothycommoner/adhocracy.png” />

A group management engine for Rails.

Compatibility

Adhocracy is a Rails Engine, which means it plays best with Rails applications. So far, it’s only been tested with Rails 4.

Usage

Installation

  1. Add Adhocracy to your gemfile: ‘gem ’adhocracy’‘

  2. Run bundler: ‘bundle install`

  3. Run this in your app folder: ‘rake adhocracy:install:migrations`

  4. Run your migrations: ‘rake db:migrate`

  5. Add ‘acts_as_group` to the models you want to have group functionality

  6. Add ‘acts_as_member` to the models you want to have member functionality

Creating Groups and Members

Scenario: You want your Club model to have group functionality. Ideally, it’ll accept members from both the Student and Teacher model.

First, add ‘acts_as_group` to your Club model:

class Club < ActiveRecord::Base
  acts_as_group
end

Then, add ‘acts_as_member’ to your Student and Teacher models:

class Student < ActiveRecord::Base
  acts_as_member
end

class Teacher < ActiveRecord::Base
  acts_as_member
end

After that, you can add members to your Club by:

@club.add_member(@student)
@student.join_group(@club)
@teacher.join_group(@club)

List the current members:

@club.members # => returns an array with @student and @teacher in it

Or list the groups a member is a part of:

@student.groups # => returns an array with only @club

Check for membership:

@student.member_of?(@club) # => true
@club.has_member?(@teacher) # => true

And destroy memberships

@student.leave_group(@club)
@group.remove_member(@teacher)

Officers

Scenario: You want a special class of membership that has the privilege to edit the group and accept/invite new members.

Adhocracy allows for administrative members called officers. You can create a new officer with:

@club.add_officer(@student)
# or
@student.promote(@club)

Later, you can strip an officer of her title with:

@club.demote_officer(@student)
# or
@student.demote(@club)

You can check if a member is an officer with:

@club.has_officer?(@member)
# or
@member.officer_of?(@club)

Or get a full list of officers:

@club.officers
# or of administered groups
@member.administrations

Inviting Members

Scenario: You want your users to have control over which groups they join.

Groups can send invitations to users:

@club.invite_member(@student)
@student.groups # => does not yet include @club
@student.group_invitations # => array including @club

The user can either accept or decline:

@invitation = @student.membership_invitations.first
@invitation.accept
- or -
@invitation.decline

If they accept, then they become a member of the group.

Invitations can be queried for their state of acceptance:

@invitation.pending?
@invitation.accepted?
@invitation.declined?

You can also check to see if an invitation is already sent:

@student.invited_to?(@club)
@club.invited?(@student)

These checks can include the state of the invitation:

@student.invited_to?(@club, { pending: true })
@student.invited_to?(@club, { accepted: false })
@student.invited_to?(@club, { declined: false })

Requesting Membership

Scenario: You want your groups to have control over which users join them.

Interested users can send membership requests to groups:

@student.request_membership_in(@club)
@club.members # => does not yet include @student
@club.membership_requests # => array including @student

The group can either accept or decline:

@request = @club.membership_requests.first
@request.accept
- or -
@request.decline

Requests can be queried for their state of acceptance:

@request.pending?
@request.accepted?
@request.declined?

You can also check to see if a request is already sent:

@student.requested_membership_in?(@club)
@club.recieved_request_from?(@student)

These checks can include the state of the request:

@student.requested_membership_in?(@club, { pending: true })
@student.requested_membership_in?(@club, { accepted: false })
@student.requested_membership_in?(@club, { declined: false })

Nesting Groups

Scenario: You want your Company model to have many subsidiary companies.

Create a model that both ‘acts_as_group` and `acts_as_member`:

class Company < ActiveRecord::Base
  acts_as_group
  acts_as_member
end

You can then nest companies like you would any other member:

@company.add_member(@child_company)
@child_company.add_member(@grand_child_company)

Limitations

Unfortunately, Rails does not support ‘has_many through` relationships that pass through a polymorphic join table, which we do in Adhocracy. In order to work around this limitation, we’ve created pseudo-associations, such as ‘@club.members` and `@user.groups`. These pseudo-associations return a simple array, rather than the feature-packed associations Rails typically uses. This means you can’t chain in scopes or use commands like ‘@user.groups.create`. If you know of a way we can reincorporate Rails associations without losing our ability to join groups and members polymorphically, please share your thoughts!

Issues

Adhocracy is still very young, and both its functionality and its documentation are bound to be lacking. If you’re having trouble with something, feel free to open an issue.

Contributing

Feel free to send us a pull request. Public methods, callbacks, and validations should have Rspec tests to back them up.

Contributors

Timothy Baron

License

Released under the MIT license.