adhocracy 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (33) hide show
  1. checksums.yaml +13 -5
  2. data/README.rdoc +1 -1
  3. data/app/models/adhocracy/membership.rb +15 -0
  4. data/app/models/adhocracy/membership_invitation.rb +31 -0
  5. data/app/models/adhocracy/membership_request.rb +31 -0
  6. data/config/locales/en.yml +14 -0
  7. data/db/migrate/20140306214416_create_adhocracy_memberships.rb +19 -0
  8. data/db/migrate/20140306214532_create_adhocracy_membership_requests.rb +23 -0
  9. data/db/migrate/20140306214552_create_adhocracy_membership_invitations.rb +23 -0
  10. data/lib/adhocracy/acceptable.rb +18 -0
  11. data/lib/adhocracy/acts_as_group.rb +65 -0
  12. data/lib/adhocracy/acts_as_member.rb +63 -0
  13. data/lib/adhocracy/membership_association.rb +49 -0
  14. data/lib/adhocracy/version.rb +1 -1
  15. data/lib/adhocracy.rb +4 -0
  16. data/spec/dummy/app/models/adhoc.rb +3 -0
  17. data/spec/dummy/app/models/user.rb +3 -0
  18. data/spec/dummy/config/application.rb +1 -0
  19. data/spec/dummy/db/migrate/20140306214611_create_users.rb +6 -0
  20. data/spec/dummy/db/migrate/20140306214620_create_adhocs.rb +6 -0
  21. data/spec/dummy/db/schema.rb +67 -0
  22. data/spec/factories/adhoc.rb +4 -0
  23. data/spec/factories/adhocracy_membership_invitations.rb +8 -0
  24. data/spec/factories/adhocracy_membership_requests.rb +8 -0
  25. data/spec/factories/adhocracy_memberships.rb +7 -0
  26. data/spec/factories/users.rb +4 -0
  27. data/spec/lib/adhocracy/acts_as_group_spec.rb +179 -0
  28. data/spec/lib/adhocracy/acts_as_member_spec.rb +179 -0
  29. data/spec/models/adhocracy/membership_invitation_spec.rb +84 -0
  30. data/spec/models/adhocracy/membership_request_spec.rb +84 -0
  31. data/spec/models/adhocracy/membership_spec.rb +21 -0
  32. metadata +114 -75
  33. data/spec/dummy/log/test.log +0 -0
checksums.yaml CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 11c39fb473477e9a5211094c1331882d05cee790
4
- data.tar.gz: 986064c8e5802aa87531ad9f266cdc13d39d7a56
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ Mzc4MGI0NWEzMTM5YTA5Mzg4NDcyNjlkMTRhODdlN2E3OGVlMDFjOA==
5
+ data.tar.gz: !binary |-
6
+ MWU2NTA1NTkyYTBiNGFjMDI3OTllN2U2ZTM4MjJmMjI4Yjc2MjJkMQ==
5
7
  SHA512:
