permissable 0.0.3 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.3
1
+ 0.1.0
@@ -4,94 +4,51 @@ module Permissable
4
4
 
5
5
  def self.included(base)
6
6
  base.send :include, InstanceMethods
7
- member_klass = (base.permissable_by_association) ? base.permissable_by_association.to_s.classify.constantize : base
8
- member_klass.class_eval do
9
- has_many :permissions, :as => :member, :conditions => { :member_type => "#{self.to_s}" }
10
- end
11
- base.send :include, ((base.permissable_by_association) ? AssociatedMethods : MemberMethods)
7
+ base.send :attr_protected, :member_identifier
12
8
  end
13
-
9
+
14
10
  # This module includes methods that should exist on ALL members.
15
11
  module InstanceMethods
16
12
 
17
- # When a member is initialized, its definitions are eager-loaded to cut down on database queries.
18
- # Once loaded all of the permission definitions are cached into the @permission_definitions variable.
19
- attr_accessor :permission_definitions
20
-
21
- # Can this member perform the requested action?
13
+ # The can? method returns a boolen value specifying whether or not this member can perform the specific method on resource
22
14
  def can?(method, resource)
23
- method = method.to_s
24
- permissions.for_resource(resource).with_permission_to(method).exists?
25
- end
26
-
27
- # Set a permission
28
- def can!(methods, resource)
29
- methods = [methods].flatten.uniq
30
-
31
- members.each do |member|
32
- methods.each do |perm|
33
- next if resource.permissions.for_member(member).with_permission_to(perm).exists?
34
- new_permission = Permission.new({ :permission_type => method.to_s })
35
- new_permission.member = m
36
- new_permission.resource = resource
37
- new_permission.save
38
- end
39
- end
40
- end
41
-
42
- def cannot?(methods, resource)
43
- !can?(methods, resource)
44
- end
45
-
46
- def cannot!(methods, resources)
47
- methods = [methods].flatten.uniq
48
- existing = permissions.for_resource(resource).with_permission_to(methods).all.collect{ |perm| perm.id }.uniq
49
- Permission.destroy(existing)
50
- end
51
-
52
- def permissions_for(resource)
53
- permissions.for_resource(resource).all.collect{ |p| p.permission_type.to_sym }.uniq
54
- end
55
-
56
- def permissions_for?(resource)
57
- !permissions_for(resource).empty?
15
+ permissions_for(resource, method).exists?
58
16
  end
59
17
 
60
- private
18
+ # Alias to can? to get the inverse.
19
+ def cannot?(method, resource); !can?(method, resource); end
61
20
 
62
- def get_const(resource)
63
- resource.class.to_s.classify.constantize
64
- end
65
-
66
- end
67
-
68
- # This module gets included on member classes that are permissable directly.
69
- module MemberMethods
70
- alias_attribute :member_id, :id
71
- def members; [self]; end
72
- end
73
-
74
- # This module gets included on member classes that are permissable with an association.
75
- module AssociatedMethods
76
-
77
- attr_reader :permissions
78
- attr_reader :member_id
79
-
80
- def permissions
81
- return @permissions unless @permissions.nil?
82
- @permissions = Permission.where(:member_id => member_id, :member_type => association.to_s)
83
- end
84
21
 
85
- def association
86
- permissable_by_association.to_s.classify.constantize
22
+ # This sets the member information for our permission lookup based on the current resource scope.
23
+ # These attributes correspond to the correct member_id and member_type in our permissions table.
24
+ def member_identifier(scope)
25
+
26
+ @member_identifier ||= {}
27
+ # The scope should be the classname of a resource we are getting identifiers for
28
+ scope = scope.to_s.classify
29
+
30
+ return @member_identifier[scope] unless @member_identifier[scope].nil?
31
+ return { :member_id => self.id, :member_type => self.class.to_s } unless permissable_associations.has_key?(scope)
32
+
33
+ assoc_key = permissable_associations[scope]
34
+ assoc = send "#{assoc_key}".to_sym
35
+
36
+ @member_identifier[scope] = { :member_id => (assoc.is_a?(Array) ? assoc.collect{ |a| a.id } : assoc.id ), :member_type => assoc_key.to_s.classify }
37
+
87
38
  end
39
+
40
+ # Provide an instance method to our associations
41
+ def permissable_associations; self.class.permissable_associations; end
88
42
 
89
- def member_id
90
- @member_id || association.all.collect{ |a| a.attributes['id'] }
91
- end
43
+ private
92
44
 
93
- def members
94
- association.all
45
+ # Looks up permissions for a particular resource.
46
+ def permissions_for(resource, methods = nil)
47
+ scope = resource.class.to_s.classify
48
+ return self.permissions unless permissable_associations.has_key?(scope)
49
+ relation = Permission.where(member_identifier(scope)).for_resource(resource)
50
+ relation = relation.with_permission_to(methods) unless methods.nil?
51
+ relation
95
52
  end
96
53
 
97
54
  end
