can_play 0.2.4 → 0.2.5

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7eed223e4874c108017e81a8d0502041e3022e81
4
- data.tar.gz: 8a490f3b79ac9af3223e67f5b5ba7c6a6a9a0510
3
+ metadata.gz: 8defb79962f3b993057d0f3adcf9a10dc76b3b53
4
+ data.tar.gz: 7be32b34ecd58d7a57c03b6bd586868198d01092
5
5
  SHA512:
6
- metadata.gz: 3926d91df4c06593a9d521ffe5274304864a3c4fffb1fd58899ca044fe53e4cb4e2fd47e676637c38109d8a426a1aae3815ebe64055f3cabc2bb2f0b26f61192
7
- data.tar.gz: 13d6190e996237505d67a5c4abb74b46cbb963898b287dc9967abb6cef0a488ab505e4c5e00e2ae6e17fe3da94db1a459fa84a1a00046eab12ecac565dab3838
6
+ metadata.gz: cec401f5b1dd9a1fd5de254f720a1704f06fd8e482553b12d0637067c8b95782db584f65e72c001324196c2c66378e4774d5381479dcd3a69d3c0692a8a79d46
7
+ data.tar.gz: 8e913fc5a5d4dda021b0a03d2c1414884f02b4942fc7c4a97ac7d8e07bc169d4692d159ee99bddc7e2d62ad6e3f8d2898ea7768a42aa99371bf4dbe7f2a085d0
data/README.md CHANGED
@@ -1,33 +1,69 @@
1
- can_plan集成了cancancan和consul的功能,使用DSL描述用户对单个类的实例或某个类的操作权限,及可获取的条目的基础的relation对象.
1
+ 系统权限的管理,就是控制用户对某个资源的操作权限。这里说的资源并不仅仅是指ActiveRecord::Base的子类,也可以是ruby的其他类或者模型。can_plan集成了cancancan和consul的功能,使用简单的DSL描述用户对单个资源或某类资源的操作权限。它的权限管理方式是基于角色的,不同角色可以赋予不同的权限,我们可以让用户和资源的操作权限之间建立关联并保存进数据库.
2
2
 
3
3
  ## 安装方式
4
4
 
5
- ### 安装cancancan
6
5
 
7
- cancancan的使用请参见cancancan主页,在此我们安装后,不需要设置Ability文件,can_play在内部集成了这些设置。
8
- ### 安装consul
9
-
10
- consul的使用请参见consul主页,在此我们安装后,不需要设置power文件,也无需在controller中设置current_power,can_play在内部集成了这些设置。
6
+ can_play在内部集成了cancancan和consul两个gem,所以没必要再安装这两个gem。
11
7
  ### can_play安装
12
- 在gemfile中加入can_play的github地址来安装。
8
+ 在gemfile中加入can_play
9
+
10
+ ```
11
+ gem 'can_play'
12
+ ```
13
13
 
14
- 安装后执行如下命令
14
+ 运行bundle,安装完成后执行如下命令
15
15
 
16
16
  ```
17
17
  rails generate can_play:install
18
18
  ```
19
19
 
20
- 会在initializer和locales文件夹下生成文件。
20
+ 执行该命令会在initializer和locales文件夹下生成配置文件。
21
21
  initializer文件夹下的can_play.rb是can_play的基本配置文件。
22
22
  locales下的can_play.zh-Cn.yml文件用于描述权限名称。
