mongoid_ability 3.0.0 → 3.0.2
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 +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 +8 -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 +51 -42
- 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 -24
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
|
@@ -88,11 +92,15 @@ module CanCan
|
|
88
92
|
condition_rules.reject(&:base_behavior).each_with_object([]) do |rule, res|
|
89
93
|
rule.conditions.each do |key, value|
|
90
94
|
key = id_key if %i[id _id].include?(key.to_sym)
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
95
|
+
case value
|
96
|
+
when Regexp then res << { key => { "$not" => value } }
|
97
|
+
else
|
98
|
+
if prev_value = res.detect { |item| item.dig(key, "$nin") }
|
99
|
+
prev_value[key]["$nin"] += Array(value)
|
100
|
+
else
|
101
|
+
res << { key => { "$nin" => Array(value) } }
|
102
|
+
end
|
103
|
+
end
|
96
104
|
end
|
97
105
|
end
|
98
106
|
end
|
@@ -100,6 +108,7 @@ module CanCan
|
|
100
108
|
|
101
109
|
def subject_type_conditions
|
102
110
|
return unless open_subject_types.present?
|
111
|
+
|
103
112
|
{ :"#{type_key}".nin => closed_subject_types.map(&:to_s) }
|
104
113
|
end
|
105
114
|
|
@@ -112,13 +121,13 @@ module CanCan
|
|
112
121
|
def database_records
|
113
122
|
return @model_class.none unless has_any_conditions?
|
114
123
|
|
115
|
-
or_conditions = {
|
116
|
-
or_conditions =
|
124
|
+
or_conditions = { "$or" => [subject_type_conditions, *open_conditions].compact }
|
125
|
+
or_conditions = {} if or_conditions["$or"].empty?
|
117
126
|
|
118
|
-
and_conditions = {
|
119
|
-
and_conditions =
|
127
|
+
and_conditions = { "$and" => [*closed_conditions].compact }
|
128
|
+
and_conditions = {} if and_conditions["$and"].empty?
|
120
129
|
|
121
|
-
@model_class.where(and_conditions)
|
130
|
+
@model_class.where(or_conditions.merge(and_conditions))
|
122
131
|
end
|
123
132
|
|
124
133
|
private
|
@@ -142,11 +151,11 @@ module CanCan
|
|
142
151
|
end
|
143
152
|
|
144
153
|
def id_key
|
145
|
-
@id_key ||= [prefix,
|
154
|
+
@id_key ||= [prefix, "_id"].reject(&:blank?).join.to_sym
|
146
155
|
end
|
147
156
|
|
148
157
|
def type_key
|
149
|
-
@type_key ||= [prefix,
|
158
|
+
@type_key ||= [prefix, "_type"].reject(&:blank?).join.to_sym
|
150
159
|
end
|
151
160
|
end
|
152
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
|