diff --git a/.gitignore b/.gitignore
index e617d15..78c1684 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,3 +5,6 @@ doc/api
doc/app
public/files
public/thumbs
+config/deploy.rb
+config/deploy
+Capfile
diff --git a/app/models/permission.rb b/app/models/permission.rb
new file mode 100644
index 0000000..c2951ff
--- /dev/null
+++ b/app/models/permission.rb
@@ -0,0 +1,19 @@
+class Permission < ActiveRecord::Base
+ # uncomment any of the following lines which is relevant to your application,
+ # or create your own with the name of the model which acts_as_permissible.
+ belongs_to :user
+
+ belongs_to :role
+
+ belongs_to :permissible, :polymorphic => true, :dependent => :destroy
+
+ validates_presence_of :permissible_id, :permissible_type, :action
+ validates_format_of :action, :with => /^[a-z_]+$/
+ validates_numericality_of :permissible_id
+ validates_uniqueness_of :action, :scope => [:permissible_id,:permissible_type]
+
+ def to_hash
+ self.new_record? ? {} : {self.action => self.granted}
+ end
+
+end
\ No newline at end of file
diff --git a/app/models/role.rb b/app/models/role.rb
new file mode 100644
index 0000000..d5c7310
--- /dev/null
+++ b/app/models/role.rb
@@ -0,0 +1,12 @@
+class Role < ActiveRecord::Base
+ has_many :role_memberships, :as => :roleable, :dependent => :destroy
+ has_many :roles, :through => :role_memberships, :source => :role
+
+ has_many :roleables, :class_name => "RoleMembership", :foreign_key => "role_id", :dependent => :destroy
+ has_many :subroles, :through => :roleables, :source => :roleable, :source_type => 'Role'
+ has_many :users, :through => :roleables, :source => :roleable, :source_type => 'User'
+
+ validates_uniqueness_of :name
+
+ acts_as_permissible
+end
\ No newline at end of file
diff --git a/app/models/role_membership.rb b/app/models/role_membership.rb
new file mode 100644
index 0000000..9ebcbe2
--- /dev/null
+++ b/app/models/role_membership.rb
@@ -0,0 +1,36 @@
+class RoleMembership < ActiveRecord::Base
+ belongs_to :user
+ belongs_to :role
+ belongs_to :roleable, :polymorphic => true
+
+ validates_presence_of :roleable_id, :roleable_type, :role_id
+ validates_uniqueness_of :role_id, :scope => [:roleable_id, :roleable_type]
+ validates_numericality_of :roleable_id, :role_id
+ validates_format_of :roleable_type, :with => /^[A-Z]{1}[a-z0-9]+([A-Z]{1}[a-z0-9]+)*$/
+ validate :role_does_not_belong_to_itself_in_a_loop
+
+ protected
+ def role_does_not_belong_to_itself_in_a_loop
+ if roleable_type == "Role"
+ if role_id == roleable_id
+ errors.add_to_base("A role cannot belong to itself.")
+ else
+ if belongs_to_itself_through_other?(roleable_id, role_id)
+ errors.add_to_base("A role cannot belong to a role which belongs to it.")
+ end
+ end
+ end
+ end
+
+ def belongs_to_itself_through_other?(original_roleable_id, current_role_id)
+ if self.class.find(:first, :select => "id", :conditions => ["roleable_id=? AND roleable_type='Role' AND role_id=?",current_role_id,original_roleable_id])
+ return true
+ else
+ memberships = self.class.find(:all, :select => "role_id", :conditions => ["roleable_id=? AND roleable_type='Role'",current_role_id])
+ if memberships.any? {|membership| belongs_to_itself_through_other?(original_roleable_id,membership.role_id)}
+ return true
+ end
+ end
+ return false
+ end
+end
\ No newline at end of file
diff --git a/app/models/user.rb b/app/models/user.rb
index 04c7d17..d1d3345 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -1,3 +1,4 @@
class User < ActiveRecord::Base
acts_as_authentic
+ acts_as_permissible
end
diff --git a/app/views/albums/show.html.erb b/app/views/albums/show.html.erb
index a32a88b..53e85df 100644
--- a/app/views/albums/show.html.erb
+++ b/app/views/albums/show.html.erb
@@ -1,6 +1,11 @@
+
<%= @album.title %>
<% for photo in @album.photos %>
<%= link_to image_tag( photo.path_modified_public("album") ), photo %>
<% end %>
+<%= @album.description %>
+<% if current_user && current_user.has_permission?("see_album_note") %>
+<%= @album.note %>
+<% end %>
<%= link_to "Update album", edit_album_path(@album) %>
<%= link_to "Upload photos", upload_album_path(@album) %>
diff --git a/config/environment.rb b/config/environment.rb
index 6426342..f0db2a5 100644
--- a/config/environment.rb
+++ b/config/environment.rb
@@ -11,7 +11,8 @@ Rails::Initializer.run do |config|
config.gem "authlogic"
config.gem 'mime-types', :lib => 'mime/types'
- #config.gem "image_science"
+ config.gem "image_science"
+ config.gem "mini_exiftool"
config.load_paths += %W( #{RAILS_ROOT}/app/middleware )
@@ -19,12 +20,4 @@ Rails::Initializer.run do |config|
config.i18n.default_locale = 'no-NB'
- config.action_mailer.smtp_settings = {
- :address => "smtp.gmail.com",
- :port => 587,
- :domain => "espen@inspired.no",
- :authentication => :plain,
- :user_name => "espen@inspired.no",
- :password => "tkg5megmeg"
- }
end
\ No newline at end of file
diff --git a/db/migrate/20090604202928_create_permissions.rb b/db/migrate/20090604202928_create_permissions.rb
new file mode 100644
index 0000000..9cdbb7b
--- /dev/null
+++ b/db/migrate/20090604202928_create_permissions.rb
@@ -0,0 +1,16 @@
+class CreatePermissions < ActiveRecord::Migration
+ def self.up
+ create_table "permissions", :force => true do |t|
+ t.integer :permissible_id
+ t.string :permissible_type
+ t.string :action
+ t.boolean :granted
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table "permissions"
+ end
+end
\ No newline at end of file
diff --git a/db/migrate/20090604202929_create_role_memberships.rb b/db/migrate/20090604202929_create_role_memberships.rb
new file mode 100644
index 0000000..dc3d053
--- /dev/null
+++ b/db/migrate/20090604202929_create_role_memberships.rb
@@ -0,0 +1,15 @@
+class CreateRoleMemberships < ActiveRecord::Migration
+ def self.up
+ create_table :role_memberships do |t|
+ t.integer :roleable_id
+ t.string :roleable_type
+ t.integer :role_id
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :role_memberships
+ end
+end
\ No newline at end of file
diff --git a/db/migrate/20090604202930_create_roles.rb b/db/migrate/20090604202930_create_roles.rb
new file mode 100644
index 0000000..8b34d74
--- /dev/null
+++ b/db/migrate/20090604202930_create_roles.rb
@@ -0,0 +1,13 @@
+class CreateRoles < ActiveRecord::Migration
+ def self.up
+ create_table :roles do |t|
+ t.string :name
+
+ t.timestamps
+ end
+ end
+
+ def self.down
+ drop_table :roles
+ end
+end
\ No newline at end of file
diff --git a/db/schema.rb b/db/schema.rb
index 026bffb..dba0b1f 100644
--- a/db/schema.rb
+++ b/db/schema.rb
@@ -9,7 +9,7 @@
#
# It's strongly recommended to check this file into your version control system.
-ActiveRecord::Schema.define(:version => 20090602131547) do
+ActiveRecord::Schema.define(:version => 20090604202930) do
create_table "albums", :force => true do |t|
t.string "title", :null => false
@@ -37,6 +37,15 @@ ActiveRecord::Schema.define(:version => 20090602131547) do
t.datetime "updated_at"
end
+ create_table "permissions", :force => true do |t|
+ t.integer "permissible_id"
+ t.string "permissible_type"
+ t.string "action"
+ t.boolean "granted"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
create_table "photo_tags", :force => true do |t|
t.integer "tag_id"
t.integer "photo_id"
@@ -55,6 +64,20 @@ ActiveRecord::Schema.define(:version => 20090602131547) do
t.float "latitude"
end
+ create_table "role_memberships", :force => true do |t|
+ t.integer "roleable_id"
+ t.string "roleable_type"
+ t.integer "role_id"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
+ create_table "roles", :force => true do |t|
+ t.string "name"
+ t.datetime "created_at"
+ t.datetime "updated_at"
+ end
+
create_table "tags", :force => true do |t|
t.string "title", :null => false
t.datetime "created_at"
diff --git a/lib/acts_as_permissible.rb b/lib/acts_as_permissible.rb
new file mode 100644
index 0000000..994b310
--- /dev/null
+++ b/lib/acts_as_permissible.rb
@@ -0,0 +1,106 @@
+# ActsAsPermissible
+module NoamBenAri
+ module Acts #:nodoc:
+ module Permissible #:nodoc:
+
+ def self.included(base)
+ base.extend ClassMethods
+ end
+
+ module ClassMethods
+ def acts_as_permissible
+ has_many :permissions, :as => :permissible, :dependent => :destroy
+
+ has_many :role_memberships, :as => :roleable, :dependent => :destroy
+ has_many :roles, :through => :role_memberships, :source => :role
+
+ include NoamBenAri::Acts::Permissible::InstanceMethods
+ extend NoamBenAri::Acts::Permissible::SingletonMethods
+
+ alias_method :full_permissions_hash, :permissions_hash
+ end
+ end
+
+ # This module contains class methods
+ module SingletonMethods
+
+ # Helper method to lookup for permissions for a given object.
+ # This method is equivalent to obj.permissions.
+ def find_permissions_for(obj)
+ permissible = ActiveRecord::Base.send(:class_name_of_active_record_descendant, self).to_s
+
+ Permission.find(:all,
+ :conditions => ["permissible_id = ? and permissible_type = ?", obj.id, permissible]
+ )
+ end
+ end
+
+ # This module contains instance methods
+ module InstanceMethods
+
+ # returns permissions in hash form
+ # from all levels recursively
+ def permissions_hash
+ @permissions_hash ||= lambda do
+ @permissions_hash = permissions.inject({}) { |hsh,perm| hsh.merge(perm.to_hash) }.symbolize_keys!
+ roles.each do |role|
+ merge_permissions!(role.permissions_hash)
+ end
+ @permissions_hash
+ end.call()
+ end
+
+ # accepts a permission identifier string or an array of permission identifier strings
+ # and return true if the user has all of the permissions given by the parameters
+ # false if not.
+ def has_permission?(*perms)
+ perms.all? {|perm| permissions_hash.include?(perm.to_sym) && (permissions_hash[perm.to_sym] == true) }
+ end
+
+ # accepts a permission identifier string or an array of permission identifier strings
+ # and return true if the user has any of the permissions given by the parameters
+ # false if none.
+ def has_any_permission?(*perms)
+ perms.any? {|perm| permissions_hash.include?(perm.to_sym) && (permissions_hash[perm.to_sym] == true) }
+ end
+
+ # Merges another permissible object's permissions into this permissible's permissions hash
+ # In the case of identical keys, a false value wins over a true value.
+ def merge_permissions!(other_permissions_hash)
+ permissions_hash.merge!(other_permissions_hash) {|key,oldval,newval| oldval.nil? ? newval : oldval && newval}
+ end
+
+ # Resets permissions and then loads them.
+ def reload_permissions!
+ reset_permissions!
+ permissions_hash
+ end
+
+ def roles_list
+ list = []
+ roles.inject(list) do |list,role|
+ list << role.name
+ role.roles_list.inject(list) {|list,role| list << role}
+ end
+ list.uniq
+ end
+
+ def in_role?(*role_names)
+ role_names.all? {|role| roles_list.include?(role) }
+ end
+
+ def in_any_role?(*role_names)
+ role_names.any? {|role| roles_list.include?(role) }
+ end
+
+
+ private
+ # Nilifies permissions_hash instance variable.
+ def reset_permissions!
+ @permissions_hash = nil
+ end
+
+ end
+ end
+ end
+end
diff --git a/spec/fixtures/permissions.yml b/spec/fixtures/permissions.yml
new file mode 100644
index 0000000..f6f4025
--- /dev/null
+++ b/spec/fixtures/permissions.yml
@@ -0,0 +1,48 @@
+one:
+ id: 1
+ permissible_id: 7
+ permissible_type: "Permission"
+ action: "view_something"
+ granted: 1
+two:
+ id: 2
+ permissible_id: 7
+ permissible_type: "Permission"
+ action: "delete_something"
+ granted: 0
+three:
+ id: 3
+ permissible_id: 8
+ permissible_type: "Permission"
+ action: "view_something"
+ granted: 1
+four:
+ id: 4
+ permissible_id: 8
+ permissible_type: "Permission"
+ action: "delete_something"
+ granted: 1
+five:
+ id: 5
+ permissible_id: 8
+ permissible_type: "Permission"
+ action: "update_something"
+ granted: 1
+six:
+ id: 6
+ permissible_id: 8
+ permissible_type: "Permission"
+ action: "create_something"
+ granted: 0
+perm:
+ id: 7
+ permissible_id: 47
+ permissible_type: "User"
+ action: "non_important"
+ granted: 1
+perm2:
+ id: 8
+ permissible_id: 48
+ permissible_type: "User"
+ action: "non_important"
+ granted: 1
\ No newline at end of file
diff --git a/spec/fixtures/role_memberships.yml b/spec/fixtures/role_memberships.yml
new file mode 100644
index 0000000..73a00d8
--- /dev/null
+++ b/spec/fixtures/role_memberships.yml
@@ -0,0 +1,25 @@
+publishers_to_customers:
+ id: 1
+ roleable_id: 1
+ roleable_type: "Role"
+ role_id: 3
+advertisers_to_customers:
+ id: 2
+ roleable_id: 2
+ roleable_type: "Role"
+ role_id: 3
+admins_to_company:
+ id: 3
+ roleable_id: 5
+ roleable_type: "Role"
+ role_id: 4
+company_to_admins:
+ id: 4
+ roleable_id: 4
+ roleable_type: "Role"
+ role_id: 5
+publishers_to_company:
+ id: 5
+ roleable_id: 1
+ roleable_type: "Role"
+ role_id: 4
\ No newline at end of file
diff --git a/spec/fixtures/roles.yml b/spec/fixtures/roles.yml
new file mode 100644
index 0000000..36843a3
--- /dev/null
+++ b/spec/fixtures/roles.yml
@@ -0,0 +1,15 @@
+publishers:
+ id: 1
+ name: "Publishers"
+advertisers:
+ id: 2
+ name: "Advertisers"
+customers:
+ id: 3
+ name: "Customers"
+company:
+ id: 4
+ name: "Company"
+admins:
+ id: 5
+ name: "Admins"
\ No newline at end of file
diff --git a/spec/models/acts_as_permissible_spec.rb b/spec/models/acts_as_permissible_spec.rb
new file mode 100644
index 0000000..8e9b955
--- /dev/null
+++ b/spec/models/acts_as_permissible_spec.rb
@@ -0,0 +1,208 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+class Permission < ActiveRecord::Base
+ acts_as_permissible
+end
+
+describe "acts_as_permissible" do
+ fixtures :permissions
+
+ before(:each) do
+ @perm = permissions(:perm)
+ end
+
+ describe "class methods" do
+ it "should find_permissions_for(obj) correctly" do
+ Permission.find_permissions_for(@perm).size.should == 2
+ Permission.find_permissions_for(@perm).first.action.should == "view_something"
+ Permission.find_permissions_for(@perm).last.action.should == "delete_something"
+ end
+ end
+
+ describe "permissions_hash" do
+ it "should return the correct permissions_hash" do
+ @perm.permissions_hash.should == {:view_something => true, :delete_something => false}
+ end
+ end
+
+ describe "has_permission?" do
+ it "should return true if permission found" do
+ @perm.has_permission?("view_something").should == true
+ end
+
+ it "should return false if permission not found" do
+ @perm.has_permission?("create_something").should == false
+ end
+
+ it "should return false if permission found and is denied" do
+ @perm.has_permission?("delete_something").should == false
+ end
+ end
+
+ describe "merge_permissions!" do
+ before(:each) do
+ @perm2 = permissions(:perm2)
+ @merged_permissions = @perm.merge_permissions!(@perm2.permissions_hash)
+ # {:update_something=>true, :view_something=>true, :delete_something=>false, :create_something=>false}
+ end
+
+ it "should include all keys from both hashes" do
+ @merged_permissions.keys.should ==
+ (@perm.permissions_hash.keys + @perm2.permissions_hash.keys).uniq
+ end
+
+ it "should override identical keys with false value" do
+ @merged_permissions[:delete_something].should == false
+ end
+ end
+
+ describe "reload_permissions!" do
+ before(:each) do
+ @original_hash = @perm.permissions_hash
+ @perm.permissions << Permission.new(:action => "add_something", :granted => true)
+ end
+
+ it "should catch-up with database changes" do
+ @perm.permissions_hash.should == @original_hash
+ reloaded_hash = @perm.reload_permissions!
+ reloaded_hash.should_not == @original_hash
+ end
+
+ it "should get the changes correctly" do
+ reloaded_hash = @perm.reload_permissions!
+ reloaded_hash.keys.should include(:add_something)
+ end
+ end
+
+ describe "roles_list" do
+ before(:each) do
+ @perm.roles_list.should == []
+ @mutables = Role.new(:name => "mutables")
+ @mutables.save!
+ @wierdos = Role.new(:name => "wierdos")
+ @wierdos.save!
+ @mutables.roles << @wierdos
+ end
+
+ after(:each) do
+ @mutables.destroy
+ @wierdos.destroy
+ @perm.roles.reset
+ @perm.roles_list.should == []
+ end
+
+ it "should return the correct list" do
+ @perm.roles << @wierdos
+ @perm.roles_list.size.should == 1
+ @perm.roles_list.should include("wierdos")
+ end
+
+ it "should return the correct list including parent roles of roles recursively." do
+ @perm.roles << @mutables
+ @perm.roles_list.size.should == 2
+ @perm.roles_list.should include("mutables")
+ @perm.roles_list.should include("wierdos")
+ end
+ end
+
+ describe "in_role?" do
+ before(:each) do
+ @mutables = Role.new(:name => "mutables")
+ @mutables.save!
+ @immutables = Role.new(:name => "immutables")
+ @immutables.save!
+ end
+
+ after(:each) do
+ @mutables.destroy
+ @immutables.destroy
+ @perm.roles.reset
+ end
+
+ it "should return true if member of one" do
+ @perm.roles << @mutables
+ @perm.in_role?("mutables").should == true
+ end
+
+ it "should return false if not a member" do
+ @perm.in_role?("mutables").should == false
+ end
+
+ it "should return true if member of all" do
+ @perm.roles << @mutables
+ @perm.roles << @immutables
+ @perm.in_role?("mutables","immutables").should == true
+ end
+
+ it "should return false if member of some" do
+ @perm.roles << @mutables
+ @perm.in_role?("mutables","immutables").should == false
+ end
+ end
+
+ describe "in_any_role?" do
+ before(:each) do
+ @mutables = Role.new(:name => "mutables")
+ @mutables.save!
+ @immutables = Role.new(:name => "immutables")
+ @immutables.save!
+ end
+
+ it "should return true if member of one" do
+ @perm.roles << @mutables
+ @perm.in_any_role?("mutables","immutables").should == true
+ end
+
+ it "should return false if not a member" do
+ @perm.in_any_role?("mutables","immutables").should == false
+ end
+
+ it "should return true if member of all" do
+ @perm.roles << @mutables
+ @perm.roles << @immutables
+ @perm.in_any_role?("mutables","immutables").should == true
+ end
+ end
+
+ describe "full_permissions_hash" do
+ before(:each) do
+ @mutables = Role.new(:name => "mutables")
+ @mutables.save!
+ @mutable_permission = Permission.new(:permissible_id => @mutables.id, :permissible_type => @mutables.class.to_s, :action => "view_something", :granted => false)
+ @mutable_permission.save!
+ @immutables = Role.new(:name => "immutables")
+ @immutables.save!
+ @immutable_permission = Permission.new(:permissible_id => @immutables.id, :permissible_type => @immutables.class.to_s, :action => "download_something", :granted => true)
+ @immutable_permission.save!
+ end
+
+ it "should return the correct hash if object doesn't belong to roles" do
+ @perm.roles.should == []
+ @perm.full_permissions_hash.should == {:view_something=>true, :delete_something=>false}
+ end
+
+ it "should return the correct hash if object belongs to one role" do
+ @perm.roles << @mutables
+ @perm.full_permissions_hash.should == {:view_something=>false, :delete_something=>false}
+ end
+
+ it "should return the correct hash if object belongs to one role which belongs to another role" do
+ @mutables.roles << @immutables
+ @perm.roles << @mutables
+ @perm.full_permissions_hash.should == {:view_something=>false, :delete_something=>false, :download_something=>true}
+ end
+
+ it "should return the correct hash if object belongs to 2 roles" do
+ @perm.roles << @immutables
+ @perm.roles << @mutables
+ @perm.full_permissions_hash.should == {:view_something=>false, :delete_something=>false, :download_something=>true}
+ end
+
+ after(:each) do
+ @mutables.destroy
+ @immutables.destroy
+ @perm.roles.reset
+ end
+ end
+
+end
diff --git a/spec/models/permission_spec.rb b/spec/models/permission_spec.rb
new file mode 100644
index 0000000..9a3e4d9
--- /dev/null
+++ b/spec/models/permission_spec.rb
@@ -0,0 +1,51 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe Permission, "to_hash" do
+ before(:each) do
+ @permission = Permission.new(:permissible_id => 1, :permissible_type => "User", :action => "some_action", :granted => 1)
+ end
+
+ it "to_hash returns {} if new record" do
+ @permission.to_hash.should == {}
+ end
+
+ it "to_hash returns {action => granted}" do
+ @permission.save
+ @permission.to_hash.should == {"some_action" => true}
+ end
+
+end
+
+describe Permission, "validations" do
+ before(:each) do
+ @permission = Permission.new(:permissible_id => 1, :permissible_type => "User", :action => "some_action", :granted => 1)
+ end
+
+ it "should be valid" do
+ @permission.should be_valid
+ end
+
+ it "action should be unique to a permissible id and type" do
+ @permission.save
+ @permission2 = Permission.new(:permissible_id => 1, :permissible_type => "User", :action => "some_action", :granted => 0)
+ @permission2.should_not be_valid
+ end
+
+ it "must have a permissible_id" do
+ @permission.permissible_id = nil
+ @permission.should_not be_valid
+ end
+
+ it "must have a permissible_type" do
+ @permission.permissible_type = nil
+ @permission.should_not be_valid
+ end
+
+ it "must have an action" do
+ @permission.action = nil
+ @permission.should_not be_valid
+ @permission.action = ""
+ @permission.should_not be_valid
+ end
+
+end
diff --git a/spec/models/role_membership_spec.rb b/spec/models/role_membership_spec.rb
new file mode 100644
index 0000000..d6de6d9
--- /dev/null
+++ b/spec/models/role_membership_spec.rb
@@ -0,0 +1,100 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe "RoleMembership" do
+
+ describe "validations" do
+ before(:all) do
+ @roles = []
+ @roles[0] = Role.new(:name => "role0")
+ @roles[1] = Role.new(:name => "role1")
+ @roles[2] = Role.new(:name => "role2")
+ @roles[3] = Role.new(:name => "role3")
+ @roles[4] = Role.new(:name => "role4")
+ @roles[5] = Role.new(:name => "role5")
+ @roles[6] = Role.new(:name => "role6")
+ @roles[7] = Role.new(:name => "role7")
+ @roles[8] = Role.new(:name => "role8")
+ @roles[9] = Role.new(:name => "role9")
+ @roles[10] = Role.new(:name => "role10")
+ @roles[11] = Role.new(:name => "role11")
+ @roles.each {|role| role.save!}
+ end
+
+ before(:each) do
+ @membership = RoleMembership.new(:roleable_id => @roles[0].id, :roleable_type => "Role", :role_id => @roles[1].id)
+ end
+
+ it "should be valid" do
+ @membership.should be_valid
+ end
+
+ # roleable_id
+ it "should have a roleable_id" do
+ @membership.roleable_id = nil
+ @membership.should_not be_valid
+ end
+
+ it "roleable_id should be an integer" do
+ @membership.roleable_id = "asd"
+ @membership.should_not be_valid
+ end
+
+ # roleable_type
+ it "should have a roleable_type" do
+ @membership.roleable_type = nil
+ @membership.should_not be_valid
+ end
+
+ it "roleable_type should be a string" do
+ @membership.roleable_type = 123
+ @membership.should_not be_valid
+ end
+
+ it "roleable_type should have a class name format" do
+ @membership.roleable_type = "asd"
+ @membership.should_not be_valid
+ @membership.roleable_type = "User"
+ @membership.should be_valid
+ @membership.roleable_type = "Some95WierdClassN4m3"
+ @membership.should be_valid
+ end
+
+ # role_id
+ it "should have a role_id" do
+ @membership.role_id = nil
+ @membership.should_not be_valid
+ end
+
+ it "role_id should be an integer" do
+ @membership.role_id = "asd"
+ @membership.should_not be_valid
+ end
+
+ it "should not allow a role to belong to itself" do
+ @membership.role_id = @roles[0].id
+ @membership.should_not be_valid
+ end
+
+ # roles cannot belong to each other in a loop
+ it "should not a allow a role to belong to a role which belongs to it in a loop" do
+ @roles[0].roles << @roles[1]
+ @roles[1].roles << @roles[2]
+ @roles[2].roles << @roles[3]
+ @roles[2].roles << @roles[4]
+ @roles[2].roles << @roles[5]
+ @roles[3].roles << @roles[6]
+ @roles[1].roles << @roles[7]
+ @roles[3].roles << @roles[8]
+ @roles[4].roles << @roles[9]
+ @roles[4].roles << @roles[10]
+ @roles[5].roles << @roles[11]
+ @membership3 = RoleMembership.new(:roleable_id => @roles[11].id, :roleable_type => "Role", :role_id => @roles[0].id)
+ @membership3.should_not be_valid
+ @membership3.errors.full_messages.should include("A role cannot belong to a role which belongs to it.")
+ end
+
+ after(:all) do
+ @roles.each {|role| role.destroy}
+ end
+ end
+end
\ No newline at end of file
diff --git a/spec/models/role_spec.rb b/spec/models/role_spec.rb
new file mode 100644
index 0000000..c9a79e9
--- /dev/null
+++ b/spec/models/role_spec.rb
@@ -0,0 +1,53 @@
+require File.dirname(__FILE__) + '/../spec_helper'
+
+describe "Role" do
+
+ describe "validations" do
+ before(:each) do
+ @role = Role.new(:name => "Hunters")
+ end
+
+ it "should be valid" do
+ @role.should be_valid
+ end
+
+ it "should have a unique name" do
+ @role.save
+ @role2 = Role.new(:name => "Hunters")
+ @role2.should_not be_valid
+ end
+ end
+
+ describe "associations" do
+ fixtures :roles, :role_memberships
+
+ it "should get subgroups correctly" do
+ roles(:company).subroles.size.should == 2
+ arr = []
+ arr << roles(:publishers)
+ arr << roles(:admins)
+ roles(:company).subroles.should include(arr.first)
+ roles(:company).subroles.should include(arr.last)
+
+ roles(:customers).subroles.size.should == 2
+ arr = []
+ arr << roles(:publishers)
+ arr << roles(:advertisers)
+ roles(:customers).subroles.should include(arr.first)
+ roles(:customers).subroles.should include(arr.last)
+ end
+
+ it "should get roles correctly" do
+ roles(:publishers).roles.size.should == 2
+ arr = []
+ arr << roles(:customers)
+ arr << roles(:company)
+ roles(:publishers).roles.should == arr
+
+ roles(:admins).roles.size.should == 1
+ arr = []
+ arr << roles(:company)
+ roles(:admins).roles.should == arr
+ end
+ end
+end
\ No newline at end of file