23
-
24
- ### DSL文件描述权限
23
+
24
+ # path_to_config/initializers/can_play.rb
25
+ CanPlay::Config.setup do |config|
26
+ config.user_class_name = 'User'
27
+ config.role_class_name = 'Role'
28
+ config.role_resources_relation_name = 'role_resources'
29
+ config.super_roles = ['超级管理员']
30
+ end
31
+
32
+ can_play.rb配置文件中,可以传入用户类名(默认User)、角色类名(默认Role)、以及角色和权限的关联的名称(默认role_resources),role_resources_relation_name是角色类和操作权限资源之间的关联。
33
+
34
+ ---
35
+ zh-CN:
36
+ can_play:
37
+ class_name:
38
+ test: 测试
39
+ contract: 合同
40
+ authority_name:
41
+ common:
42
+ list: 列表查看
43
+ create: 新建
44
+ read: 查看
45
+ update: 修改
46
+ delete: 删除
47
+ crud: 管理
48
+ menus_roles: 菜单分配
49
+ role_resources: 权限分配
50
+ improve: 完善信息
51
+ contract:
52
+ terminate: 终止
53
+ purchaser_confirm: 采购人确认
54
+ supplier_confirm: 供应商确认
55
+
56
+ can_play.zh-CN.yml是中文翻译文件,在这里写下权限的英文和中文名称的对应,在前端就可以获取到权限的中文描述,其中common是一些常用权限名称,特别的权限名称,可以单独写,如contract下的terminate权限是合同独有的权限名称,必须单独写,而class_name也可以写上资源名称,如contract,如果不写,会默认去ActiveRecord的翻译文件下去取中文翻译。
57
+
58
+
59
+ ### DSL文件描述权限的方法
25
60
  dsl文件写法如下:
26
61
 
27
62
  #用哪个类用来描写权限,可在intializer下的can_play.rb文件下描写。
28
63
  class Resource
29
64
  include CanPlay
30
-
65
+ self.module_name = '核心模块'
66
+
31
67
  # 所有limit块、collection块和member块中都注入了user这个变量,指向当前登录用户,可直接使用。
32
68
 
33
69
  group Contract do |klass|
@@ -37,9 +73,9 @@ dsl文件写法如下:
37
73
  if user.is_admin?
38
74
  klass.all
39
75
  elsif user.role? '供应商'
40
- klass.where(emall: user.emall)
76
+ klass.where(supplier: user.supplier)
41
77
  elsif user.role? '采购人'
42
- klass.where(department: user.department)
78
+ klass.where(purchaser: user.purchaser)
43
79
  else
44
80
  klass.none
45
81
  end
@@ -55,9 +91,9 @@ dsl文件写法如下:
55
91
  if user.is_admin?
56
92
  true
57
93
  elsif user.role? '供应商'
58
- obj.emall.is? user.emall
94
+ obj.supplier.is? user.supplier
59
95
  elsif user.role? '采购人'
60
- obj.department.is? user.department
96
+ obj.purchaser.is? user.purchaser
61
97
  else
62
98
  false
63
99
  end
@@ -69,26 +105,25 @@ dsl文件写法如下:
69
105
  end
70
106
  end
71
107
 
72
- group Project do |klass|
108
+ end
109
+
110
+ `limit`方法用于控制某个用户可以查看的资源的额列表,如Contract类下的limit限制了管理员可以查看所有合同,供应商和采购人只能查看和自己相关的合同。limit方法会让在controller中生成一个动态方法,`current_power.contracts`,这个方法返回的是是我们再limit中写如的对象,这样就能根据用户的信息返回不同的资源数组。
73
111
 
74
- limit do
75
- if user.is_admin?
76
- klass.all
77
- else
78
- klass.none
79
- end
80
- end
112
+ `collection`方法,可以控制某个用户对某类资源的控制权限。如list和create权限,在controller中,我们可以用`authorize!(:read, Contract)`来限制用户的访问。
81
113
 
82
- collection [:list, :create], klass do
83
- user.is_admin?
84
- end
114
+ `member`方法,可以控制用户对某个资源的控制权限,如read权限,在controller中我们可以用authorize!(:read, @contract)来限制用户的访问。
85
115
 
