##// END OF EJS Templates
Perf: use a custom decoder for Role#permissions instead of YAML.load....
Jean-Philippe Lang -
r9733:a1f17b982c72
parent child
Show More
@@ -1,185 +1,194
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 class Role < ActiveRecord::Base
19 # Custom coder for the permissions attribute that should be an
20 # array of symbols. Rails 3 uses Psych which can be *unbelievably*
21 # slow on some platforms (eg. mingw32).
22 class PermissionsAttributeCoder
23 def self.load(str)
24 str.to_s.scan(/:([a-z0-9_]+)/).flatten.map(&:to_sym)
25 end
26
27 def self.dump(value)
28 YAML.dump(value)
29 end
30 end
31
19 32 # Built-in roles
20 33 BUILTIN_NON_MEMBER = 1
21 34 BUILTIN_ANONYMOUS = 2
22 35
23 36 ISSUES_VISIBILITY_OPTIONS = [
24 37 ['all', :label_issues_visibility_all],
25 38 ['default', :label_issues_visibility_public],
26 39 ['own', :label_issues_visibility_own]
27 40 ]
28 41
29 42 scope :sorted, order("#{table_name}.builtin ASC, #{table_name}.position ASC")
30 43 scope :givable, order("#{table_name}.position ASC").where(:builtin => 0)
31 44 scope :builtin, lambda { |*args|
32 45 compare = (args.first == true ? 'not' : '')
33 46 where("#{compare} builtin = 0")
34 47 }
35 48
36 49 before_destroy :check_deletable
37 50 has_many :workflows, :dependent => :delete_all do
38 51 def copy(source_role)
39 52 Workflow.copy(nil, source_role, nil, proxy_association.owner)
40 53 end
41 54 end
42 55
43 56 has_many :member_roles, :dependent => :destroy
44 57 has_many :members, :through => :member_roles
45 58 acts_as_list
46 59
47 serialize :permissions, Array
60 serialize :permissions, ::Role::PermissionsAttributeCoder
48 61 attr_protected :builtin
49 62
50 63 validates_presence_of :name
51 64 validates_uniqueness_of :name
52 65 validates_length_of :name, :maximum => 30
53 66 validates_inclusion_of :issues_visibility,
54 67 :in => ISSUES_VISIBILITY_OPTIONS.collect(&:first),
55 68 :if => lambda {|role| role.respond_to?(:issues_visibility)}
56 69
57 def permissions
58 read_attribute(:permissions) || []
59 end
60
61 70 def permissions=(perms)
62 71 perms = perms.collect {|p| p.to_sym unless p.blank? }.compact.uniq if perms
63 72 write_attribute(:permissions, perms)
64 73 end
65 74
66 75 def add_permission!(*perms)
67 76 self.permissions = [] unless permissions.is_a?(Array)
68 77
69 78 permissions_will_change!
70 79 perms.each do |p|
71 80 p = p.to_sym
72 81 permissions << p unless permissions.include?(p)
73 82 end
74 83 save!
75 84 end
76 85
77 86 def remove_permission!(*perms)
78 87 return unless permissions.is_a?(Array)
79 88 permissions_will_change!
80 89 perms.each { |p| permissions.delete(p.to_sym) }
81 90 save!
82 91 end
83 92
84 93 # Returns true if the role has the given permission
85 94 def has_permission?(perm)
86 95 !permissions.nil? && permissions.include?(perm.to_sym)
87 96 end
88 97
89 98 def <=>(role)
90 99 if role
91 100 if builtin == role.builtin
92 101 position <=> role.position
93 102 else
94 103 builtin <=> role.builtin
95 104 end
96 105 else
97 106 -1
98 107 end
99 108 end
100 109
101 110 def to_s
102 111 name
103 112 end
104 113
105 114 def name
106 115 case builtin
107 116 when 1; l(:label_role_non_member, :default => read_attribute(:name))
108 117 when 2; l(:label_role_anonymous, :default => read_attribute(:name))
109 118 else; read_attribute(:name)
110 119 end
111 120 end
112 121
113 122 # Return true if the role is a builtin role
114 123 def builtin?
115 124 self.builtin != 0
116 125 end
117 126
118 127 # Return true if the role is a project member role
119 128 def member?
120 129 !self.builtin?
121 130 end
122 131
123 132 # Return true if role is allowed to do the specified action
124 133 # action can be:
125 134 # * a parameter-like Hash (eg. :controller => 'projects', :action => 'edit')
126 135 # * a permission Symbol (eg. :edit_project)
127 136 def allowed_to?(action)
128 137 if action.is_a? Hash
129 138 allowed_actions.include? "#{action[:controller]}/#{action[:action]}"
130 139 else
131 140 allowed_permissions.include? action
132 141 end
133 142 end
134 143
135 144 # Return all the permissions that can be given to the role
136 145 def setable_permissions
137 146 setable_permissions = Redmine::AccessControl.permissions - Redmine::AccessControl.public_permissions
138 147 setable_permissions -= Redmine::AccessControl.members_only_permissions if self.builtin == BUILTIN_NON_MEMBER
139 148 setable_permissions -= Redmine::AccessControl.loggedin_only_permissions if self.builtin == BUILTIN_ANONYMOUS
140 149 setable_permissions
141 150 end
142 151
143 152 # Find all the roles that can be given to a project member
144 153 def self.find_all_givable
145 154 Role.givable.all
146 155 end
147 156
148 157 # Return the builtin 'non member' role. If the role doesn't exist,
149 158 # it will be created on the fly.
150 159 def self.non_member
151 160 find_or_create_system_role(BUILTIN_NON_MEMBER, 'Non member')
152 161 end
153 162
154 163 # Return the builtin 'anonymous' role. If the role doesn't exist,
155 164 # it will be created on the fly.
156 165 def self.anonymous
157 166 find_or_create_system_role(BUILTIN_ANONYMOUS, 'Anonymous')
158 167 end
159 168
160 169 private
161 170
162 171 def allowed_permissions
163 172 @allowed_permissions ||= permissions + Redmine::AccessControl.public_permissions.collect {|p| p.name}
164 173 end
165 174
166 175 def allowed_actions
167 176 @actions_allowed ||= allowed_permissions.inject([]) { |actions, permission| actions += Redmine::AccessControl.allowed_actions(permission) }.flatten
168 177 end
169 178
170 179 def check_deletable
171 180 raise "Can't delete role" if members.any?
172 181 raise "Can't delete builtin role" if builtin?
173 182 end
174 183
175 184 def self.find_or_create_system_role(builtin, name)
176 185 role = where(:builtin => builtin).first
177 186 if role.nil?
178 187 role = create(:name => name, :position => 0) do |r|
179 188 r.builtin = builtin
180 189 end
181 190 raise "Unable to create the #{name} role." if role.new_record?
182 191 end
183 192 role
184 193 end
185 194 end
@@ -1,128 +1,133
1 1 # Redmine - project management software
2 2 # Copyright (C) 2006-2012 Jean-Philippe Lang
3 3 #
4 4 # This program is free software; you can redistribute it and/or
5 5 # modify it under the terms of the GNU General Public License
6 6 # as published by the Free Software Foundation; either version 2
7 7 # of the License, or (at your option) any later version.
8 8 #
9 9 # This program is distributed in the hope that it will be useful,
10 10 # but WITHOUT ANY WARRANTY; without even the implied warranty of
11 11 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 12 # GNU General Public License for more details.
13 13 #
14 14 # You should have received a copy of the GNU General Public License
15 15 # along with this program; if not, write to the Free Software
16 16 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 17
18 18 require File.expand_path('../../test_helper', __FILE__)
19 19
20 20 class RoleTest < ActiveSupport::TestCase
21 21 fixtures :roles, :workflows
22 22
23 23 def test_sorted_scope
24 24 assert_equal Role.all.sort, Role.sorted.all
25 25 end
26 26
27 27 def test_givable_scope
28 28 assert_equal Role.all.reject(&:builtin?).sort, Role.givable.all
29 29 end
30 30
31 31 def test_builtin_scope
32 32 assert_equal Role.all.select(&:builtin?).sort, Role.builtin(true).all.sort
33 33 assert_equal Role.all.reject(&:builtin?).sort, Role.builtin(false).all.sort
34 34 end
35 35
36 36 def test_copy_workflows
37 37 source = Role.find(1)
38 38 assert_equal 90, source.workflows.size
39 39
40 40 target = Role.new(:name => 'Target')
41 41 assert target.save
42 42 target.workflows.copy(source)
43 43 target.reload
44 44 assert_equal 90, target.workflows.size
45 45 end
46 46
47 def test_permissions_should_be_unserialized_with_its_coder
48 Role::PermissionsAttributeCoder.expects(:load).once
49 Role.find(1).permissions
50 end
51
47 52 def test_add_permission
48 53 role = Role.find(1)
49 54 size = role.permissions.size
50 55 role.add_permission!("apermission", "anotherpermission")
51 56 role.reload
52 57 assert role.permissions.include?(:anotherpermission)
53 58 assert_equal size + 2, role.permissions.size
54 59 end
55 60
56 61 def test_remove_permission
57 62 role = Role.find(1)
58 63 size = role.permissions.size
59 64 perm = role.permissions[0..1]
60 65 role.remove_permission!(*perm)
61 66 role.reload
62 67 assert ! role.permissions.include?(perm[0])
63 68 assert_equal size - 2, role.permissions.size
64 69 end
65 70
66 71 def test_name
67 72 I18n.locale = 'fr'
68 73 assert_equal 'Manager', Role.find(1).name
69 74 assert_equal 'Anonyme', Role.anonymous.name
70 75 assert_equal 'Non membre', Role.non_member.name
71 76 end
72 77
73 78 def test_find_all_givable
74 79 assert_equal Role.all.reject(&:builtin?).sort, Role.find_all_givable
75 80 end
76 81
77 82 context "#anonymous" do
78 83 should "return the anonymous role" do
79 84 role = Role.anonymous
80 85 assert role.builtin?
81 86 assert_equal Role::BUILTIN_ANONYMOUS, role.builtin
82 87 end
83 88
84 89 context "with a missing anonymous role" do
85 90 setup do
86 91 Role.delete_all("builtin = #{Role::BUILTIN_ANONYMOUS}")
87 92 end
88 93
89 94 should "create a new anonymous role" do
90 95 assert_difference('Role.count') do
91 96 Role.anonymous
92 97 end
93 98 end
94 99
95 100 should "return the anonymous role" do
96 101 role = Role.anonymous
97 102 assert role.builtin?
98 103 assert_equal Role::BUILTIN_ANONYMOUS, role.builtin
99 104 end
100 105 end
101 106 end
102 107
103 108 context "#non_member" do
104 109 should "return the non-member role" do
105 110 role = Role.non_member
106 111 assert role.builtin?
107 112 assert_equal Role::BUILTIN_NON_MEMBER, role.builtin
108 113 end
109 114
110 115 context "with a missing non-member role" do
111 116 setup do
112 117 Role.delete_all("builtin = #{Role::BUILTIN_NON_MEMBER}")
113 118 end
114 119
115 120 should "create a new non-member role" do
116 121 assert_difference('Role.count') do
117 122 Role.non_member
118 123 end
119 124 end
120 125
121 126 should "return the non-member role" do
122 127 role = Role.non_member
123 128 assert role.builtin?
124 129 assert_equal Role::BUILTIN_NON_MEMBER, role.builtin
125 130 end
126 131 end
127 132 end
128 133 end
General Comments 0
You need to be logged in to leave comments. Login now