mongoid_ability 3.0.1 → 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 +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
|