86
- member [:read, :update, :delete, :create_later_documents], klass do |obj|
87
- user.is_admin?
88
- end
89
- end
90
- end
116
+ `self.module_name = '核心模块'`是用来处理在多模块开发的环境下,各个模块可能有自己的resource文件,并可能出现中文的重名,权限最终要集中管理,module_name可以做个简单的分隔,让用户清楚某个权限属于哪个模块。
117
+
118
+ 这些在controller中需要使用的current_power方法和authorize!方法分别是consul和cancancan中既有的方法。
91
119
 
92
120
  ### 和角色类之间建立关联
93
121
 
94
- 此处的DSL相当于在数据库中的resouces表,记录了所有权限。我们需要通过role_resources这样的中间表,建立角色和资源之间的关联。因此在数据库建立中间表role_resources,使用一个resource_name字段来跟DSL中的权限、资源进行关联。我们再前端页面,只需要调用Resource.grouped_resources_with_chinese_desc就可获取到所有DSL文件中描述的所有权限以及中文描述。再在controller和view中创建权限和role的关联即可(往role_resources中间表写条目)。
122
+ 此处的resouce文件相当于在数据库中的resouces表,以动态的语言记录了所有的权限。我们需要通过role_resources这样的中间表,建立角色和权限之间的关联。可以在数据库建立中间表role_resources
123
+
124
+ 在controller或view中只要调用 CanPlay.splat_grouped_resources_with_chinese_desc就能返回所有的权限hash,并按资源进行了分组。如果调用CanPlay.grouped_resources_with_chinese_desc则返回按module_name分组后,再按资源名称分组的权限hash。我们用这个hash在表单中呈现,让用户勾选,然后在controller中保存角色和资源权限的关联。
125
+
126
+
127
+ 其中roles_resources的表结构,至少要有role_id、resource_name字段,其中role_id关联角色,而 resource_name用于关联resource类(伪关联,实际不用加belongs_to这类关联)。
128
+
129
+ 有了这个关联,加载页面,就可以自动执行权限的限制了,当然前提是你在controller的每个action加入了cancancan的权限限制语句authorize!。
data/can_play.gemspec CHANGED
@@ -24,4 +24,5 @@ Gem::Specification.new do |spec|
24
24
  spec.add_dependency 'cancancan', "~> 1.12"
25
25
  spec.add_dependency 'consul', "~> 0.12"
26
26
  spec.add_dependency 'ror_hack', "~> 0.1 "
27
+ spec.add_dependency 'modularity', "~> 2.0 "
27
28
  end
@@ -5,12 +5,12 @@ class Ability
5
5
  def initialize(user)
6
6
  self.user = user||CanPlay::Config.user_class_name.constantize.new
7
7
  CanPlay::Config.super_roles.each do |role_name|
8
- can(:manage, :all) if user.role?(role_name)
8
+ can(:manage, :all) if user.send(CanPlay::Config.role_judge_method, role_name)
9
9
  end
10
10
  CanPlay::Config.role_class_name.constantize.all.each do |role|
11
11
  next unless user.role?(role.name)
12
12
  role.send(CanPlay::Config.role_resources_relation_name).each do |role_resource|
13
- resource = CanPlay.find_by_name(role_resource.resource_name)
13
+ resource = CanPlay.find_by_name_and_code(role_resource.resource_name, CanPlay.override_code)
14
14
  next unless resource
15
15
  if resource[:type] == 'collection'
16
16
  if resource[:behavior]
@@ -1,6 +1,29 @@
1
1
  class ActionController::Base
2
2
  include Consul::Controller
3
3
  current_power do
4
- Power.new(current_user)
4
+ CanPlay::Power.new(current_user)
5
5
  end
6
+
7
+ # 对current_power采用动态方法调用的装饰者。
8
+ class PlayResourceObject < BasicObject
9
+ def initialize(obj, klass)
10
+ @obj = obj
11
+ @klass = klass
12
+ end
13
+
14
+ def method_missing(method, *args, &block)
15
+ if @obj.respond_to? "#{method}_evaluate_in_#{@klass.override_code}_scope"
16
+ @obj.send("#{method}_evaluate_in_#{@klass.override_code}_scope", *args, &block)
17
+ elsif @obj.respond_to? method
18
+ @obj.send(method, *args, &block)
19
+ else
20
+ super
21
+ end
22
+ end
23
+ end
24
+
25
+ def play_resources
26
+ @play_resource_object ||= PlayResourceObject.new(current_power, CanPlay)
27
+ end
28
+
6
29
  end
