sevencan 0.1.8 → 0.2.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.
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