@@ -3,28 +3,19 @@ class Permission < ActiveRecord::Base
3
3
  belongs_to :resource, :polymorphic => true
4
4
 
5
5
  class << self
6
- def for_member(member)
7
- member = flatten(member)
8
- where(:member_id => member[:ids], :member_type => member[:types])
9
- end
10
6
 
11
7
  def for_resource(resource)
12
8
  resource = flatten(resource)
13
- where(:resource_id => resource[:ids], :resource_type => resource[:types])
14
- end
15
-
16
- def for_member_and_resource(member, resource)
17
- for_member(member).for_resource(resource)
9
+ where(resource)
18
10
  end
19
11
 
20
- def with_permission_to(perm)
21
- perm = perm.is_a?(Array) ? perm.collect{ |p| p.to_s } : perm.to_s
22
- where(:permission_type => perm)
12
+ def with_permission_to(methods)
13
+ where(:permission_type => [methods].flatten.uniq.collect{ |m| m.to_s.downcase })
23
14
  end
24
15
 
25
16
  def flatten(obj)
26
- return { :ids => obj, :types => obj.class.to_s } unless obj.is_a?(Array)
27
- { :ids => obj.collect{ |o| o.id }, :types => obj.collect{ |o| o.class.to_s } }
17
+ return { :resource_id => obj, :resource_type => obj.class.to_s } unless obj.is_a?(Array)
18
+ { :resource_id => obj.collect{ |o| o.id }, :resource_type => obj.collect{ |o| o.class.to_s } }
28
19
  end
29
20
 
30
21
  end
@@ -3,19 +3,8 @@ module Permissable
3
3
  module Resource
4
4
 
5
5
  def self.included(base)
6
- base.class_eval{ has_many(:permissions, :as => :resource, :conditions => { :resource_type => "#{self.to_s}" }) }
7
- base.extend(ClassMethods)
8
6
  end
9
-
10
- module ClassMethods
11
- private
12
- def set_permissions!(permission_list)
13
- class_inheritable_accessor :permissable_methods
14
- write_inheritable_attribute(:permissable_methods, []) if permissable_methods.nil?
15
- permissable_methods.concat permission_list.uniq.flatten
16
- end
17
- end
18
-
7
+
19
8
  end
20
9
 
21
10
  end
data/lib/permissable.rb CHANGED
@@ -3,13 +3,10 @@
3
3
  # License:: MIT License (http://www.opensource.org/licenses/mit-license.php)
4
4
  #
5
5
  # Permissable creates the ability to add a permissions system to "resources" based on a "member".
6
- # It allows you to define a member using the +permissable+ method:
6
+ # It allows you to define a member using the +has_permissions_for+ method:
7
7
  #
8
8
  # class User < ActiveRecord::Base
9
- # permissable do |configure|
10
- # configure.permission_for :read, :posts, :categories
11
- # configure.permission_for [:read, :write, :moderate], :comments
12
- # end
9
+ # has_permissions_for [:section, :category, :entry], :to => [:read, :write, :moderate]
13
10
  # end
14
11
  #
15
12
  # Permissable uses a single "permissions" table (and subsuquently a Permission model), with a polymorphic relationship
@@ -32,7 +29,7 @@ module Permissable
32
29
  class PermissableError < StandardError
33
30
  end
34
31
 
35
- class PermissionNotDefined < PermissableError
32
+ class PermissionNotDefined < PermissableError
36
33
  end
37
34
 
38
35
  class ResourceNotPermissable < PermissableError
@@ -40,45 +37,65 @@ module Permissable
40
37
 
41
38
  module ClassMethods
42
39
 
43
- # +permissable+ gives the class its called on the ability to effect various permission states on the resources
44
- # it specifies. It accepts an options hash and a block. The following options are supported:
40
+ # +has_permissions_for+ gives the class its called on the ability to effect various permission states on the resources
41
+ # it specifies. It requires two parameters, a resource or array of resources to add permissions to, and an options
42
+ # hash with instructions of how to handle the permissions.
43
+ # The options hash consists of the following keys, but only the :to key is required.
45
44
  #
46
- # * with: This is an association of the primary member class. This allows you to use an association to determine
47
- # which permissions are available. One example of this would be to assign permission to Roles of a User class.
48
- # In this case, when doing lookups, it will use the permissions assigned to each Role the user belongs to.
49
- # The with option accepts the association in the same manner as you would define it in active record.
50
- # ie: has_many roles would be: :with => :roles. belongs_to role would be :with => :role
51
- def permissable(options = {})
45
+ # * +to+: This is a permission or array of permissions allowed. You can use any number of values
46
+ # and those values can be whatever you want. You must include at least one.
47
+ #
48
+ # * +through+: Allows you to scope permissions through one of the member's associations. For example, if your member is
49
+ # a User, and that user has_many Roles, you could set permissions :through => :roles. When looking up permissions
50
+ # Permissable will use the association instead of the main member object. This also applies to setting permissions.
51
+ # NOTE: The :through option uses the same value as your assocation. If the association is a has_many, the value will be
52
+ # plural. Likewise belongs_to or has_one associations will be singular. The best way to think of this is that the
53
+ # :through attribute would be exactly the same as the method you would call on your model to find its associations.
54
+ #
55
+ def has_permissions_for(resources, options = {})
56
+ raise Permissable::PermissionNotDefined, "has_permissions_for missing the :to option." unless options.has_key?(:to) and !options[:to].empty?
52
57
 