@@ -1,9 +1,11 @@
1
- class Power
2
- include Consul::Power
3
- attr_accessor :user
1
+ module CanPlay
2
+ class Power
3
+ include Consul::Power
4
+ attr_accessor :user
4
5
 
5
- def initialize(user)
6
- self.user = user
7
- end
6
+ def initialize(user)
7
+ self.user = user
8
+ end
8
9
 
10
+ end
9
11
  end
@@ -1,3 +1,3 @@
1
1
  module CanPlay
2
- VERSION = "0.2.4"
2
+ VERSION = "0.2.5"
3
3
  end
data/lib/can_play.rb CHANGED
@@ -1,41 +1,41 @@
1
1
  require 'ror_hack'
2
2
  require 'consul'
3
3
  require 'cancancan'
4
- require "can_play/version"
5
- require "can_play/power"
6
- require "can_play/ability"
7
- require "can_play/controller"
4
+ require 'modularity'
8
5
 
9
6
  module CanPlay
10
- mattr_accessor :resources
11
- @@resources = []
7
+ mattr_accessor :resources, :override_resources, :override_code
8
+ self.override_code = nil
9
+ self.resources = []
10
+ self.override_resources = {}.with_indifferent_access
12
11
 
13
12
  class << self
14
13
  def included(base)
15
14
  base.class_eval <<-RUBY
16
- include RorHack::ClassLevelInheritableAttributes
17
- inheritable_attributes(:groups, :current_group, :module_name)
18
- @groups = []
19
- @current_group = nil
20
- @module_name = ''
15
+ singleton_attr_accessor(:groups, :current_group, :module_name)
16
+ self.groups = []
17
+ self.current_group = nil
18
+ self.module_name = ''
21
19
  RUBY
22
20
  base.extend ClassMethods
23
21
  end
24
22
 
25
- def find_by_name(name)
26
- CanPlay.resources.find { |r| r.name == name }
23
+ def find_by_name_and_code(name, code)
24
+ resource = CanPlay.override_resources[code].p2a.find { |r| r.name.to_s == name.to_s }
25
+ resource || CanPlay.resources.find { |r| r.name.to_s == name.to_s }
27
26
  end
28
27
 
29
- def grouped_resources
30
- @grouped_resources ||= CanPlay.resources.multi_group_by(:module_name, :group)
28
+ def conjunct_resources
29
+ resources = CanPlay.override_resources[CanPlay.override_code].p2a + CanPlay.resources
30
+ resources.uniq { |i| i.name }
31
31
  end
32
32
 
33
- def splat_grouped_resources
34
- @grouped_resources ||= CanPlay.resources.multi_group_by(:group)
33
+ def grouped_resources
34
+ conjunct_resources.multi_group_by :module_name, :group
35
35
  end
36
36
 
37
- def my_resources
38
- CanPlay.resources
37
+ def splat_grouped_resources
38
+ conjunct_resources.multi_group_by :group
39
39
  end
40
40
 
41
41
  def grouped_resources_with_chinese_desc
@@ -60,8 +60,8 @@ module CanPlay
60
60
  splat_grouped_resources.tap do |i|
61
61
  i.each do |group, resources|
62
62
  group[:chinese_desc] = begin
63
- name = I18n.t("can_play.class_name.#{group[:name].singularize}", default: '')
64
- name = group[:klass].model_name.human if name.blank?
63
+ name = I18n.t("can_play.class_name.#{group.name.singularize}", default: '')
64
+ name = group.klass.model_name.human if name.blank?
65
65
  name
66
66
  end
67
67
  resources.each do |resource|
