permissable 0.0.3 → 0.1.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.
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