camaraderie 0.1.3 → 0.2

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.
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --colour
data/.travis.yml ADDED
@@ -0,0 +1,11 @@
1
+ language: ruby
2
+
3
+ rvm:
4
+ - 2.0.0
5
+ - 1.9.3
6
+
7
+ gemfile:
8
+ - gemfiles/Gemfile.activerecord-4.0
9
+ - gemfiles/Gemfile.activerecord-3.2.x
10
+
11
+ script: "echo 'DO IT' && bundle exec rake spec"
data/README.md CHANGED
@@ -1,6 +1,7 @@
1
1
  # Camaraderie
2
2
 
3
3
  [![Gem Version](https://badge.fury.io/rb/camaraderie.png)](https://rubygems.org/gems/camaraderie)
4
+ [![Build Status](https://travis-ci.org/mirego/camaraderie.png?branch=master)](https://travis-ci.org/mirego/camaraderie)
4
5
 
5
6
  Camaraderie takes away the pain of managing membership stuff between users and organizations.
6
7
 
@@ -24,7 +25,14 @@ First, you have to configure which type of memberships you want. Usually, you’
24
25
 
25
26
  ```ruby
26
27
  Camaraderie.configure do |config|
28
+ # The different types of memberships (defaults to `['admin']`)
27
29
  config.membership_types = %w(admin moderator member)
30
+
31
+ # The class name of the organization model (defaults to `'Organization'`)
32
+ config.organization_class = 'Company'
33
+
34
+ # The class name of the user model (defaults to `'User'`)
35
+ config.user_class = 'Employee'
28
36
  end
29
37
  ```
30
38
 
@@ -59,6 +67,10 @@ organization.members.create(user: user)
59
67
  # Check whether the user is an admin
60
68
  user.admin_of?(organization)
61
69
  # => true
70
+
71
+ # List all the admins for the organization
72
+ organization.admin_users
73
+ # => #<ActiveRecord::Relation [<User id=1>]>
62
74
  ```
63
75
 
64
76
  ## License
data/Rakefile CHANGED
@@ -1 +1,11 @@
1
- require "bundler/gem_tasks"
1
+ require 'bundler'
2
+ require 'rake'
3
+ require 'bundler/gem_tasks'
4
+ require 'rspec/core/rake_task'
5
+
6
+ task default: :spec
7
+
8
+ desc 'Run all specs'
9
+ RSpec::Core::RakeTask.new(:spec) do |task|
10
+ task.pattern = 'spec/**/*_spec.rb'
11
+ end
data/camaraderie.gemspec CHANGED
@@ -20,6 +20,8 @@ Gem::Specification.new do |spec|
20
20
 
21
21
  spec.add_development_dependency 'bundler', '~> 1.3'
22
22
  spec.add_development_dependency 'rake'
23
+ spec.add_development_dependency 'sqlite3'
24
+ spec.add_development_dependency 'rspec', '~> 2.14'
23
25
 
24
26
  spec.add_dependency 'activerecord', '>= 3.0.0'
25
27
  spec.add_dependency 'activesupport', '>= 3.0.0'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '../'
4
+
5
+ gem 'activerecord', '~> 3.2.0'
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec path: '../'
4
+
5
+ gem 'activerecord', '~> 4.0.0'
@@ -3,8 +3,8 @@ module Camaraderie
3
3
  extend ActiveSupport::Concern
4
4
  included do
5
5
  # Associations
6
- belongs_to :user, validate: true
7
- belongs_to :organization
6
+ belongs_to :user, validate: true, class_name: Camaraderie.user_class, inverse_of: :memberships
7
+ belongs_to :organization, class_name: Camaraderie.organization_class, inverse_of: :memberships
8
8
 
9
9
  # Validations
10
10
  validates :user, presence: true
@@ -4,8 +4,8 @@ module Camaraderie
4
4
 
5
5
  included do
6
6
  # Associations
7
- has_many :memberships, dependent: :destroy
8
- has_many :users, through: :memberships
7
+ has_many :memberships, dependent: :destroy, foreign_key: :organization_id, class_name: 'Membership', inverse_of: :organization
8
+ has_many :users, through: :memberships, class_name: Camaraderie.user_class, inverse_of: :organizations
9
9
 
10
10
  # Define a method for each type of membership
11
11
  #
@@ -17,6 +17,10 @@ module Camaraderie
17
17
  def #{type.pluralize}
18
18
  memberships.#{type.pluralize}
19
19
  end
20
+
21
+ def #{type}_users
22
+ Camaraderie.user_class.constantize.where(id: #{type.pluralize}.pluck(:user_id))
23
+ end
20
24
  RUBY
21
25
  end
22
26
  end
@@ -4,13 +4,13 @@ module Camaraderie
4
4
 
5
5
  included do
6
6
  # Associations
7
- has_many :memberships, dependent: :destroy
8
- has_many :organizations, through: :memberships
7
+ has_many :memberships, dependent: :destroy, foreign_key: :user_id, class_name: 'Membership', inverse_of: :user
8
+ has_many :organizations, through: :memberships, class_name: Camaraderie.organization_class, inverse_of: :users
9
9
 
10
10
  Camaraderie.membership_types.each do |type|
11
11
  class_eval <<-RUBY, __FILE__, __LINE__ + 1
12
12
  def #{type}_of?(organization)
13
- !!memberships.#{type.pluralize}.where(organization: organization).exists?
13
+ !!memberships.#{type.pluralize}.where(organization_id: organization.id).exists?
14
14
  end
15
15
  RUBY
16
16
  end
@@ -1,3 +1,3 @@
1
1
  module Camaraderie
2
- VERSION = '0.1.3'
2
+ VERSION = '0.2'
3
3
  end
data/lib/camaraderie.rb CHANGED
@@ -1,5 +1,9 @@
1
1
  require 'camaraderie/version'
2
2
 
3
+ require 'active_support'
4
+ require 'active_record'
5
+ require 'ostruct'
6
+
3
7
  require 'camaraderie/user'
4
8
  require 'camaraderie/organization'
5
9
  require 'camaraderie/membership'
@@ -13,7 +17,17 @@ module Camaraderie
13
17
 
14
18
  # Return the allowed membership types
15
19
  def self.membership_types
16
- @configuration.membership_types
20
+ @configuration.membership_types.try(:map, &:to_s) || %w(member)
21
+ end
22
+
23
+ # Return the class name to use for the organizations association
24
+ def self.organization_class
25
+ @configuration.organization_class.try(:to_s) || 'Organization'
26
+ end
27
+
28
+ # Return the class name to use for the users association
29
+ def self.user_class
30
+ @configuration.user_class.try(:to_s) || 'User'
17
31
  end
18
32
 
19
33
  # Return a block that can be injected into a class
@@ -0,0 +1,93 @@
1
+ require 'spec_helper'
2
+
3
+ describe Camaraderie::Membership do
4
+ before do
5
+ Camaraderie.configure { |config| config.membership_types = %w(admin member) }
6
+ spawn_membership_model
7
+ spawn_organization_model
8
+ spawn_user_model
9
+
10
+ run_migration do
11
+ create_table(:users, force: true)
12
+ create_table(:organizations, force: true)
13
+ end
14
+ end
15
+
16
+ describe :Scopes do
17
+ let(:user1) { User.create }
18
+ let(:user2) { User.create }
19
+
20
+ let(:organization1) { Organization.create }
21
+ let(:organization2) { Organization.create }
22
+
23
+ before do
24
+ Membership.create(user: user1, organization: organization1, membership_type: 'admin')
25
+ Membership.create(user: user2, organization: organization2, membership_type: 'admin')
26
+ Membership.create(user: user1, organization: organization2, membership_type: 'member')
27
+ end
28
+
29
+ describe :admins do
30
+ subject { Membership.admins }
31
+
32
+ it { should be_an_instance_of(relation_class(Membership)) }
33
+ it { should have(2).items }
34
+ end
35
+
36
+ describe :members do
37
+ subject { Membership.members }
38
+
39
+ it { should be_an_instance_of(relation_class(Membership)) }
40
+ it { should have(1).item }
41
+ end
42
+ end
43
+
44
+ describe :Validations do
45
+ let(:membership) { Membership.create(attributes) }
46
+ subject { membership }
47
+
48
+ context 'with missing user' do
49
+ let(:attributes) { { organization: Organization.create, membership_type: 'admin' } }
50
+ it { should_not be_valid }
51
+
52
+ describe :errors do
53
+ subject { membership.errors }
54
+ it { should have(1).item }
55
+ its(:full_messages) { should include "User can't be blank" }
56
+ end
57
+ end
58
+
59
+ context 'with missing organization' do
60
+ let(:attributes) { { user: User.create, membership_type: 'admin' } }
61
+ it { should_not be_valid }
62
+
63
+ describe :errors do
64
+ subject { membership.errors }
65
+ it { should have(1).item }
66
+ its(:full_messages) { should include "Organization can't be blank" }
67
+ end
68
+ end
69
+
70
+ context 'with missing membership type' do
71
+ let(:attributes) { { user: User.create, organization: Organization.create } }
72
+ it { should_not be_valid }
73
+
74
+ describe :errors do
75
+ subject { membership.errors }
76
+ it { should have(2).item }
77
+ its(:full_messages) { should include "Membership type can't be blank" }
78
+ its(:full_messages) { should include "Membership type is not included in the list" }
79
+ end
80
+ end
81
+
82
+ context 'with wrong membership type' do
83
+ let(:attributes) { { membership_type: 'super_admin', user: User.create, organization: Organization.create } }
84
+ it { should_not be_valid }
85
+
86
+ describe :errors do
87
+ subject { membership.errors }
88
+ it { should have(1).item }
89
+ its(:full_messages) { should include "Membership type is not included in the list" }
90
+ end
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,78 @@
1
+ require 'spec_helper'
2
+
3
+ describe Camaraderie::Organization do
4
+ before do
5
+ Camaraderie.configure { |config| config.membership_types = %w(admin member) }
6
+ spawn_membership_model
7
+ spawn_organization_model
8
+ spawn_user_model
9
+
10
+ run_migration do
11
+ create_table(:users, force: true)
12
+ create_table(:organizations, force: true)
13
+ end
14
+ end
15
+
16
+ describe :InstanceMethods do
17
+ let(:organization) { Organization.create }
18
+ let(:user1) { User.create }
19
+ let(:user2) { User.create }
20
+ let(:user3) { User.create }
21
+
22
+ before do
23
+ organization.admins.create(user: user1)
24
+ organization.members.create(user: user2)
25
+ organization.members.create(user: user3)
26
+ end
27
+
28
+ describe :admins do
29
+ subject { organization.admins }
30
+ it { should be_an_instance_of(relation_class(Membership)) }
31
+ it { should have(1).item }
32
+ end
33
+
34
+ describe :members do
35
+ subject { organization.members }
36
+ it { should be_an_instance_of(relation_class(Membership)) }
37
+ it { should have(2).items }
38
+ end
39
+
40
+ describe :admin_users do
41
+ subject { organization.admin_users }
42
+ it { should be_an_instance_of(relation_class(User)) }
43
+ it { should have(1).item }
44
+ it { should include user1 }
45
+ end
46
+
47
+ describe :member_users do
48
+ subject { organization.member_users }
49
+ it { should be_an_instance_of(relation_class(User)) }
50
+ it { should have(2).items }
51
+ it { should include user2, user3 }
52
+ end
53
+ end
54
+
55
+ describe :Associations do
56
+ let(:organization) { Organization.create }
57
+ let(:user1) { User.create }
58
+ let(:user2) { User.create }
59
+ let(:user3) { User.create }
60
+
61
+ before do
62
+ organization.admins.create(user: user1)
63
+ organization.members.create(user: user2)
64
+ organization.members.create(user: user3)
65
+ end
66
+
67
+ describe :users do
68
+ subject { organization.users }
69
+ it { should have(3).items }
70
+
71
+ describe :DestroyDependent do
72
+ specify do
73
+ expect { organization.destroy }.to change { Membership.count }.from(3).to(0)
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,60 @@
1
+ require 'spec_helper'
2
+
3
+ describe Camaraderie::User do
4
+ before do
5
+ Camaraderie.configure { |config| config.membership_types = %w(admin member) }
6
+ spawn_membership_model
7
+ spawn_organization_model
8
+ spawn_user_model
9
+
10
+ run_migration do
11
+ create_table(:users, force: true)
12
+ create_table(:organizations, force: true)
13
+ end
14
+ end
15
+
16
+ describe :InstanceMethods do
17
+ subject do
18
+ User.create.tap do |user|
19
+ organization1.admins.create(user: user)
20
+ organization2.members.create(user: user)
21
+ end
22
+ end
23
+
24
+ let(:organization1) { Organization.create }
25
+ let(:organization2) { Organization.create }
26
+ let(:organization3) { Organization.create }
27
+
28
+ describe :admin_of? do
29
+ it{ should be_admin_of(organization1) }
30
+ it{ should_not be_admin_of(organization2) }
31
+ it{ should_not be_admin_of(organization3) }
32
+ end
33
+
34
+ describe :member_of? do
35
+ it{ should be_member_of(organization2) }
36
+ it{ should_not be_member_of(organization1) }
37
+ it{ should_not be_member_of(organization3) }
38
+ end
39
+ end
40
+
41
+ describe :Associations do
42
+ subject { user }
43
+ let(:user) { User.create }
44
+
45
+ before do
46
+ Organization.create.admins.create(user: user)
47
+ Organization.create.members.create(user: user)
48
+ end
49
+
50
+ describe :organizations do
51
+ its(:organizations) { should have(2).items }
52
+
53
+ describe :DestroyDependent do
54
+ specify do
55
+ expect { user.destroy }.to change { Membership.count }.from(2).to(0)
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Camaraderie do
4
+ describe 'Custom model classes' do
5
+ before do
6
+ Camaraderie.configure do |config|
7
+ config.organization_class = 'Company'
8
+ config.user_class = 'Employee'
9
+ config.membership_types = %w(admin member)
10
+ end
11
+
12
+ spawn_organization_model 'Company'
13
+ spawn_user_model 'Employee'
14
+ spawn_membership_model
15
+
16
+ run_migration do
17
+ create_table(:employees, force: true)
18
+ create_table(:companies, force: true)
19
+ end
20
+ end
21
+
22
+ let(:user) { Employee.create }
23
+ let(:organization) { Company.create }
24
+
25
+ before do
26
+ # FIXME Why doesn't this work?
27
+ # It bypasses `class_name: 'Employee'` and tries to use 'User'
28
+ #
29
+ # organization.admins.create(user: user)
30
+
31
+ # We'll have to use this for now
32
+ Membership.create(membership_type: 'admin', user: user, organization: organization)
33
+ end
34
+
35
+ it { expect(user).to be_admin_of(organization) }
36
+ it { expect(organization.admin_users).to include(user) }
37
+ end
38
+ end
@@ -0,0 +1,41 @@
1
+ $:.unshift File.expand_path('../lib', __FILE__)
2
+
3
+ require 'rspec'
4
+ require 'sqlite3'
5
+
6
+ require 'camaraderie'
7
+
8
+ # Require our macros and extensions
9
+ Dir[File.expand_path('../../spec/support/macros/*.rb', __FILE__)].map(&method(:require))
10
+
11
+ # Inject our methods into ActiveRecord (like our railtie does)
12
+ ActiveRecord::Base.class_eval(&Camaraderie.inject_into_active_record)
13
+
14
+ RSpec.configure do |config|
15
+ # Include our macros
16
+ config.include DatabaseMacros
17
+ config.include ModelMacros
18
+ config.include RailsMacros
19
+
20
+ config.before(:each) do
21
+ # Create the SQLite database
22
+ setup_database
23
+
24
+ # Run our migration
25
+ run_default_migration
26
+
27
+ # Reset Camaraderie.configuration
28
+ Camaraderie.instance_variable_set(:@configuration, nil)
29
+
30
+ # Prepare our models array
31
+ @spawned_models = []
32
+ end
33
+
34
+ config.after(:each) do
35
+ # Make sure we remove our test database file
36
+ cleanup_database
37
+
38
+ # Remove our models
39
+ @spawned_models.each { |model| Object.instance_eval { remove_const model.name.to_sym } }
40
+ end
41
+ end
@@ -0,0 +1,39 @@
1
+ module DatabaseMacros
2
+ # Run migrations in the test database
3
+ def run_migration(&block)
4
+ # Create a new migration class
5
+ klass = Class.new(ActiveRecord::Migration)
6
+
7
+ # Create a new `up` that executes the argument
8
+ klass.send(:define_method, :up) { self.instance_exec(&block) }
9
+
10
+ # Create a new instance of it and execute its `up` method
11
+ klass.new.up
12
+ end
13
+
14
+ def self.database_file
15
+ @database_file || File.expand_path('../test.db', __FILE__)
16
+ end
17
+
18
+ def setup_database
19
+ # Make sure the test database file is gone
20
+ cleanup_database
21
+
22
+ # Establish the connection
23
+ SQLite3::Database.new FileUtils.touch(DatabaseMacros.database_file).first
24
+ ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: DatabaseMacros.database_file)
25
+
26
+ # Silence everything
27
+ ActiveRecord::Base.logger = ActiveRecord::Migration.verbose = false
28
+ end
29
+
30
+ def cleanup_database
31
+ FileUtils.rm(DatabaseMacros.database_file) if File.exists?(DatabaseMacros.database_file)
32
+ end
33
+
34
+ # Run the built-in migration
35
+ def run_default_migration
36
+ load File.expand_path('../../../../lib/generators/camaraderie/templates/migration.rb', __FILE__)
37
+ AddCamaraderie.new.up
38
+ end
39
+ end
@@ -0,0 +1,33 @@
1
+ module ModelMacros
2
+ # Create a new organization model
3
+ def spawn_organization_model(klass_name = 'Organization', &block)
4
+ spawn_model klass_name, ActiveRecord::Base do
5
+ acts_as_organization
6
+ instance_exec(&block) if block
7
+ end
8
+ end
9
+
10
+ # Create a new user model
11
+ def spawn_user_model(klass_name = 'User', &block)
12
+ spawn_model klass_name, ActiveRecord::Base do
13
+ acts_as_user
14
+ instance_exec(&block) if block
15
+ end
16
+ end
17
+
18
+ def spawn_membership_model(klass_name = 'Membership', &block)
19
+ spawn_model 'Membership', ActiveRecord::Base do
20
+ acts_as_membership
21
+ instance_exec(&block) if block
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ # Create a new model class
28
+ def spawn_model(klass_name, parent_klass, &block)
29
+ Object.instance_eval { remove_const klass_name } if Object.const_defined?(klass_name)
30
+ @spawned_models << Object.const_set(klass_name, Class.new(parent_klass))
31
+ Object.const_get(klass_name).class_eval(&block) if block_given?
32
+ end
33
+ end
@@ -0,0 +1,9 @@
1
+ module RailsMacros
2
+ def relation_class(klass)
3
+ if ActiveRecord::VERSION::MAJOR == 3
4
+ ActiveRecord::Relation
5
+ else
6
+ "ActiveRecord::Relation::ActiveRecord_Relation_#{klass.name}".constantize
7
+ end
8
+ end
9
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: camaraderie
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.3
4
+ version: '0.2'
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-08-10 00:00:00.000000000 Z
12
+ date: 2013-08-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -43,6 +43,38 @@ dependencies:
43
43
  - - ! '>='
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: sqlite3
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: rspec
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '2.14'
70
+ type: :development
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ version: '2.14'
46
78
  - !ruby/object:Gem::Dependency
47
79
  name: activerecord
48
80
  requirement: !ruby/object:Gem::Requirement
@@ -84,11 +116,15 @@ extensions: []
84
116
  extra_rdoc_files: []
85
117
  files:
86
118
  - .gitignore
119
+ - .rspec
120
+ - .travis.yml
87
121
  - Gemfile
88
122
  - LICENSE.md
89
123
  - README.md
90
124
  - Rakefile
91
125
  - camaraderie.gemspec
126
+ - gemfiles/Gemfile.activerecord-3.2.x
127
+ - gemfiles/Gemfile.activerecord-4.0
92
128
  - lib/camaraderie.rb
93
129
  - lib/camaraderie/membership.rb
94
130
  - lib/camaraderie/organization.rb
@@ -99,6 +135,14 @@ files:
99
135
  - lib/generators/camaraderie/install_generator.rb
100
136
  - lib/generators/camaraderie/templates/migration.rb
101
137
  - lib/generators/camaraderie/templates/model.rb
138
+ - spec/camaraderie/membership_spec.rb
139
+ - spec/camaraderie/organization_spec.rb
140
+ - spec/camaraderie/user_spec.rb
141
+ - spec/camaraderie_spec.rb
142
+ - spec/spec_helper.rb
143
+ - spec/support/macros/database_macros.rb
144
+ - spec/support/macros/model_macros.rb
145
+ - spec/support/macros/rails_macros.rb
102
146
  homepage: https://github.com/mirego/camaraderie
103
147
  licenses:
104
148
  - BSD 3-Clause
@@ -114,7 +158,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
114
158
  version: '0'
115
159
  segments:
116
160
  - 0
117
- hash: -1376039277595789238
161
+ hash: -3093721818560586396
118
162
  required_rubygems_version: !ruby/object:Gem::Requirement
119
163
  none: false
120
164
  requirements:
@@ -123,7 +167,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
167
  version: '0'
124
168
  segments:
125
169
  - 0
126
- hash: -1376039277595789238
170
+ hash: -3093721818560586396
127
171
  requirements: []
128
172
  rubyforge_project:
129
173
  rubygems_version: 1.8.23
@@ -131,4 +175,12 @@ signing_key:
131
175
  specification_version: 3
132
176
  summary: Camaraderie takes away the pain of managing membership stuff between users
133
177
  and organizations.
134
- test_files: []
178
+ test_files:
179
+ - spec/camaraderie/membership_spec.rb
180
+ - spec/camaraderie/organization_spec.rb
181
+ - spec/camaraderie/user_spec.rb
182
+ - spec/camaraderie_spec.rb
183
+ - spec/spec_helper.rb
184
+ - spec/support/macros/database_macros.rb
185
+ - spec/support/macros/model_macros.rb
186
+ - spec/support/macros/rails_macros.rb