sevencan 0.1.8 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7db631307564eb0f1fd649a781d5d4b91977192f
4
- data.tar.gz: 383f380c830eae6bc8663cd02a2698bb5d964100
3
+ metadata.gz: b220308d46b28e4232122be61e42d6bf78d30b77
4
+ data.tar.gz: 990548a985b65bc327b3944a91b3c178c90b32db
5
5
  SHA512:
6
- metadata.gz: 2ad8f13ee3c7ab08049b8c1b42b0f25fdc15ac46342fb66bcfc41867cdff1498a18ec53fe89255813aabe00a1d539fe7546412ca1268c4685ea9a3c5e7a643ab
7
- data.tar.gz: 945fa4ece21230c9ff4deb120afcc1a5702646fea248c501a7cade40151bfdae8fefb2784973eed4a434bd202e869d5654b4598259021a777971b7e32148fbd5
6
+ metadata.gz: ca0e4a6f5a4de9f456e1d1bbe5e6d86c0a4d8248d58121263ca1051834a1b3d3273da6cfe2ff427fd92eebbb108c8291fb4dcd69ccc6cdaebcb4936f1a110c67
7
+ data.tar.gz: 746f524f2e012be8497df9674db0924029899f8f8bb4dabbc9f3816ca9048c807993ddc1eec36becfde1b74ba44fb01ede241e9af3c388ba9127c2334ce6ae71
data/.travis.yml ADDED
@@ -0,0 +1,30 @@
1
+ sudo: false
2
+ dist: trusty
3
+ language: ruby
4
+
5
+ cache: bundler
6
+
7
+ rvm:
8
+ - 2.1.10
9
+ - 2.2.7
10
+ - 2.3.4
11
+ - 2.4.1
12
+
13
+ gemfile:
14
+ - gemfiles/activesupport-4.0
15
+ - gemfiles/activesupport-4.1
16
+ - gemfiles/activesupport-4.2
17
+ - gemfiles/activesupport-5.0
18
+ - gemfiles/activesupport-5.1
19
+
20
+ matrix:
21
+ exclude:
22
+ - {rvm: '2.1.10', gemfile: 'gemfiles/activesupport-5.0'}
23
+ - {rvm: '2.1.10', gemfile: 'gemfiles/activesupport-5.1'}
24
+ - {rvm: '2.4.1', gemfile: 'gemfiles/activesupport-4.0'}
25
+ - {rvm: '2.4.1', gemfile: 'gemfiles/activesupport-4.1'}
26
+
27
+ before_install:
28
+ - gem install bundler --no-doc
29
+
30
+ script: bundle exec rspec
data/Gemfile CHANGED
@@ -1,4 +1,4 @@
1
- source 'https://gems.ruby-china.org'
1
+ source "https://rubygems.org"
2
2
 
3
3
  # Specify your gem's dependencies in seven.gemspec
4
4
  gemspec
data/README.md CHANGED
@@ -1,6 +1,9 @@
1
1
  # Seven
2
2
 