@@ -74,47 +74,52 @@ module CanPlay
74
74
  end
75
75
 
76
76
  module Config
77
- mattr_accessor :user_class_name, :role_class_name, :role_resources_relation_name, :super_roles
78
- @@user_class_name = 'User'
79
- @@role_class_name = 'Role'
80
- @@role_resources_relation_name = 'role_resources'
81
- @@super_roles = []
77
+ mattr_accessor :user_class_name, :role_class_name, :role_resources_relation_name, :super_roles, :role_judge_method
78
+ self.user_class_name = 'User'
79
+ self.role_class_name = 'Role'
80
+ self.role_resources_relation_name = 'role_resources'
81
+ self.super_roles = []
82
+ self.role_judge_method = 'role?'
82
83
 
83
84
  def self.setup
84
85
  yield self
85
86
  end
86
87
  end
87
88
 
88
-
89
-
90
89
  module ClassMethods
91
90
 
91
+ class NameImportantOpenStruct < OpenStruct
92
+ def eql?(another)
93
+ self.name == another.name
94
+ end
95
+ end
96
+
92
97
  # 为每个 resource 添加一个 group, 方便管理
93
98
  def group(opts, &block)
94
99
  if opts.is_a?(Hash)
95
100
  opts = opts.with_indifferent_access
96
- group = OpenStruct.new(name: opts.delete(:name).to_s, klass: opts.delete(:klass))
101
+ group = NameImportantOpenStruct.new(name: opts.delete(:name).to_s, klass: opts.delete(:klass))
97
102
  elsif opts.is_a?(Module)
98
- name = opts.try(:table_name).presence || opts.to_s.underscore.gsub('/', '_').pluralize
99
- group = OpenStruct.new(name: name, klass: opts)
103
+ name = opts.try(:table_name).presence || opts.to_s.underscore.gsub('/', '_').pluralize
104
+ group = NameImportantOpenStruct.new(name: name, klass: opts)
100
105
  else
101
106
  # do nothing
102
107
  end
103
- @groups << group
104
- @groups = @groups.uniq(&:name)
105
- @current_group = group
108
+ self.groups << group
109
+ self.groups = groups.uniq(&:name)
110
+ self.current_group = group
106
111
  block.call(group.klass)
107
- @current_group = nil
112
+ self.current_group = nil
108
113
  end
109
114
 
110
115
  def limit(name=nil, &block)
111
- raise "Need define group first" if @current_group.nil?
112
- Power.power(name||@current_group.name, &block)
116
+ raise "Need define group first" if current_group.nil?
117
+ CanPlay::Power.power(name||current_group.name, &block)
113
118
  end
114
119
 
115
- def collection(verb_or_verbs, &block)
116
- raise "Need define group first" if @current_group.nil?
117
- group = @current_group
120
+ def collection(verb_or_verbs, opts={}.with_indifferent_access, &block)
121
+ raise "Need define group first" if current_group.nil?
122
+ group = current_group
118
123
  behavior = nil
119
124
  if block
120
125
  behavior = lambda do |user|
@@ -127,16 +132,16 @@ module CanPlay
127
132
 
128
133
  if verb_or_verbs.kind_of?(Array)
129
134
  verb_or_verbs.each do |verb|
130
- add_resource(group, verb, group.klass, 'collection', behavior)
135
+ add_resource(group, verb, group.klass, 'collection', behavior, opts)
131
136
  end
132
137
  else
133
- add_resource(group, verb_or_verbs, group.klass, 'collection', behavior)
138
+ add_resource(group, verb_or_verbs, group.klass, 'collection', behavior, opts)
134
139
  end
135
140
  end
136
141
 
137
- def member(verb_or_verbs, &block)
138
- raise "Need define group first" if @current_group.nil?
139
- group = @current_group
142
+ def member(verb_or_verbs, opts={}.with_indifferent_access, &block)
143
+ raise "Need define group first" if current_group.nil?
144
+ group = current_group
140
145
  behavior = nil