6
- metadata.gz: 1275ed58ab98887053037b999b5257c5f0dc95d29e57d7c99de75b5b870ebacfdabb402f898ffb87b6075136a0ca7b712a9ec5df2ccf13aa61cf3ada140e8d59
7
- data.tar.gz: ebabc49ef8de133c6bd946db1b3b35c5f524e56d570f73b08c609f3723869408ce31c057524166a43c0850946aaa7507a308bd1482cd6ee2b334480ba17614a5
8
+ metadata.gz: !binary |-
9
+ Y2U1ZDBiYjQ0NzZjNGNkMmFmOTFlOTYwMjVmN2U3NDEzNTI1MDA0ODU2ZDhl
10
+ YzFkZmJmM2JhNGJhODZiODQ2YzRkMzBlYzY3MTMxNDRmMmY4MTNkOWNjOTU1
11
+ YzQ2NWQxYTA1YjUyYWJiMjExMzg1NWE5NWMxYjU4MmVlNjg2ZGQ=
12
+ data.tar.gz: !binary |-
13
+ YTMyZTIwZjM2MjA5OWQxNzgyYWZlNTI1M2UzNmVkOTk1ZGMyZGM2ZTU3YzJm
14
+ ZDE4NWIyYmMzMWYwYjAyMDFmMGM0NGEyNDg5YmI3ZDQ3OGY2ZDMxM2E5YWE5
15
+ MjVmZDUxOTIzZWZhZmU2MWRmNGRlYzRjM2U0MTI4NmIxYmY2YWU=
data/README.rdoc CHANGED
@@ -1,3 +1,3 @@
1
- = Adhocracy {<img src="https://codeclimate.com/github/timothycommoner/adhocracy.png" />}[https://codeclimate.com/github/timothycommoner/adhocracy]
1
+ = Adhocracy {<img src="https://badge.fury.io/rb/adhocracy.png" alt="Gem Version" />}[http://badge.fury.io/rb/adhocracy] {<img src="https://travis-ci.org/timothycommoner/adhocracy.png?branch=master" alt="Build Status" />}[https://travis-ci.org/timothycommoner/adhocracy] {<img src="https://codeclimate.com/github/timothycommoner/adhocracy.png" />}[https://codeclimate.com/github/timothycommoner/adhocracy]
2
2
 
3
3
  A group management engine for Rails.
@@ -0,0 +1,15 @@
1
+ module Adhocracy
2
+ class Membership < ActiveRecord::Base
3
+ belongs_to :member, polymorphic: true
4
+ belongs_to :group, polymorphic: true
5
+
6
+ validate :membership_is_unique
7
+
8
+ private
9
+ def membership_is_unique
10
+ if self.member.member_of?(self.group)
11
+ errors[:base] << I18n.t("activerecord.errors.models.membership.not_unique")
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,31 @@
1
+ module Adhocracy
2
+ class MembershipInvitation < ActiveRecord::Base
3
+ include Acceptable
4
+
5
+ belongs_to :member, polymorphic: true
6
+ belongs_to :group, polymorphic: true
7
+
8
+ validate :no_pending_invitations, on: :create
9
+ validate :no_declined_invitations, on: :create
10
+ validate :not_currently_a_member, on: :create
11
+
12
+ private
13
+ def no_pending_invitations
14
+ if self.member.invited_to?(self.group, { pending: true })
15
+ errors[:base] << I18n.t("activerecord.errors.models.membership_invitation.still_pending")
16
+ end
17
+ end
18
+
19
+ def no_declined_invitations
20
+ if self.member.invited_to?(self.group, { declined: true })
21
+ errors[:base] << I18n.t("activerecord.errors.models.membership_invitation.already_declined")
22
+ end
23
+ end
24
+
25
+ def not_currently_a_member
26
+ if self.member.member_of?(self.group)
27
+ errors[:base] << I18n.t("activerecord.errors.models.membership_invitation.already_a_member")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,31 @@
1
+ module Adhocracy
2
+ class MembershipRequest < ActiveRecord::Base
3
+ include Acceptable
4
+
5
+ belongs_to :member, polymorphic: true
6
+ belongs_to :group, polymorphic: true
7
+
8
+ validate :no_pending_requests, on: :create
9
+ validate :no_declined_requests, on: :create
10
+ validate :not_currently_a_member, on: :create
11
+
12
+ private
13
+ def no_pending_requests
14
+ if self.member.requested_membership_in?(self.group, { pending: true })
15
+ errors[:base] << I18n.t("activerecord.errors.models.membership_request.still_pending")
16
+ end
17
+ end
18
+
19
+ def no_declined_requests
20
+ if self.member.requested_membership_in?(self.group, { declined: true })
21
+ errors[:base] << I18n.t("activerecord.errors.models.membership_request.already_declined")
22
+ end
23
+ end
24
+
25
+ def not_currently_a_member
26
+ if self.member.member_of?(self.group)
27
+ errors[:base] << I18n.t("activerecord.errors.models.membership_request.already_a_member")
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,14 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ models:
5
+ membership:
6
+ not_unique: "Membership already exists."
7
+ membership_invitation:
8
+ still_pending: "Invitation still pending."
9
+ already_declined: "Cannot invite member again."
10
+ already_a_member: "Already a member."
11
+ membership_request:
12
+ still_pending: "Request still pending."
13
+ already_declined: "Cannot request membership again."
14
+ already_a_member: "Already a member."
@@ -0,0 +1,19 @@
1
+ class CreateAdhocracyMemberships < ActiveRecord::Migration
2
+ def change
3
+ create_table :adhocracy_memberships do |t|
4
+ t.string :member_type
5
+ t.integer :member_id
6
+ t.string :group_type
7
+ t.integer :group_id
8
+
9
+ t.timestamps
10
+ end
11
+
12
+ add_index :adhocracy_memberships,
13
+ [:member_id, :member_type],
14
+ name: "adhocracy_memberships_on_member"
15
+ add_index :adhocracy_memberships,
16
+ [:group_id, :group_type],
17
+ name: "adhocracy_memberships_on_group"
18
+ end
19
+ end
@@ -0,0 +1,23 @@
1
+ class CreateAdhocracyMembershipRequests < ActiveRecord::Migration
2
+ def change
3
+ create_table :adhocracy_membership_requests do |t|
4
+ t.string :member_type
5
+ t.integer :member_id
6
+ t.string :group_type
7
+ t.integer :group_id
8
+
9
+ t.boolean :pending, null: false, default: true
10
+ t.boolean :accepted, null: false, default: false
11
+ t.boolean :declined, null: false, default: false
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :adhocracy_membership_requests,
17
+ [:member_id, :member_type],
18
+ name: "adhocracy_membership_requests_on_member"
19
+ add_index :adhocracy_memberships,
20
+ [:group_id, :group_type],
21
+ name: "adhocracy_membership_requests_on_group"
22
+ end
23
+ end
@@ -0,0 +1,23 @@
1
+ class CreateAdhocracyMembershipInvitations < ActiveRecord::Migration
2
+ def change
3
+ create_table :adhocracy_membership_invitations do |t|
4
+ t.string :member_type
5
+ t.integer :member_id
6
+ t.string :group_type
7
+ t.integer :group_id
8
+
9
+ t.boolean :pending, null: false, default: true
10
+ t.boolean :accepted, null: false, default: false
11
+ t.boolean :declined, null: false, default: false
12
+
13
+ t.timestamps
14
+ end
15
+
16
+ add_index :adhocracy_membership_invitations,
17
+ [:member_id, :member_type],
18
+ name: "adhocracy_membership_invitations_on_member"
19
+ add_index :adhocracy_memberships,
20
+ [:group_id, :group_type],
21
+ name: "adhocracy_membership_invitations_on_group"
22
+ end
23
+ end
@@ -0,0 +1,18 @@
1
+ module Adhocracy
2
+ module Acceptable
3
+ extend ActiveSupport::Concern
4
+
5
+ def accept
6
+ if pending?
7
+ member.join_group(group)
8
+ update_attributes(pending: false, accepted: true, declined: false)
9
+ end
10
+ end
11
+
12
+ def decline
13
+ if pending?
14
+ update_attributes(pending: false, accepted: false, declined: true)
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,65 @@
1
+ module Adhocracy
2
+ module ActsAsGroup
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_group(options = {})
7
+
8
+ has_many :memberships, as: :group, class_name: "Adhocracy::Membership"
9
+ has_many :membership_invitations, as: :group,
10
+ class_name: "Adhocracy::MembershipInvitation"
11
+ has_many :membership_requests, as: :group,
12
+ class_name: "Adhocracy::MembershipRequest"
13
+
14
+ send :include, InstanceMethods
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ def members
20
+ MembershipAssociation.new(group: self).list_members_for_membership
21
+ end
22
+
23
+ def invited_members
24
+ MembershipAssociation.new(group: self).
25
+ list_members_for_membership_invitation
26
+ end
27
+
28
+ def membership_requests
29
+ MembershipAssociation.new(group: self).
30
+ list_members_for_membership_request
31
+ end
32
+
33
+ def add_member(member)
34
+ MembershipAssociation.new(member: member, group: self).
35
+ create_membership
36
+ end
37
+
38
+ def invite_member(member)
39
+ MembershipAssociation.new(member: member, group: self).
40
+ create_membership_invitation
41
+ end
42
+
43
+ def remove_member(member)
44
+ MembershipAssociation.new(member: member, group: self).
45
+ destroy_membership
46
+ end
47
+
48
+ def has_member?(member)
49
+ MembershipAssociation.new(member: member, group: self).verify_membership
50
+ end
51
+
52
+ def invited?(member, params = {})
53
+ params.merge!({ member: member, group: self })
54
+ MembershipAssociation.new(params).verify_membership_invitation
55
+ end
56
+
57
+ def recieved_request_from?(member, params = {})
58
+ params.merge!({ member: member, group: self })
59
+ MembershipAssociation.new(params).verify_membership_request
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ ActiveRecord::Base.send :include, Adhocracy::ActsAsGroup
@@ -0,0 +1,63 @@
1
+ module Adhocracy
2
+ module ActsAsMember
3
+ extend ActiveSupport::Concern
4
+
5
+ module ClassMethods
6
+ def acts_as_member(options = {})
7
+
8
+ has_many :memberships, as: :member, class_name: "Adhocracy::Membership"
9
+ has_many :membership_invitations, as: :member,
10
+ class_name: "Adhocracy::MembershipInvitation"
11
+ has_many :membership_requests, as: :member,
12
+ class_name: "Adhocracy::MembershipRequest"
13
+
14
+ send :include, InstanceMethods
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ def groups
20
+ MembershipAssociation.new(member: self).list_groups_for_membership
21
+ end
22
+
23
+ def group_invitations
24
+ MembershipAssociation.new(member: self).
25
+ list_groups_for_membership_invitation
26
+ end
27
+
28
+ def membership_requests
29
+ MembershipAssociation.new(member: self).
30
+ list_groups_for_membership_request
31
+ end
32
+
33
+ def join_group(group)
34
+ MembershipAssociation.new(member: self, group: group).create_membership
35
+ end
36
+
37
+ def request_membership_in(group)
38
+ MembershipAssociation.new(member: self, group: group).
39
+ create_membership_request
40
+ end
41
+
42
+ def leave_group(group)
43
+ MembershipAssociation.new(member: self, group: group).destroy_membership
44
+ end
45
+
46
+ def member_of?(group)
47
+ MembershipAssociation.new(member: self, group: group).verify_membership
48
+ end
49
+
50
+ def invited_to?(group, params = {})
51
+ params.merge!({ member: self, group: group })
52
+ MembershipAssociation.new(params).verify_membership_invitation
53
+ end
54
+
55
+ def requested_membership_in?(group, params = {})
56
+ params.merge!({ member: self, group: group })
57
+ MembershipAssociation.new(params).verify_membership_request
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ ActiveRecord::Base.send :include, Adhocracy::ActsAsMember
@@ -0,0 +1,49 @@
1
+ module Adhocracy
2
+ class MembershipAssociation
3
+
4
+ attr_accessor :params
5
+ def initialize(params)
6
+ @params = params
7
+ end
8
+
9
+ # Creates Methods:
10
+ # list_membership, create_membership, destroy_membership, verify_membership
11
+ # list_membership_request, create_membership_request,
12
+ # destroy_membership_request, verify_membership_request
13
+ # list_membership_invitation, create_membership_invitation,
14
+ # destroy_membership_invitation, verify_membership_invitation
15
+
16
+ %w(membership membership_request membership_invitation).each do |type|
17
+ %w(list_members_for list_groups_for create destroy verify).each do |action|
18
+ define_method "#{action}_#{type}" do
19
+ send("#{action}", "Adhocracy::#{type.camelize}".constantize)
20
+ end
21
+ end
22
+ end
23
+
24
+ private
25
+ def list_members_for(membership_class)
26
+ membership_class.where(group: params[:group]).includes(:member).
27
+ collect { |membership| membership.member }
28
+ end
29
+
30
+ def list_groups_for(membership_class)
31
+ membership_class.where(member: params[:member]).includes(:group).
32
+ collect { |membership| membership.group }
33
+ end
34
+
35
+ def create(membership_class)
36
+ membership_class.create(params)
37
+ end
38
+
39
+ def destroy(membership_class)
40
+ membership = membership_class.find_by(params)
41
+ return false if membership.nil?
42
+ membership.destroy
43
+ end
44
+
45
+ def verify(membership_class)
46
+ membership_class.where(params).exists?
47
+ end
48
+ end
49
+ end
@@ -1,3 +1,3 @@
1
1
  module Adhocracy
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/adhocracy.rb CHANGED
@@ -1,4 +1,8 @@
1
1
  require "adhocracy/engine"
2
+ require "adhocracy/acts_as_member"
3
+ require "adhocracy/acts_as_group"
4
+ require "adhocracy/acceptable"
5
+ require "adhocracy/membership_association"
2
6
 
3
7
  module Adhocracy
4
8
  end
@@ -0,0 +1,3 @@
1
+ class Adhoc < ActiveRecord::Base
2
+ acts_as_group
3
+ end
@@ -0,0 +1,3 @@
1
+ class User < ActiveRecord::Base
2
+ acts_as_member
3
+ end
@@ -23,6 +23,7 @@ module Dummy
23
23
  # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
24
24
  # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
25
25
  # config.i18n.default_locale = :de
26
+ config.i18n.enforce_available_locales = true
26
27
  end
27
28
  end
28
29
 
@@ -0,0 +1,6 @@
1
+ class CreateUsers < ActiveRecord::Migration
2
+ def change
3
+ create_table :users do |t|
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,6 @@
1
+ class CreateAdhocs < ActiveRecord::Migration
2
+ def change
3
+ create_table :adhocs do |t|
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,67 @@
1
+ # encoding: UTF-8
2
+ # This file is auto-generated from the current state of the database. Instead
3
+ # of editing this file, please use the migrations feature of Active Record to
4
+ # incrementally modify your database, and then regenerate this schema definition.
5
+ #
6
+ # Note that this schema.rb definition is the authoritative source for your
7
+ # database schema. If you need to create the application database on another
8
+ # system, you should be using db:schema:load, not running all the migrations
9
+ # from scratch. The latter is a flawed and unsustainable approach (the more migrations
10
+ # you'll amass, the slower it'll run and the greater likelihood for issues).
11
+ #
12
+ # It's strongly recommended that you check this file into your version control system.
13
+
14
+ ActiveRecord::Schema.define(version: 20140306214620) do
15
+
16
+ # These are extensions that must be enabled in order to support this database
17
+ enable_extension "plpgsql"
18
+
19
+ create_table "adhocracy_membership_invitations", force: true do |t|
20
+ t.string "member_type"
21
+ t.integer "member_id"
22
+ t.string "group_type"
23
+ t.integer "group_id"
24
+ t.boolean "pending", default: true, null: false
25
+ t.boolean "accepted", default: false, null: false
26
+ t.boolean "declined", default: false, null: false
27
+ t.datetime "created_at"
28
+ t.datetime "updated_at"
29
+ end
30
+
31
+ add_index "adhocracy_membership_invitations", ["member_id", "member_type"], name: "adhocracy_membership_invitations_on_member", using: :btree
32
+
33
+ create_table "adhocracy_membership_requests", force: true do |t|
34
+ t.string "member_type"
35
+ t.integer "member_id"
36
+ t.string "group_type"
37
+ t.integer "group_id"
38
+ t.boolean "pending", default: true, null: false
39
+ t.boolean "accepted", default: false, null: false
40
+ t.boolean "declined", default: false, null: false
41
+ t.datetime "created_at"
42
+ t.datetime "updated_at"
43
+ end
44
+
45
+ add_index "adhocracy_membership_requests", ["member_id", "member_type"], name: "adhocracy_membership_requests_on_member", using: :btree
46
+
47
+ create_table "adhocracy_memberships", force: true do |t|
48
+ t.string "member_type"
49
+ t.integer "member_id"
50
+ t.string "group_type"
51
+ t.integer "group_id"
52
+ t.datetime "created_at"
53
+ t.datetime "updated_at"
54
+ end
55
+
56
+ add_index "adhocracy_memberships", ["group_id", "group_type"], name: "adhocracy_membership_invitations_on_group", using: :btree
57
+ add_index "adhocracy_memberships", ["group_id", "group_type"], name: "adhocracy_membership_requests_on_group", using: :btree
58
+ add_index "adhocracy_memberships", ["group_id", "group_type"], name: "adhocracy_memberships_on_group", using: :btree
59
+ add_index "adhocracy_memberships", ["member_id", "member_type"], name: "adhocracy_memberships_on_member", using: :btree
60
+
61
+ create_table "adhocs", force: true do |t|
62
+ end
63
+
64
+ create_table "users", force: true do |t|
65
+ end
66
+
67
+ end
@@ -0,0 +1,4 @@
1
+ FactoryGirl.define do
2
+ factory :adhoc do
3
+ end
4
+ end
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+ factory :adhocracy_membership_invitation,
3
+ :class => 'Adhocracy::MembershipInvitation',
4
+ aliases: [:membership_invitation] do
5
+ member { |a| a.association(:user) }
6
+ group { |a| a.association(:adhoc) }
7
+ end
8
+ end
@@ -0,0 +1,8 @@
1
+ FactoryGirl.define do
2
+ factory :adhocracy_membership_request,
3
+ :class => 'Adhocracy::MembershipRequest',
4
+ aliases: [:membership_request] do
5
+ member { |a| a.association(:user) }
6
+ group { |a| a.association(:adhoc) }
7
+ end
8
+ end
@@ -0,0 +1,7 @@
1
+ FactoryGirl.define do
2
+ factory :adhocracy_membership, :class => 'Adhocracy::Membership',
3
+ aliases: [:membership] do
4
+ member { |a| a.association(:user) }
5
+ group { |a| a.association(:adhoc) }
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ FactoryGirl.define do
2
+ factory :user do
3
+ end
4
+ end