3
- Permission manage center
3
+ Define and verify Permissions.
4
+
5
+ [![Build Status](https://travis-ci.org/xiejiangzhi/seven.svg?branch=master)](https://travis-ci.org/xiejiangzhi/seven)
6
+ [![Gem Version](https://badge.fury.io/rb/sevencan.svg)](https://badge.fury.io/rb/sevencan)
4
7
 
5
8
  ## Installation
6
9
 
@@ -18,56 +21,96 @@ Or install it yourself as:
18
21
 
19
22
  $ gem install sevencan
20
23
 
24
+
21
25
  ## Usage
22
26
 
23
- New manager
27
+ ### Create your manager
24
28
 
25
29
  ```
26
- manager = Seven::Manager.new # save dynamic abilities to memory store
27
- manager = Seven::Manager.new(store: {redis: Redis.current}) # redis store
28
- manager = Seven::Manager.new(store: {activerecord: UserAbility}) # db store
30
+ $abilities_manager = Seven::Manager.new
29
31
  ```
30
32
 
31
- Define system rules
33
+ You can put it to `config/initializers/abilities.rb` if you on Rails
34
+
35
+
36
+ ### Define your rules
37
+
38
+ On Rails, we can save these rules as app/abilities/*_abilities.rb
39
+
40
+ A simple example
32
41
 
33
42
  ```
34
- # all objects, global rules
35
- manager.define_rules(Object) do
36
- can :read_home, :read_topics
43
+ class TopicAbilities
44
+ include Seven::Abilities
45
+
46
+ # it has some instance methods:
47
+ # current_user: a user instance of nil
48
+ # target: verify current_user permissions with this target. It's a Topic or a instance of Topic here
49
+
50
+
51
+ # if target if Topic or a instance of Topic, we will use this class to verify ability
52
+ $abilities_manager.define_rules(Topic, TopicAbilities)
53
+
54
+ # anyone can read list of topics(index action), non-login user also can read it
55
+ abilities do
56
+ can :read_topics
57
+ end
58
+
59
+ # define some abilities if the user logined
60
+ abilities pass: Proc.new { current_user } do
61
+ # user can read show page and create a new topic.(show, new and create action)
62
+ can :read_topic, :create_topic
63
+
64
+ subject = target.is_a?(Topic) ? Topic.new : target # Maybe the target is a Topic class
65
+ # user can edit and destroy own topic.(edit, update and destroy action)
66
+ can :edit_topic, :delete_topic if subject.user_id == current_user.id
67
+ end
68
+
69
+ # define some abilities if current_user is admin. `%w{admin}.include?(current_user.role)`
70
+ abilities check: :role, in: %w{admin} do
71
+ # admin can edit and delete all topics
72
+ can :edit_topic, :delete_topic
73
+ end
37
74
  end
75
+ ```
76
+
77
+ You also can write saome complex rules
38
78
 
79
+ ```
39
80
  # Topic and Topic instances
40
- class MyTopicAbilities
81
+ class TopicAbilities
41
82
  include Seven::Abilities
42
83
 
43
- # Instance methods:
44
- # current_user:
45
- # target:
84
+ $abilities_manager.define_rules(Topic, TopicAbilities)
85
+
86
+ # we will define some abilities for any user(user instance or nil) in this block
46
87
  abilities do
47
88
  can(:read_topic)
48
89
  can_manager_topic if target_topic.user_id == current_user.id
49
-
50
90
  cannot_manager_topic if target_topic.is_lock
51
91
  end
52
92
 
53
- # if [:admin, :editor].include?(current_user.role)
54
- abilities check: :role, in: [:admin, :editor] do
93
+ # if current user(current_user isn't nil) and %i{admin editor}.include?(current_user.role)
94
+ # we will define some abilities for the user
95
+ abilities check: :role, in: %i{admin editor} do
55
96
  can_manager_topic
56
97
  end
57
98
 
58
- # current_user.role eqlual :reviewer
99
+ # current_user.role is :reviewer
59
100
  abilities check: :role, equal: :reviewer do
60
101
  can :review_topic
61
102
  end
62
103
 
63
- abilities pass: Proc.new { current_user.permissions.include?(:topic_manager) } do
104
+ # Of course, you also can use your rule.
105
+ # For example, we will give current_user some abilities if our proc doesn't return a false or nil value
106
+ abilities pass: Proc.new { current_user && target.user_id.nil? } do
64
107
  can_manager_topic
65
108
  end
66
109
 
67
- abilities pass: :editor_filter do
110
+ # And you can move that proc to a instance method
111
+ abilities pass: :my_filter do
68
112
  end
69
113
 
70
-
71
114
  def can_manager_topic
72
115
  can :edit_topic, :destroy_topic
73
116
  end
@@ -76,64 +119,68 @@ class MyTopicAbilities
76
119
  cannot :edit_topic, :destroy_topic
77
120
  end
78
121
 
79
- def editor_filter
80
- current_user.permissions.include?(:topic_editor)
122
+ def my_filter
123
+ current_user && target.user_id.nil?
81
124
  end
82
125
  end
126
+ ```
83
127
 
84
- manager.define_rules(Topic, MyTopicAbilities)
128
+ You can define some abilities for all objects
85
129
 
86
- # with block
87
- manager.define_rules(User) do
88
- can(:read_user)
89
- can(:edit_user) if target.id == current_user.id
90
- can(:destroy_user) if current_user.is_admin?
91
- end
130
+ ```
131
+ # for all objects, they're global rules
132
+ $abilities_manager.define_rules(Object, YourAbilities)
92
133
  ```
93
134
 
94
- Manage dynamic rules
135
+ Use a block to define some abilities
95
136
 
96
137
  ```
97
- manager.add_dynamic_rule(user, :edit_user)
98
- manager.list_dynamic_rules(user)
99
- manager.del_dynamic_rules(user, :edit_user)
138
+ $abilities_manager.define_rules(User) do
139
+ can(:read_user)
140
+ if current_user
141
+ can(:edit_user) if target.id == current_user.id
142
+ can(:destroy_user) if current_user.is_admin?
143
+ end
144
+ end
100
145
  ```
101
146
 
102
- Check abilities
103
147
 
104
- Target is nil
148
+
149
+ ### Verify user abilities
150
+
151
+ No target
105
152
 
106
153
  ```
107
154
  manager.define_rules(Object) { can :read_topics }
108
- manager.can?(current_user, :read_topics) # true
109
- manager.can?(nil, :read_topics) # true
110
- manager.can?(current_user, :read_user) # false
155
+ manager.can?(current_user, :read_topics, nil) # true, target is nil
156
+ manager.can?(nil, :read_topics) # true, anyone can read_topics
111
157
 
158
+ manager.can?(current_user, :read_user) # false, we didn't define this abilities
112
159
  manager.can?(current_user, :edit_user) # false
113
160
 
114
- manager.add_dynamic_rule(user, :edit_user)
161
+ manager.store.add(user.id, :edit_user, true)
115
162
  manager.can?(current_user, :edit_user) # true
116
163
  manager.can?(nil, :edit_user) # true
117
164
  ```
118
165
 
119
- Specify target class
166
+ Verify abilities for a class or its instances
120
167
 
121
168
  ```
122
169
  manager.define_rules(Topic) { can :read_topics }
123
- manager.can?(nil, :read_topics, Topic) # true
124
- manager.can?(nil, :read_topics, Topic.first) # true
170
+ manager.can?(nil, :read_topics, Topic) # true, for Topic class
171
+ manager.can?(nil, :read_topics, Topic.first) # true, for instance of Topic
125
172
  manager.can?(current_user, :read_topics, Topic.first) # true
126
173
  manager.can?(current_user, :read_topics) # false
127
- manager.can?(nil, :read_topics) # false
174
+ manager.can?(nil, :read_topics) # false, it's target is nil, it isn't a topic
128
175
 
129
- manager.add_dynamic_rule(user, :edit_user, User)
176
+ manager.store.add(user.id, :edit_user, true)
130
177
  manager.can?(current_user, :edit_user, User) # true
131
178
  manager.can?(current_user, :edit_user, User.first) # true
132
- manager.can?(current_user, :edit_user) # false
179
+ manager.can?(current_user, :edit_user) # true
133
180
  manager.can?(nil, :edit_user) # false
134
181
  ```
135
182
 
136
- Specify instance
183
+ Define and verify abilities for a instance(TODO)
137
184
 
138
185
  ```
139
186
  manager.define_rules(Topic.first) { can :read_topics }
@@ -143,93 +190,94 @@ manager.can?(current_user, :read_topics, Topic.first) # true
143
190
  manager.can?(current_user, :read_topics, Topic.last) # false
144
191
  manager.can?(current_user, :read_topics) # false
145
192
  manager.can?(nil, :read_topics) # false
146
-
147
- manager.add_dynamic_rule(user, :edit_user, User.first)
148
- manager.can?(current_user, :edit_user, User) # false
149
- manager.can?(current_user, :edit_user, User.first) # true
150
- manager.can?(current_user, :edit_user, User.last) # false
151
- manager.can?(current_user, :edit_user) # false
152
- manager.can?(nil, :edit_user) # false
153
193
  ```
154
194
 
155
195
 
156
196
  ## Rails
157
197
 
158
-
159
- ### Init manager
198
+ ### Init your manager
160
199
 
161
200
  in `config/initializers/seven_abilities.rb`
162
201
 
163
202
  ```
164
203
  $abilities_manager = Seven::Manager.new
165
- Dir[Rails.root.join('app/abilities/**/*.rb')].each {|file| require file }
204
+ Dir[Rails.root.join('app/abilities/**/*.rb')].each { |file| require file }
166
205
  ```
167
206
 
168
- Define rules in `app/abilities/*.rb`
207
+ Define some rules in `app/abilities/*.rb`
169
208
 
170
209
  ```
171
- class UserAbilities
210
+ class MyAbilities
172
211
  include Seven::Abilities
173
212
 
174
- $abilities_manager.define_rules(User, self)
213
+ $abilities_manager.define_rules(MyObject, MyAbilities)
175
214
 
176
- # define rules
215
+ # define some rules
177
216
  end"
178
217
  ```
179
218
 
180
- ### Require methods
181
219
 
182
- * `current_user`: return current user
183
- * `abilities_manager`: return `Seven::Manager` instance
184
- * `ability_check_callback`: call the method after check
220
+ ### ControllerHelpers
185
221
 
222
+ We need these methods of controller to check user ability
186
223
 
187
- ### ControllerHelpers
224
+ * `current_user`: It is MyAbilities#current_user
225
+ * `abilities_manager`: You need return a instance of `Seven::Manager`
226
+ * `ability_check_callback`: We will call it after verifying
227
+
228
+
229
+ For example:
188
230
 
189
231
  ```
190
232
  class ApplicationController < ActionController::Base
191
- # define `can?` method and `seven_ability_check` methods
192
- # define `seven_ability_check_filter` method
193
- # `seven_ability_check` call `before_action :seven_ability_check_filter`
233
+ # when you include `Seven::Rails::ControllerHelpers` module, it will do something below
234
+ # define `can?` instance method and `seven_ability_check` methods for your controller
235
+ # define `seven_ability_check_filter` instance method, it's callback of before_action for Seven
236
+ # define `seven_ability_check` class methods, it will call `before_action :seven_ability_check_filter` and store some your options
194
237
  include Seven::Rails::ControllerHelpers
195
238
 
196
239
  def abilities_manager
197
- $my_abilities_manager
240
+ $abilities_manager
198
241
  end
199
242
 
200
- def ability_check_callback(allowed, ability, target)
201
- # allowed: true or false, allowed is true when can access
202
- # ability: checked ability, like :read_topic
203
- # target: checked target object
243
+ def ability_check_callback(is_allowed, ability, target)
244
+ # is_allowed: true or false, is_allowed is true when user can access this action
245
+ # ability: ability of this action, like :read_topic
246
+ # target: resource object of this action
247
+ redirect_to root_path notice: 'Permission denied' unless is_allowed
204
248
  end
205
249
  end
206
250
  ```
207
251
 
208
- Default actions
252
+ Verify permissions for default actions, we will get a ability name according to controller name.
253
+
254
+ Some mapping examples:
255
+
256
+ * TopicController#index => :read_topics
257
+ * TopicController#show => :read_topic
258
+ * UserController#new => :create_user
259
+ * UserController#create => :create_user
260
+ * UserController#edit => :edit_user
261
+ * UserController#update => :edit_user
262
+ * UserController#destroy => :delete_user
263
+
209
264
 
210
265
  ```
211
266
  class TopicController < ApplicationController
212
267
  before_action :find_topic
213
268
 
214
- # if exist @topic, target is @topic, else use Proc result or Topic
215
- seven_ability_check [:@topic, Proc.new { fetch_check_target }, Topic]
269
+ # if exists @topic, target is @topic, else use the result of proc, use Topic if the proc return nil
270
+ seven_ability_check [:@topic, Proc.new { nil }, Topic]
216
271
 
217
- # auto check current_user allow read_topics of Topic
272
+ # Seven will automitically checks current_user has read_topics of Topic
273
+ # We have no @topic and proc is nil, the Topic is our target
218
274
  def index
219
275
  end
220
276
 
221
- # auto check current_user allow read_topic of @topic
277
+ # check current_user can read_topic of @topic
222
278
  def show
223
279
  end
224
280
 
225
- # Other actions:
226
- # new: create_topic of Topic
227
- # create: create_topic of Topic
228
- # edit: edit_topic of @topic
229
- # update: edit_topic of @topic
230
- # destory: delete_topic of @topic
231
-
232
-
233
281
  private
234
282
 
235
283
  def find_topic
@@ -238,7 +286,7 @@ class TopicController < ApplicationController
238
286
  end
239
287
  ```
240
288
 
241
- Custom require ability for actions
289
+ Set a customized ability for actions
242
290
 
243
291
  ```
244
292
  class TopicController < ApplicationController
@@ -247,13 +295,13 @@ class TopicController < ApplicationController
247
295
  # if exist @topic, target is @topic, else use Topic
248
296
  seven_ability_check(
249
297
  [:@topic, Topic], # default targets
250
- my_action1: {ability: :custom_ability}, # use default targets
251
- my_action2: {ability: :custom_ability, target: [:@my_target]}
298
+ my_action1: {ability: :custom_ability}, # check :custom_ability and use default targets
299
+ my_action2: {ability: :custom_ability, target: [:@my_target]} # check :custom_ability and use :@my_target
252
300
  )
253
301
  # or
254
302
  # seven_ability_check(
255
- # index: {ability: read_my_ability, target: SuperTopic},
256
- # my_action1: {ability: :custom_ability1}, # use default targets
303
+ # index: {ability: :read_my_ability, target: SuperTopic},
304
+ # my_action1: {ability: :custom_ability1},
257
305
  # my_action2: {ability: :custom_ability2, target: [:@my_target]}
258
306
  # )
259
307
 
@@ -275,7 +323,7 @@ class TopicController < ApplicationController
275
323
  end
276
324
  ```
277
325
 
278
- Custom resource name
326
+ Use a customize resource name, we will get ability according to this suffix
279
327
 
280
328
  ```
281
329
  class TopicController < ApplicationController
@@ -296,7 +344,7 @@ class TopicController < ApplicationController
296
344
  # create: create_comment of Topic
297
345
  # edit: edit_comment of @topic
298
346
  # update: edit_comment of @topic
299
- # destory: delete_comment of @topic
347
+ # destroy: delete_comment of @topic
300
348
 
301
349
 
302
350
  private
@@ -308,11 +356,12 @@ end
308
356
  ```
309
357
 
310
358
 
311
- Manual check, not call `ability_check_callback`
359
+ Manually check, don't call `ability_check_callback`
312
360
 
313
361
  ```
314
362
  class TopicController < ApplicationController
315
363
  before_action :find_topic
364
+ skip_before_action :seven_ability_check_filter
316
365
 
317
366
  def my_action1
318
367
  raise 'no permission' unless can?(:read_something, @topic)
@@ -328,6 +377,75 @@ class TopicController < ApplicationController
328
377
  end
329
378
  ```
330
379
 
380
+ Skip some actions
381
+
382
+ ```
383
+ class TopicController < ApplicationController
384
+ before_action :find_topic
385
+ skip_before_filter :seven_ability_check_filter, only: :index
386
+
387
+ def index
388
+ if page_no > 1
389
+ ability_check_callback(can?(:read_something, @topic), :read_something, @topic)
390
+ end
391
+ end
392
+
393
+
394
+ private
395
+
396
+ def find_topic
397
+ @topic = Topic.find(params[:id])
398
+ end
399
+ end
400
+ ```
401
+
402
+ ## Dynamic abilities
403
+
404
+ ### Store
405
+
406
+ ```
407
+ manager = Seven::Manager.new # read/write dynamic abilities from memory store
408
+ # or
409
+ manager = Seven::Manager.new(store: {redis: Redis.current}) # read/write dynamic abilities from Redis
410
+ # or
411
+ manager = Seven::Manager.new(store: MyStore.new) # read/write from your store, Seven just access MyStore#list methods
412
+ ```
413
+
414
+ ### Create your store
415
+
416
+ ```
417
+ # columns: user_id: integer, ability: string, status: boolean
418
+ class Ability < ActiveRecord::Base
419
+ def self.list(user)
420
+ where(user_id: user.id).each_with_object({}) do |record, result|
421
+ result[record.ability.to_sym] = record.status # true or false
422
+ end
423
+ end
424
+ end
425
+
426
+ Seven::Manager.new(store: Ability)
427
+
428
+ # then, you can add some abilities for a user:
429
+ Ability.create(user: user, ability: :read_user, status: true)
430
+ ```
431
+
432
+
433
+ ### Define some dynamic rules for system store
434
+
435
+ ```
436
+ $abilities_manager.store.add(user.id, :edit_user, true)
437
+ $abilities_manager.store.add(user.id, :create_user, false)
438
+ $abilities_manager.store.list(user.id) # {edit_user: true, create_user: false}
439
+
440
+ $abilities_manager.can?(user, :create_user, nil) # false
441
+ $abilities_manager.can?(user, :edit_user, nil) # true
442
+
443
+ $abilities_manager.store.del(user.id, :edit_user)
444
+ $abilities_manager.store.list(user.id) # {create_user: false}
445
+ ```
446
+
447
+
448
+
331
449
  ## RSpec Testing
332
450
 
333
451
  in `spec/rails_helper.rb` or `spec/spec_helper.rb`
@@ -336,7 +454,7 @@ in `spec/rails_helper.rb` or `spec/spec_helper.rb`
336
454
  require 'seven/rspec'
337
455
  ```
338
456
 
339
- Write abilities testing
457
+ Write some abilities testing
340
458
 
341
459
  ```
342
460
  RSpec.describe UserAbilities do
@@ -352,10 +470,11 @@ end
352
470
  ```
353
471
 
354
472
 
473
+
474
+
355
475
  ## TODO
356
476
 
357
- * [x] Rails Helpers
358
- * [ ] Dynamic rule
477
+ * [ ] Dynamic rule for a record
359
478
 
360
479
 
361
480
  ## Development
@@ -366,7 +485,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
366
485
 
367
486
  ## Contributing
368
487
 
369
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/seven. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
488
+ Bug reports and pull requests are welcome on GitHub at https://github.com/xiejiangzhi/seven. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](contributor-covenant.org) code of conduct.
370
489
 
371
490
 
372
491
  ## License
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in influx_orm.gemspec
4
+ gemspec path: '../'
5
+
6
+ gem 'activesupport', '~> 4.0.0'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in influx_orm.gemspec
4
+ gemspec path: '../'
5
+
6
+ gem 'activesupport', '~> 4.1.0'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in influx_orm.gemspec
4
+ gemspec path: '../'
5
+
6
+ gem 'activesupport', '~> 4.2.0'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in influx_orm.gemspec
4
+ gemspec path: '../'
5
+
6
+ gem 'activesupport', '~> 5.0.0'
@@ -0,0 +1,6 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in influx_orm.gemspec
4
+ gemspec path: '../'
5
+
6
+ gem 'activesupport', '~> 5.1.0'
@@ -1,7 +1,7 @@
1
1
  module Seven
2
2
  module Abilities
3
3
  class << self
4
- def warp_proc(rule_proc)
4
+ def wrap_proc(rule_proc)
5
5
  return unless rule_proc
6
6
 
7
7
  Class.new do
@@ -41,8 +41,8 @@ module Seven
41
41
  end
42
42
 
43
43
  def cannot(*some_abilities)
44
- syn_abilities = some_abilities.map(&:to_sym)
45
- @abilities.delete_if {|ability| syn_abilities.include?(ability) }
44
+ symtax_abilities = some_abilities.map(&:to_sym)
45
+ @abilities.delete_if {|ability| symtax_abilities.include?(ability) }
46
46
  end
47
47
 
48
48
 
data/lib/seven/manager.rb CHANGED
@@ -1,13 +1,17 @@
1
1
  module Seven
2
2
  class Manager
3
- attr_reader :rules
3
+ attr_reader :rules, :store
4
4
 
5
- def initialize
5
+ # Params:
6
+ # store: hash or your store, the store requires get(user_id) and set(user_id, ability, status) methods
7
+ # get(user_id) should return a hash of abilities {${ability}: ${true || false}}
8
+ def initialize(store: {})
6
9
  @rules = []
10
+ @store = fetch_store(store)
7
11
  end
8
12
 
9
13
  def define_rules(matcher, rule_class = nil, &rule_proc)
10
- rule_class ||= Seven::Abilities.warp_proc(rule_proc)
14
+ rule_class ||= Seven::Abilities.wrap_proc(rule_proc)
11
15
 
12
16
  if valid_rule_class?(rule_class)
13
17
  @rules << [matcher, rule_class]
@@ -25,7 +29,13 @@ module Seven
25
29
  # [A, B, Object].min # => B
26
30
  # find last class
27
31
  rule_class = matched_rules.min_by(&:first).last
28
- rule_class.new(current_user, target).abilities.include?(ability.to_sym)
32
+ abilities = rule_class.new(current_user, target).abilities
33
+
34
+ # dynamic abilities
35
+ store.list(current_user).each do |new_ability, is_allowed|
36
+ is_allowed ? (abilities << new_ability) : abilities.delete(new_ability)
37
+ end
38
+ abilities.include?(ability.to_sym)
29
39
  end
30
40
 
31
41
 
@@ -35,6 +45,24 @@ module Seven
35
45
  return false unless rule_class && rule_class.is_a?(Class)
36
46
  rule_class.included_modules.include?(Seven::Abilities)
37
47
  end
48
+
49
+ def fetch_store(store_options)
50
+ unless store_options.is_a?(Hash) || store_options.nil?
51
+ if store_options.respond_to?(:list)
52
+ return store_options
53
+ else
54
+ raise "Invalid store: #{store_options.inspect}, a store should defined #list method"
55
+ end
56
+ end
57
+
58
+ opts = (store_options || {}).symbolize_keys
59
+
60
+ if opts[:redis]
61
+ RedisStore.new(opts)
62
+ else
63
+ MemoryStore.new
64
+ end
65
+ end
38
66
  end
39
67
  end
40
68
 
@@ -0,0 +1,25 @@
1
+ class Seven::MemoryStore
2
+ def initialize
3
+ @data = {}
4
+ end
5
+
6
+ def set(user, ability, allowed)
7
+ (@data[user.id.to_s] ||= {}).merge!(ability.to_s.to_sym => !!allowed)
8
+ end
9
+
10
+ def del(user, ability)
11
+ (@data[user.id.to_s] ||= {}).delete(ability.to_s.to_sym)
12
+ end
13
+
14
+ def list(user)
15
+ @data[user.id.to_s] || {}
16
+ end
17
+
18
+ def clear(user)
19
+ @data.delete(user.id.to_s)
20
+ end
21
+
22
+ def clear_all!
23
+ @data.clear
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ require 'redis'
2
+
3
+ class Seven::RedisStore
4
+ def initialize(redis_opts)
5
+ opts = redis_opts.symbolize_keys
6
+ @redis = opts[:redis]
7
+ end
8
+
9
+ def set(user, ability, allowed)
10
+ @redis.hset(get_user_key(user.id), ability, allowed ? '1' : '0')
11
+ end
12
+
13
+ def del(user, ability)
14
+ @redis.hdel(get_user_key(user.id), ability)
15
+ end
16
+
17
+ def list(user)
18
+ @redis.hgetall(get_user_key(user.id)).symbolize_keys.tap do |abilities|
19
+ abilities.each { |k, v| abilities[k] = v == '1' ? true : false }
20
+ end
21
+ end
22
+
23
+ def clear(user)
24
+ @redis.del(get_user_key(user.id))
25
+ end
26
+
27
+ def clear_all!
28
+ @redis.eval(
29
+ "local keys = redis.call('keys', 'seven_abilities/*')\n" +
30
+ "for i = 1, #keys,5000 do\n" +
31
+ " redis.call('del', unpack(keys, i, math.min(i+4999, #keys)))\n" +
32
+ "end\n" +
33
+ "return #keys"
34
+ )
35
+ end
36
+
37
+ private
38
+
39
+ def get_user_key(user_id)
40
+ "seven_abilities/#{user_id}"
41
+ end
42
+ end
data/lib/seven/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Seven
2
- VERSION = "0.1.8"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/seven.rb CHANGED
@@ -9,5 +9,8 @@ module Seven
9
9
  autoload :Manager, 'seven/manager'
10
10
  autoload :Abilities, 'seven/abilities'
11
11
  autoload :Rails, 'seven/rails'
12
+
13
+ autoload :MemoryStore, 'seven/memory_store'
14
+ autoload :RedisStore, 'seven/redis_store'
12
15
  end
13
16
 
data/seven.gemspec CHANGED
@@ -20,9 +20,10 @@ Gem::Specification.new do |spec|
20
20
  spec.require_paths = ["lib"]
21
21
 
22
22
  spec.add_development_dependency "bundler", "~> 1.10"
23
- spec.add_development_dependency "rake", "~> 10.0"
24
- spec.add_development_dependency "rspec"
25
- spec.add_development_dependency "pry"
23
+ spec.add_development_dependency "rspec", '~> 3.7.0'
24
+ spec.add_development_dependency "pry", '>= 0.10'
25
+
26
+ spec.add_development_dependency "redis", '>= 3.0'
26
27
 
27
28
  spec.add_dependency "activesupport"
28
29
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sevencan
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - jiangzhi.xie
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-02-27 00:00:00.000000000 Z
11
+ date: 2017-12-24 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -25,47 +25,47 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.10'
27
27
  - !ruby/object:Gem::Dependency
28
- name: rake
28
+ name: rspec
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
31
  - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '10.0'
33
+ version: 3.7.0
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
38
  - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: '10.0'
40
+ version: 3.7.0
41
41
  - !ruby/object:Gem::Dependency
42
- name: rspec
42
+ name: pry
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
45
  - - ">="
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '0.10'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '0.10'
55
55
  - !ruby/object:Gem::Dependency
56
- name: pry
56
+ name: redis
57
57
  requirement: !ruby/object:Gem::Requirement
58
58
  requirements:
59
59
  - - ">="
60
60
  - !ruby/object:Gem::Version
61
- version: '0'
61
+ version: '3.0'
62
62
  type: :development
63
63
  prerelease: false
64
64
  version_requirements: !ruby/object:Gem::Requirement
65
65
  requirements:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
- version: '0'
68
+ version: '3.0'
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: activesupport
71
71
  requirement: !ruby/object:Gem::Requirement
@@ -90,6 +90,7 @@ files:
90
90
  - ".gitignore"
91
91
  - ".rspec"
92
92
  - ".ruby-version"
93
+ - ".travis.yml"
93
94
  - CODE_OF_CONDUCT.md
94
95
  - Gemfile
95
96
  - LICENSE.txt
@@ -97,12 +98,19 @@ files:
97
98
  - Rakefile
98
99
  - bin/console
99
100
  - bin/setup
101
+ - gemfiles/activesupport-4.0
102
+ - gemfiles/activesupport-4.1
103
+ - gemfiles/activesupport-4.2
104
+ - gemfiles/activesupport-5.0
105
+ - gemfiles/activesupport-5.1
100
106
  - lib/seven.rb
101
107
  - lib/seven/abilities.rb
102
108
  - lib/seven/error.rb
103
109
  - lib/seven/manager.rb
110
+ - lib/seven/memory_store.rb
104
111
  - lib/seven/rails.rb
105
112
  - lib/seven/rails/controller_helpers.rb
113
+ - lib/seven/redis_store.rb
106
114
  - lib/seven/rspec.rb
107
115
  - lib/seven/version.rb
108
116
  - lib/sevencan.rb