141
146
  if block
142
147
  behavior = lambda do |user, obj|
@@ -149,26 +154,65 @@ module CanPlay
149
154
 
150
155
  if verb_or_verbs.kind_of?(Array)
151
156
  verb_or_verbs.each do |verb|
152
- add_resource(group, verb, group.klass, 'member', behavior)
157
+ add_resource(group, verb, group.klass, 'member', behavior, opts)
153
158
  end
154
159
  else
155
- add_resource(group, verb_or_verbs, group.klass, 'member', behavior)
160
+ add_resource(group, verb_or_verbs, group.klass, 'member', behavior, opts)
156
161
  end
157
162
  end
158
163
 
159
- def add_resource(group, verb, object, type, behavior)
164
+ def add_resource(group, verb, object, type, behavior, opts)
160
165
  name = "#{verb}_#{group.name}"
161
166
  resource = OpenStruct.new(
162
167
  module_name: module_name,
163
- name: name,
164
- group: group,
165
- verb: verb,
166
- object: object,
167
- type: type,
168
- behavior: behavior
168
+ name: name,
169
+ group: group,
170
+ verb: verb,
171
+ object: object,
172
+ type: type,
173
+ behavior: behavior,
174
+ opts: opts
169
175
  )
170
176
  CanPlay.resources.keep_if { |i| i.name != name }
171
177
  CanPlay.resources << resource
172
178
  end
173
179
  end
180
+
181
+ module Override
182
+ as_trait do |code|
183
+ extend ClassMethods
184
+ singleton_attr_accessor(:groups, :current_group, :module_name)
185
+ self.groups = []
186
+ self.current_group = nil
187
+ self.module_name = ''
188
+
189
+ define_singleton_method(:limit) do |name=nil, &block|
190
+ raise "Need define group first" if current_group.nil?
191
+ method_name = name ? "#{name}_evaluate_in_#{code}_scope" : "#{current_group.name}_evaluate_in_#{code}_scope"
192
+ Power.power(method_name, &block)
193
+ end
194
+
195
+ define_singleton_method(:add_resource) do |group, verb, object, type, behavior, opts|
196
+ super(group, verb, object, type, behavior, opts) and return if code.blank?
197
+ CanPlay.override_resources[code] ||= []
198
+ name = "#{verb}_#{group.name}"
199
+ resource = OpenStruct.new(
200
+ module_name: module_name,
201
+ name: name,
202
+ group: group,
203
+ verb: verb,
204
+ object: object,
205
+ type: type,
206
+ behavior: behavior,
207
+ opts: opts
208
+ )
209
+ CanPlay.override_resources[code].keep_if { |i| i.name != name }
210
+ CanPlay.override_resources[code] << resource
211
+ end
212
+ end
213
+ end
174
214
  end
215
+
216
+ require "can_play/power"
217
+ require "can_play/controller"
218
+ require "can_play/ability"
@@ -13,4 +13,7 @@ CanPlay::Config.setup do |config|
13
13
 
14
14
  # super_roles表示无需分配权限既可拥有所有权限的角色。
15
15
  config.super_roles = ['超级管理员']
16
+
17
+ # 判断角色是否符合的方法。
18
+ config.role_judge_method = 'role?'
16
19
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: can_play
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.4
4
+ version: 0.2.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - happyming9527
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2015-08-30 00:00:00.000000000 Z
11
+ date: 2015-09-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -80,6 +80,20 @@ dependencies:
80
80
  - - ~>
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0.1'
83
+ - !ruby/object:Gem::Dependency
84
+ name: modularity
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ~>
88
+ - !ruby/object:Gem::Version
89
+ version: '2.0'
90
+ type: :runtime
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ~>
95
+ - !ruby/object:Gem::Version
96
+ version: '2.0'
83
97
  description: control user's permissions based on role and resource.
84
98
  email:
85
99
  - happyming9527@gmail.com