mongoid_ability 3.0.1 → 3.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/workflows/test.yml +26 -0
- data/.gitignore +2 -0
- data/.travis.yml +8 -1
- data/Appraisals +31 -0
- data/CHANGELOG.md +4 -0
- data/Rakefile +3 -2
- data/gemfiles/7_0.gemfile +7 -0
- data/gemfiles/7_1.gemfile +7 -0
- data/gemfiles/7_2.gemfile +7 -0
- data/gemfiles/7_3.gemfile +7 -0
- data/gemfiles/7_4.gemfile +7 -0
- data/gemfiles/7_5.gemfile +7 -0
- data/gemfiles/8_0.gemfile +7 -0
- data/gemfiles/8_1.gemfile +7 -0
- data/lib/cancancan/model_adapters/mongoid_adapter.rb +45 -41
- data/lib/cancancan/model_additions.rb +2 -0
- data/lib/mongoid_ability/ability.rb +3 -1
- data/lib/mongoid_ability/find_lock.rb +2 -2
- data/lib/mongoid_ability/lock.rb +62 -77
- data/lib/mongoid_ability/locks_decorator.rb +2 -0
- data/lib/mongoid_ability/owner.rb +2 -0
- data/lib/mongoid_ability/subject.rb +11 -1
- data/lib/mongoid_ability/version.rb +3 -1
- data/lib/mongoid_ability.rb +0 -2
- data/mongoid_ability.gemspec +4 -4
- data/test/cancancan/model_adapters/mongoid_adapter_options_test.rb +69 -68
- data/test/cancancan/model_adapters/mongoid_adapter_test.rb +83 -69
- data/test/mongoid_ability/ability_basic_benchmark.rb +2 -2
- data/test/mongoid_ability/ability_basic_test.rb +14 -14
- data/test/mongoid_ability/ability_marshal_test.rb +8 -3
- data/test/mongoid_ability/ability_options_test.rb +16 -16
- data/test/mongoid_ability/ability_test.rb +31 -35
- data/test/mongoid_ability/find_lock_test.rb +9 -9
- data/test/mongoid_ability/lock_test.rb +20 -30
- data/test/mongoid_ability/owner_locks_test.rb +4 -4
- data/test/mongoid_ability/owner_test.rb +6 -6
- data/test/mongoid_ability/subject_test.rb +13 -13
- data/test/support/test_classes/my_flat_subject.rb +9 -0
- data/test/test_helper.rb +7 -10
- metadata +29 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 275a88f165da3e4172f6665fa6b15f65e66a1dc57f6ac930176f58e3b4a32e42
|
4
|
+
data.tar.gz: 2997e27d3e00810dd7fe1f09e3a5212e867a77ace41945dba1c978b36c5b1b4d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: f8cb94a2215357b1d740741ed42fff8ea516b2d44bd348577fd16aedee21fb3f5dbac502562807e74f9bcbf573c9599ad129f532308da09380cd4402e679d208
|
7
|
+
data.tar.gz: e5992d7339082d50f151a4af5755a4e81bd1e634fe96860389550ff42010c3d87f2092a68d471b0350e942c3f883f170b395488bc12bd984259e45834de9ef91
|
@@ -0,0 +1,26 @@
|
|
1
|
+
name: Tests
|
2
|
+
on: [push, pull_request]
|
3
|
+
concurrency:
|
4
|
+
group: ${{ github.ref }}
|
5
|
+
cancel-in-progress: true
|
6
|
+
jobs:
|
7
|
+
test:
|
8
|
+
strategy:
|
9
|
+
fail-fast: false
|
10
|
+
matrix:
|
11
|
+
gemfile: ["7_0", "7_1", "7_2", "7_3", "7_4", "7_5", "8_0", "8_1"]
|
12
|
+
ruby: ["2.7", "3.0", "3.1"]
|
13
|
+
runs-on: ubuntu-latest
|
14
|
+
services:
|
15
|
+
mongodb:
|
16
|
+
image: mongo
|
17
|
+
ports: ["27017:27017"]
|
18
|
+
env:
|
19
|
+
BUNDLE_GEMFILE: ${{ github.workspace }}/gemfiles/${{ matrix.gemfile }}.gemfile
|
20
|
+
steps:
|
21
|
+
- uses: actions/checkout@v3
|
22
|
+
- uses: ruby/setup-ruby@v1
|
23
|
+
with:
|
24
|
+
ruby-version: ${{ matrix.ruby }}
|
25
|
+
bundler-cache: true
|
26
|
+
- run: bundle exec rake
|
data/.gitignore
CHANGED
data/.travis.yml
CHANGED
@@ -2,9 +2,16 @@ language: ruby
|
|
2
2
|
script: 'bundle exec rake'
|
3
3
|
sudo: false
|
4
4
|
rvm:
|
5
|
-
- 2.
|
5
|
+
- 2.6.6
|
6
|
+
- 2.7.6
|
7
|
+
- 3.0.4
|
8
|
+
- 3.1.2
|
6
9
|
services:
|
7
10
|
- mongodb
|
11
|
+
gemfile:
|
12
|
+
- gemfiles/7_0.gemfile
|
13
|
+
- gemfiles/7_1.gemfile
|
14
|
+
- gemfiles/7_2.gemfile
|
8
15
|
|
9
16
|
notifications:
|
10
17
|
email:
|
data/Appraisals
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
appraise "7-0" do
|
2
|
+
gem "mongoid", "~> 7.0.0"
|
3
|
+
end
|
4
|
+
|
5
|
+
appraise "7-1" do
|
6
|
+
gem "mongoid", "~> 7.1.0"
|
7
|
+
end
|
8
|
+
|
9
|
+
appraise "7-2" do
|
10
|
+
gem "mongoid", "~> 7.2.0"
|
11
|
+
end
|
12
|
+
|
13
|
+
appraise "7-3" do
|
14
|
+
gem "mongoid", "~> 7.3.0"
|
15
|
+
end
|
16
|
+
|
17
|
+
appraise "7-4" do
|
18
|
+
gem "mongoid", "~> 7.4.0"
|
19
|
+
end
|
20
|
+
|
21
|
+
appraise "7-5" do
|
22
|
+
gem "mongoid", "~> 7.5.0"
|
23
|
+
end
|
24
|
+
|
25
|
+
appraise "8-0" do
|
26
|
+
gem "mongoid", "~> 8.0.0"
|
27
|
+
end
|
28
|
+
|
29
|
+
appraise "8-1" do
|
30
|
+
gem "mongoid", "~> 8.1.0"
|
31
|
+
end
|
data/CHANGELOG.md
CHANGED
data/Rakefile
CHANGED
@@ -1,40 +1,44 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "set"
|
2
4
|
|
3
5
|
module CanCan
|
4
6
|
module ModelAdapters
|
5
7
|
class MongoidAdapter < AbstractAdapter
|
6
|
-
|
7
|
-
model_class
|
8
|
-
|
9
|
-
|
10
|
-
# Used to determine if this model adapter will override the matching behavior for a hash of conditions.
|
11
|
-
# If this returns true then matches_conditions_hash? will be called. See Rule#matches_conditions_hash
|
12
|
-
def self.override_conditions_hash_matching?(_subject, _conditions)
|
13
|
-
false
|
14
|
-
end
|
8
|
+
class << self
|
9
|
+
def for_class?(model_class)
|
10
|
+
model_class <= Mongoid::Document
|
11
|
+
end
|
15
12
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
13
|
+
# Used to determine if this model adapter will override the matching behavior for a hash of conditions.
|
14
|
+
# If this returns true then matches_conditions_hash? will be called. See Rule#matches_conditions_hash
|
15
|
+
def override_conditions_hash_matching?(_subject, _conditions)
|
16
|
+
false
|
17
|
+
end
|
20
18
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
end
|
19
|
+
# Override if override_conditions_hash_matching? returns true
|
20
|
+
def matches_conditions_hash?(_subject, _conditions)
|
21
|
+
raise NotImplemented, "This model adapter does not support matching on a conditions hash."
|
22
|
+
end
|
26
23
|
|
27
|
-
|
28
|
-
|
29
|
-
|
24
|
+
# Used to determine if this model adapter will override the matching behavior for a specific condition.
|
25
|
+
# If this returns true then matches_condition? will be called. See Rule#matches_conditions_hash
|
26
|
+
def override_condition_matching?(_subject, _name, _value)
|
27
|
+
true
|
28
|
+
end
|
30
29
|
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
30
|
+
# Override if override_condition_matching? returns true
|
31
|
+
def matches_condition?(subject, name, value)
|
32
|
+
attribute = subject.send(name)
|
33
|
+
|
34
|
+
case value
|
35
|
+
when Hash then hash_condition_match?(attribute, value)
|
36
|
+
when Range then value.cover?(attribute)
|
37
|
+
when Regexp then value.match(attribute)
|
38
|
+
when Array then value.include?(attribute)
|
39
|
+
when Enumerable then value.include?(attribute)
|
40
|
+
else attribute == value
|
41
|
+
end
|
38
42
|
end
|
39
43
|
end
|
40
44
|
|
@@ -75,7 +79,7 @@ module CanCan
|
|
75
79
|
rule.conditions.each do |key, value|
|
76
80
|
key = id_key if %i[id _id].include?(key.to_sym)
|
77
81
|
res << case value
|
78
|
-
when Array then { key => {
|
82
|
+
when Array then { key => { "$in" => value } }
|
79
83
|
else { key => value }
|
80
84
|
end
|
81
85
|
end
|
@@ -89,12 +93,12 @@ module CanCan
|
|
89
93
|
rule.conditions.each do |key, value|
|
90
94
|
key = id_key if %i[id _id].include?(key.to_sym)
|
91
95
|
case value
|
92
|
-
when Regexp then res << { key => {
|
96
|
+
when Regexp then res << { key => { "$not" => value } }
|
93
97
|
else
|
94
|
-
if prev_value = res.detect { |item| item.dig(key,
|
95
|
-
prev_value[key][
|
98
|
+
if prev_value = res.detect { |item| item.dig(key, "$nin") }
|
99
|
+
prev_value[key]["$nin"] += Array(value)
|
96
100
|
else
|
97
|
-
res << { key => {
|
101
|
+
res << { key => { "$nin" => Array(value) } }
|
98
102
|
end
|
99
103
|
end
|
100
104
|
end
|
@@ -117,13 +121,13 @@ module CanCan
|
|
117
121
|
def database_records
|
118
122
|
return @model_class.none unless has_any_conditions?
|
119
123
|
|
120
|
-
or_conditions = {
|
121
|
-
or_conditions =
|
124
|
+
or_conditions = { "$or" => [subject_type_conditions, *open_conditions].compact }
|
125
|
+
or_conditions = {} if or_conditions["$or"].empty?
|
122
126
|
|
123
|
-
and_conditions = {
|
124
|
-
and_conditions =
|
127
|
+
and_conditions = { "$and" => [*closed_conditions].compact }
|
128
|
+
and_conditions = {} if and_conditions["$and"].empty?
|
125
129
|
|
126
|
-
@model_class.where(and_conditions)
|
130
|
+
@model_class.where(or_conditions.merge(and_conditions))
|
127
131
|
end
|
128
132
|
|
129
133
|
private
|
@@ -147,11 +151,11 @@ module CanCan
|
|
147
151
|
end
|
148
152
|
|
149
153
|
def id_key
|
150
|
-
@id_key ||= [prefix,
|
154
|
+
@id_key ||= [prefix, "_id"].reject(&:blank?).join.to_sym
|
151
155
|
end
|
152
156
|
|
153
157
|
def type_key
|
154
|
-
@type_key ||= [prefix,
|
158
|
+
@type_key ||= [prefix, "_type"].reject(&:blank?).join.to_sym
|
155
159
|
end
|
156
160
|
end
|
157
161
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
require 'cancancan'
|
2
4
|
|
3
5
|
module MongoidAbility
|
@@ -17,7 +19,7 @@ module MongoidAbility
|
|
17
19
|
end
|
18
20
|
|
19
21
|
def self.subject_classes
|
20
|
-
|
22
|
+
ObjectSpace.each_object(Class).select do |cls|
|
21
23
|
cls.included_modules.include?(MongoidAbility::Subject)
|
22
24
|
end
|
23
25
|
end
|
@@ -1,3 +1,5 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
1
3
|
module MongoidAbility
|
2
4
|
# finds first lock that controls specified params
|
3
5
|
|
@@ -26,8 +28,6 @@ module MongoidAbility
|
|
26
28
|
subject_type.constantize
|
27
29
|
end
|
28
30
|
|
29
|
-
# ---------------------------------------------------------------------
|
30
|
-
|
31
31
|
class FindDefaultLock < FindLock
|
32
32
|
def call
|
33
33
|
locks = subject_class.default_locks.for_action(action)
|
data/lib/mongoid_ability/lock.rb
CHANGED
@@ -1,97 +1,82 @@
|
|
1
|
-
|
1
|
+
# frozen_string_literal: true
|
2
2
|
|
3
3
|
module MongoidAbility
|
4
4
|
module Lock
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
included do
|
8
|
+
field :action, type: Symbol, default: :read
|
9
|
+
field :outcome, type: Mongoid::Boolean, default: false
|
10
|
+
field :opts, as: :options, type: Hash, default: {}
|
11
|
+
|
12
|
+
belongs_to :subject, polymorphic: true, optional: true
|
13
|
+
# Mongoid 7 does not support `touch: true` on polymorphic associations
|
14
|
+
after_save -> { subject.touch if subject? }
|
15
|
+
after_destroy -> { subject.touch if subject? }
|
16
|
+
after_touch -> { subject.touch if subject? }
|
17
|
+
|
18
|
+
# TODO: validate that action is defined on subject or its superclasses
|
19
|
+
validates :action, presence: true, uniqueness: { scope: [:subject_type, :subject_id, :outcome] }
|
20
|
+
validates :outcome, presence: true
|
21
|
+
|
22
|
+
scope :for_action, ->(action) { where(action: action.to_sym) }
|
23
|
+
|
24
|
+
scope :for_subject_type, ->(subject_type) { where(subject_type: subject_type.to_s) }
|
25
|
+
scope :for_subject_types, ->(subject_types) { criteria.in(subject_type: subject_types) }
|
26
|
+
|
27
|
+
scope :for_subject_id, ->(subject_id) {
|
28
|
+
return where(subject_id: nil) unless subject_id.present?
|
29
|
+
where(subject_id: BSON::ObjectId.from_string(subject_id))
|
30
|
+
}
|
31
|
+
|
32
|
+
scope :for_subject, ->(subject) {
|
33
|
+
return where(subject_id: nil) unless subject.present?
|
34
|
+
where(subject_type: subject.class.model_name, subject_id: subject.id)
|
35
|
+
}
|
36
|
+
|
37
|
+
scope :class_locks, -> { where(subject_id: nil) }
|
38
|
+
scope :id_locks, -> { ne(subject_id: nil) }
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.sort
|
42
|
+
-> (a, b) {
|
43
|
+
[a.subject_type, a.subject_id.to_s, a.action, (a.outcome ? -1 : 1)] <=>
|
44
|
+
[b.subject_type, b.subject_id.to_s, b.action, (b.outcome ? -1 : 1)]
|
45
|
+
}
|
40
46
|
end
|
41
47
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
end
|
48
|
+
def class_lock?
|
49
|
+
!id_lock?
|
50
|
+
end
|
46
51
|
|
47
|
-
|
48
|
-
|
49
|
-
end
|
52
|
+
def id_lock?
|
53
|
+
subject_id.present?
|
50
54
|
end
|
51
55
|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
end
|
56
|
+
def open?
|
57
|
+
outcome
|
58
|
+
end
|
56
59
|
|
57
|
-
|
58
|
-
|
59
|
-
end
|
60
|
+
def closed?
|
61
|
+
!open?
|
60
62
|
end
|
61
63
|
|
62
64
|
# calculates outcome as if this lock is not present
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
cloned_ability = MongoidAbility::Ability.new(cloned_owner)
|
69
|
-
|
70
|
-
cloned_ability.can?(action, (subject.present? ? subject : subject_class), options)
|
71
|
-
end
|
72
|
-
end
|
65
|
+
def inherited_outcome(options = {})
|
66
|
+
return outcome unless owner.present?
|
67
|
+
cloned_owner = owner.clone
|
68
|
+
cloned_owner.locks_relation = cloned_owner.locks_relation - [self]
|
69
|
+
cloned_ability = MongoidAbility::Ability.new(cloned_owner)
|
73
70
|
|
74
|
-
|
75
|
-
def subject_class
|
76
|
-
subject_type.constantize
|
77
|
-
end
|
71
|
+
cloned_ability.can?(action, (subject.present? ? subject : subject_class), options)
|
78
72
|
end
|
79
73
|
|
80
|
-
|
81
|
-
|
82
|
-
[subject_type, subject_id, action, options]
|
83
|
-
end
|
74
|
+
def subject_class
|
75
|
+
subject_type.constantize
|
84
76
|
end
|
85
77
|
|
86
|
-
|
87
|
-
|
88
|
-
def sort
|
89
|
-
-> (a, b) {
|
90
|
-
[a.subject_type, a.subject_id.to_s, a.action, (a.outcome ? -1 : 1)] <=>
|
91
|
-
[b.subject_type, b.subject_id.to_s, b.action, (b.outcome ? -1 : 1)]
|
92
|
-
}
|
93
|
-
end
|
94
|
-
end
|
78
|
+
def group_key_for_calc
|
79
|
+
[subject_type, subject_id, action, options]
|
95
80
|
end
|
96
81
|
end
|
97
82
|
end
|
@@ -1,3 +1,11 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# TODO: rewrite as concern, use proc for the _type or consider either using
|
4
|
+
# discriminator_key or maybe even better add our own configurable discriminator
|
5
|
+
# key (.ability_subject_discriminator_key), to not rely on mongoid internals?
|
6
|
+
#
|
7
|
+
# https://github.com/mongodb/mongoid/blob/01ee33970c5e9cefe436528c56c32e843f474e9b/lib/mongoid/traversable.rb
|
8
|
+
|
1
9
|
module MongoidAbility
|
2
10
|
module Subject
|
3
11
|
def self.included(base)
|
@@ -6,7 +14,9 @@ module MongoidAbility
|
|
6
14
|
include Mongoid::Touchable::InstanceMethods
|
7
15
|
|
8
16
|
# always set the _type field as it is used by :accessible_by queries
|
9
|
-
|
17
|
+
# FIXME: removing this line should in theory break non SCI object, but
|
18
|
+
# it doesn't ...
|
19
|
+
# field :_type, type: String, default: model_name
|
10
20
|
end
|
11
21
|
end
|
12
22
|
|
data/lib/mongoid_ability.rb
CHANGED
@@ -15,8 +15,6 @@ require 'mongoid_ability/subject'
|
|
15
15
|
require 'mongoid_ability/locks_decorator'
|
16
16
|
require 'mongoid_ability/find_lock'
|
17
17
|
|
18
|
-
# ---------------------------------------------------------------------
|
19
|
-
|
20
18
|
if defined?(Rails)
|
21
19
|
class ActionController::Base
|
22
20
|
def current_ability
|
data/mongoid_ability.gemspec
CHANGED
@@ -19,13 +19,13 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ['lib']
|
20
20
|
|
21
21
|
spec.add_dependency 'cancancan', '~> 2.2'
|
22
|
-
spec.add_dependency 'mongoid'
|
22
|
+
spec.add_dependency 'mongoid'
|
23
23
|
|
24
|
+
spec.add_development_dependency 'appraisal'
|
24
25
|
spec.add_development_dependency 'bundler'
|
25
|
-
spec.add_development_dependency '
|
26
|
-
spec.add_development_dependency 'database_cleaner', '>= 1.5.1'
|
26
|
+
spec.add_development_dependency 'database_cleaner-mongoid'
|
27
27
|
spec.add_development_dependency 'guard'
|
28
28
|
spec.add_development_dependency 'guard-minitest'
|
29
29
|
spec.add_development_dependency 'minitest'
|
30
|
-
spec.add_development_dependency 'rake'
|
30
|
+
spec.add_development_dependency 'rake'
|
31
31
|
end
|