53
- write_inheritable_attribute :permissable_by_association, (options[:with] || false)
54
- class_inheritable_reader :permissable_by_association
58
+ write_inheritable_attribute(:permissable_associations, {}) if permissable_associations.nil?
59
+ resources = [resources].flatten
55
60
 
56
- write_inheritable_attribute :has_permissions_for, []
57
- class_inheritable_accessor :has_permissions_for
58
-
59
- # The class that called permissable becomes a member.
61
+ resources.each do |resource|
62
+ resource = resource.to_s.classify
63
+
64
+ # If there is an association on these resources, add those to our associations attribute
65
+ # so our classes and sub classes will know about it.
66
+ if options.has_key?(:through)
67
+
68
+ permissable_associations[resource] = options[:through]
69
+ assoc = options[:through].to_s.classify.constantize
70
+
71
+ # Our association also creates a has_many association on our permissions table.
72
+ assoc.class_eval do
73
+ has_many(:permissions, :as => :member, :conditions => { :member_type => "#{self.to_s}" }) unless respond_to? :permissions
74
+ end
75
+
76
+ end
77
+
78
+ # Setup a has_many association of permissions on our resource.
79
+ resource.constantize.class_eval do
80
+ include Permissable::Resource
81
+ has_many(:permissions, :as => :resource, :conditions => { :resource_type => "#{self.to_s}" }) unless respond_to? :permissions
82
+ end
83
+
84
+ end
85
+
86
+ # This class becomes a member to resources.
60
87
  include Permissable::Member
61
88
 
62
- # Yield the permissions block to add permissions.
63
- yield self
89
+ # Members create a has_many association on permissions as a member.
90
+ has_many(:permissions, :as => :member, :conditions => { :member_type => "#{self.to_s}" }) unless respond_to? :permissions
64
91
 
65
- end
92
+ end
66
93
 
67
- # +permission_for+ is called via the permissable block
68
- def permission_for(methods, *resources)
69
-
70
- methods = [methods].flatten
71
-
72
- # Add each resource to the member class' permissions list.
73
- has_permissions_for.concat(resources).flatten.uniq
74
-
75
- resources.uniq.each do |resource|
76
- #Include the Resource module to each resource class.
77
- resource.to_s.classify.constantize.class_eval{ include Permissable::Resource }
78
- resource.to_s.classify.constantize.send :set_permissions!, methods
79
- end
80
-
81
- end
94
+ # Each time has_permissions_for is called, different associations may exist.
95
+ # This provides a way to store and update them all as necessary.
96
+ def permissable_associations
97
+ read_inheritable_attribute(:permissable_associations)
98
+ end
82
99
 
83
100
  end
84
101
  end
Binary file
data/permissable.gemspec CHANGED
@@ -5,11 +5,11 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{permissable}
8
- s.version = "0.0.3"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Brent Kirby"]
12
- s.date = %q{2010-07-25}
12
+ s.date = %q{2010-07-26}
13
13
  s.description = %q{Permissable lets you set a number of 'permissions' to your database models. By assigning 'members' to 'resources' (models) you can specify various permission states.}
14
14
  s.email = %q{brent@kurbmedia.com}
15
15
  s.extra_rdoc_files = [
@@ -28,6 +28,7 @@ Gem::Specification.new do |s|
28
28
  "lib/permissable/member.rb",
29
29
  "lib/permissable/permission.rb",
30
30
  "lib/permissable/resource.rb",
31
+ "permissable-0.0.3.gem",
31
32
  "permissable.gemspec",
32
33
  "test/helper.rb",
33
34
  "test/test_permissable.rb"
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: permissable
3
3
  version: !ruby/object:Gem::Version
4
- hash: 25
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
+ - 1
8
9
  - 0
9
- - 3
10
- version: 0.0.3
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Brent Kirby
@@ -15,7 +15,7 @@ autorequire:
15
15
  bindir: bin
16
16
  cert_chain: []
17
17
 
18
- date: 2010-07-25 00:00:00 -04:00
18
+ date: 2010-07-26 00:00:00 -04:00
19
19
  default_executable:
20
20
  dependencies:
21
21
  - !ruby/object:Gem::Dependency
@@ -53,6 +53,7 @@ files:
53
53
  - lib/permissable/member.rb
54
54
  - lib/permissable/permission.rb
55
55
  - lib/permissable/resource.rb
56
+ - permissable-0.0.3.gem
56
57
  - permissable.gemspec
57
58
  - test/helper.rb
58
59
  - test/test_permissable.rb