policy 1.2.0 → 2.0.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 +4 -4
- data/Guardfile +4 -4
- data/README.md +177 -78
- data/config/metrics/flay.yml +1 -1
- data/config/metrics/roodi.yml +2 -2
- data/lib/policy.rb +52 -33
- data/lib/policy/base.rb +122 -0
- data/lib/policy/base/and.rb +38 -0
- data/lib/policy/base/negator.rb +52 -0
- data/lib/policy/base/node.rb +59 -0
- data/lib/policy/base/not.rb +42 -0
- data/lib/policy/base/or.rb +39 -0
- data/lib/policy/base/xor.rb +39 -0
- data/lib/policy/cli.rb +8 -3
- data/lib/policy/cli/attribute.rb +49 -0
- data/lib/policy/cli/locale.erb +1 -2
- data/lib/policy/cli/policy.erb +33 -6
- data/lib/policy/cli/spec.erb +31 -11
- data/lib/policy/follower.rb +54 -94
- data/lib/policy/follower/name_error.rb +53 -0
- data/lib/policy/follower/policies.rb +104 -0
- data/lib/policy/follower/violation_error.rb +60 -0
- data/lib/policy/version.rb +2 -2
- data/policy.gemspec +2 -3
- data/spec/support/composer.rb +28 -0
- data/spec/tests/lib/policy/base/and_spec.rb +62 -0
- data/spec/tests/lib/policy/base/negator_spec.rb +49 -0
- data/spec/tests/lib/policy/base/not_spec.rb +50 -0
- data/spec/tests/lib/policy/base/or_spec.rb +62 -0
- data/spec/tests/lib/policy/base/xor_spec.rb +73 -0
- data/spec/tests/lib/policy/base_spec.rb +123 -0
- data/spec/tests/lib/policy/cli/attribute_spec.rb +52 -0
- data/spec/tests/{policy → lib/policy}/cli_spec.rb +25 -24
- data/spec/tests/lib/policy/follower/name_error_spec.rb +51 -0
- data/spec/tests/lib/policy/follower/policies_spec.rb +156 -0
- data/spec/tests/lib/policy/follower/violation_error_spec.rb +60 -0
- data/spec/tests/lib/policy/follower_spec.rb +153 -0
- data/spec/tests/lib/policy_spec.rb +52 -0
- metadata +43 -44
- data/lib/policy/follower/followed_policies.rb +0 -45
- data/lib/policy/follower/followed_policy.rb +0 -104
- data/lib/policy/follower/names.rb +0 -29
- data/lib/policy/interface.rb +0 -48
- data/lib/policy/validations.rb +0 -28
- data/lib/policy/violation_error.rb +0 -52
- data/spec/features/follower_spec.rb +0 -95
- data/spec/tests/policy/follower/followed_policies_spec.rb +0 -87
- data/spec/tests/policy/follower/followed_policy_spec.rb +0 -117
- data/spec/tests/policy/follower/names_spec.rb +0 -19
- data/spec/tests/policy/follower_spec.rb +0 -220
- data/spec/tests/policy/interface_spec.rb +0 -83
- data/spec/tests/policy/validations_spec.rb +0 -13
- data/spec/tests/policy/violation_error_spec.rb +0 -75
- data/spec/tests/policy_spec.rb +0 -35
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: dfc9740968e7733a76015392220aff44505d85e3
|
4
|
+
data.tar.gz: bbfb0d67cbbff03aa344d1c8f2ba69dc3fd4f16d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 811e7374d0b8c41cec609dd9adb42eb5cedde61e391bed53cdd6cd4b1e2c72674c5207a3d9cfe85fac99b28aa52ead898fc737625629dd07cdd8c3f6ba8bf4dc
|
7
|
+
data.tar.gz: c75267c516e6da655b80980ca2437878c858d33b45ac9520bccdfea981e0de2cb96e80327e612c8fbcd6bebfc8b05eedb170628deaf09c62f778267dda2228af
|
data/Guardfile
CHANGED
@@ -6,12 +6,12 @@ guard :rspec, cmd: "bundle exec rspec" do
|
|
6
6
|
|
7
7
|
watch("lib/policy.rb") { "spec" }
|
8
8
|
|
9
|
-
watch(
|
9
|
+
watch(%r{^lib/policy/cli/}) { "spec/tests/policy/cli_spec.rb" }
|
10
10
|
|
11
|
-
watch(/^lib(.+)\.rb$/) do |m|
|
12
|
-
"spec/tests
|
11
|
+
watch(/^(bin|lib)\/(.+)\.rb$/) do |m|
|
12
|
+
"spec/tests/#{ m[1] }/#{ m[2] }_spec.rb"
|
13
13
|
end
|
14
14
|
|
15
|
-
watch(
|
15
|
+
watch(%r{^spec/tests/.+_spec\.rb$})
|
16
16
|
|
17
17
|
end # guard :rspec
|
data/README.md
CHANGED
@@ -17,28 +17,36 @@ Policy
|
|
17
17
|
|
18
18
|
A tiny library to implement a **Policy Object pattern**.
|
19
19
|
|
20
|
+
**NOTE** the gem was re-written from scratch in v2.0.0 (see Changelog section below)
|
21
|
+
|
22
|
+
Introduction
|
23
|
+
------------
|
24
|
+
|
20
25
|
The gem was inspired by:
|
21
|
-
* the CodeClimate's blog post "[7 ways to decompose fat ActiveRecord module]"
|
22
|
-
* the
|
26
|
+
* the CodeClimate's blog post "[7 ways to decompose fat ActiveRecord module]".
|
27
|
+
* the Chapter 10 of the book "[Domain-Driven Design]" by Eric Evans.
|
28
|
+
|
29
|
+
A **Policy Object** (assertion, invariant) encapsulates a business rule in isolation from objects (such as entities or services) following it.
|
23
30
|
|
24
|
-
|
31
|
+
Policy Objects can be combined by logical operators `and`, `or`, `xor`, `not` to provide complex policies.
|
25
32
|
|
26
|
-
This
|
33
|
+
This approach gives a number of benefits:
|
27
34
|
|
28
35
|
* It makes business rules **explicit** instead of spreading and hiding them inside application objects.
|
29
|
-
* It
|
30
|
-
* It
|
31
|
-
* It makes complex rules **testable** in isolation from
|
36
|
+
* It allows definition of rules for **numerous attributes** at once that should correspond to each other in some way.
|
37
|
+
* It makes the rules **simple** and **reusable** in various context and combinations.
|
38
|
+
* It makes complex rules **testable** in isolation from their parts.
|
32
39
|
|
33
40
|
[7 ways to decompose fat ActiveRecord module]: http://blog.codeclimate.com/blog/2012/10/17/7-ways-to-decompose-fat-activerecord-models/
|
34
41
|
[Domain-Driven Design]: http://www.amazon.com/dp/B00794TAUG/
|
35
42
|
|
36
|
-
|
43
|
+
Installation
|
44
|
+
------------
|
37
45
|
|
38
46
|
Add this line to your application's Gemfile:
|
39
47
|
|
40
48
|
```ruby
|
41
|
-
gem "policy"
|
49
|
+
gem "policy", ">= 1.0"
|
42
50
|
```
|
43
51
|
|
44
52
|
And then execute:
|
@@ -53,39 +61,43 @@ Or install it yourself as:
|
|
53
61
|
$ gem install policy
|
54
62
|
```
|
55
63
|
|
56
|
-
|
64
|
+
Usage
|
65
|
+
-----
|
57
66
|
|
58
|
-
|
67
|
+
### The Model for Illustration
|
59
68
|
|
60
69
|
Suppose an over-simplified model of bank account transactions and account-to-account transfers.
|
61
70
|
|
62
71
|
```ruby
|
63
|
-
# The account
|
64
|
-
class
|
72
|
+
# The account has a limit
|
73
|
+
class Account < Struct.new(:owner, :limit); end
|
74
|
+
|
75
|
+
# The transaction belongs to account and has a sum (< 0 for withdrawals)
|
76
|
+
class Transaction < Struct.new(:account, :sum); end
|
65
77
|
|
66
78
|
# The transfer, connecting two separate transactions
|
67
|
-
# (maybe this isn't an optimal model, but helpful for the subject)
|
68
79
|
class Transfer < Struct.new(:withdrawal, :enrollment); end
|
69
80
|
```
|
70
81
|
|
71
|
-
What we need is to apply
|
82
|
+
What we need is to apply set of policies:
|
72
83
|
|
73
84
|
**The sum of withdrawal's and enrollment's sums should be 0.**
|
85
|
+
**The sum of withdrawal doesn't exceed the accounts' limit.**
|
86
|
+
**The sum of transfers between client's own accounts can exceed the limit.**
|
74
87
|
|
75
88
|
Let's do it with Policy Objects!
|
76
89
|
|
77
|
-
|
78
|
-
|
79
|
-
Define policies with a list of necessary attributes like using [Struct].
|
90
|
+
### Policy Declaration
|
80
91
|
|
81
|
-
Tnen use [ActiveModel::Validations] methods to describe its rules:
|
92
|
+
Define policies with `Policy::Base` module included. Tnen use [ActiveModel::Validations] methods to describe its rules:
|
82
93
|
|
83
94
|
```ruby
|
84
95
|
# An arbitrary namespace for financial policies
|
85
|
-
module Policies
|
96
|
+
module Policies
|
86
97
|
|
87
98
|
# Withdrawal from one account should be equal to enrollment to another
|
88
|
-
class Consistency <
|
99
|
+
class Consistency < Struct.new(:withdrawal, :enrollment)
|
100
|
+
include Policy::Base
|
89
101
|
|
90
102
|
validates :withdrawal, :enrollment, presence: true
|
91
103
|
validates :total_sum, numericality: { equal_to: 0 }
|
@@ -96,119 +108,204 @@ module Policies::Financial
|
|
96
108
|
withdrawal.sum + enrollment.sum
|
97
109
|
end
|
98
110
|
end
|
99
|
-
end
|
100
|
-
```
|
101
111
|
|
102
|
-
|
112
|
+
# The sum of withdrawal doesn't exceed the accounts' limit
|
113
|
+
class Limited < Struct.new(:withdrawal)
|
114
|
+
include Policy::Base
|
103
115
|
|
104
|
-
|
116
|
+
validate :not_exceeds_the_limit
|
105
117
|
|
106
|
-
|
118
|
+
private
|
107
119
|
|
108
|
-
|
109
|
-
|
110
|
-
|
120
|
+
def not_exceeds_the_limit
|
121
|
+
return if withdrawal.sum + withdrawal.limit > 0
|
122
|
+
errors.add :base, :exceeds_the_limit
|
123
|
+
end
|
124
|
+
end
|
111
125
|
|
112
|
-
|
126
|
+
# The transfer is made between client's own accounts
|
127
|
+
class InternalTransfer < Struct.new(:withdrawal, :enrollment)
|
128
|
+
include Policy::Base
|
113
129
|
|
114
|
-
|
115
|
-
|
130
|
+
validate :the_same_client
|
131
|
+
|
132
|
+
private
|
133
|
+
|
134
|
+
def the_same_client
|
135
|
+
return if withdrawal.account.owner == enrollment.account.owner
|
136
|
+
errors.add :base, :different_owners
|
137
|
+
end
|
138
|
+
end
|
139
|
+
|
140
|
+
end
|
116
141
|
```
|
117
142
|
|
118
143
|
[Struct]: http://ruby-doc.org//core-2.2.0/Struct.html
|
119
144
|
[ActiveModel::Validations]: http://apidock.com/rails/ActiveModel/Validations
|
120
145
|
|
121
|
-
|
146
|
+
### Combining Policies
|
122
147
|
|
123
|
-
|
148
|
+
Use `and`, `or`, `xor` instance methods to provide complex policies from elementary ones.
|
149
|
+
|
150
|
+
You can use factory methods:
|
124
151
|
|
125
152
|
```ruby
|
126
|
-
|
127
|
-
|
153
|
+
module Policies
|
154
|
+
|
155
|
+
module LimitedOrInternal
|
156
|
+
def self.new(withdrawal, enrollment)
|
157
|
+
InternalTransfer.new(withdrawal, enrollment).or Limited.new(withdrawal)
|
158
|
+
end
|
159
|
+
end
|
128
160
|
|
129
|
-
follow_policy Policies::Financial::Consistency, :withdrawal, :enrollment
|
130
161
|
end
|
131
162
|
```
|
132
163
|
|
133
|
-
|
134
|
-
|
135
|
-
You can swap attributes (this is ok for our example)...
|
164
|
+
As an alternative to instance methods, use the `Policy` module's methods:
|
136
165
|
|
137
166
|
```ruby
|
138
|
-
|
167
|
+
def self.new(withdrawal, enrollment)
|
168
|
+
Policy.or(
|
169
|
+
InternalTransfer.new(withdrawal, enrollment),
|
170
|
+
Limited.new(withdrawal)
|
171
|
+
)
|
172
|
+
end
|
139
173
|
```
|
140
174
|
|
141
|
-
|
175
|
+
To provide negation use `and.not`, `or.not`, `xor.not` syntax:
|
142
176
|
|
143
177
|
```ruby
|
144
|
-
|
178
|
+
first_policy.and.not(second_policy, third_policy)
|
179
|
+
|
180
|
+
# this is equal to:
|
181
|
+
Policy.and(first_policy, Policy.not(second_policy), Policy.not(third_policy))
|
145
182
|
```
|
146
183
|
|
147
|
-
|
184
|
+
Policies can composed at any number of levels.
|
185
|
+
|
186
|
+
### Following Policies
|
187
|
+
|
188
|
+
Include the `Policy::Follower` module to the policies follower class.
|
189
|
+
|
190
|
+
Use the **class** method `.follows_policies` to declare policies (like ActiveModel::Validations `.validate` method does).
|
148
191
|
|
149
192
|
```ruby
|
150
|
-
|
151
|
-
|
193
|
+
class Transfer < Struct.new(:withdrawal, :enrollment)
|
194
|
+
include Policy::Follower
|
195
|
+
|
196
|
+
follows_policies :consistent, :limited_or_internal
|
197
|
+
|
198
|
+
private
|
199
|
+
|
200
|
+
def consistent
|
201
|
+
Policies::Consistency.new(withdrawal, enrollment)
|
202
|
+
end
|
203
|
+
|
204
|
+
def limited_or_internal
|
205
|
+
Policies::LimitedOrInternal.new(withdrawal, enrollment)
|
206
|
+
end
|
207
|
+
|
152
208
|
end
|
153
209
|
```
|
154
210
|
|
155
|
-
|
156
|
-
|
157
|
-
To verify object use `#follow_policies?` or `#follow_policies!` **instance** methods.
|
211
|
+
Surely, you can skip creating `LimitedOrInternal` builder and combine policies for current class only:
|
158
212
|
|
159
213
|
```ruby
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
transfer = Transfer.new withdrawal, enrollment
|
214
|
+
def limited_or_internal
|
215
|
+
limited.or internal
|
216
|
+
end
|
165
217
|
|
166
|
-
|
167
|
-
|
218
|
+
def limited
|
219
|
+
Policies::Limited.new(withdrawal)
|
220
|
+
end
|
168
221
|
|
169
|
-
|
170
|
-
|
222
|
+
def internal
|
223
|
+
Policies::Internal.new(withdrawal, enrollment)
|
224
|
+
end
|
171
225
|
```
|
172
226
|
|
173
|
-
|
227
|
+
[builder]: http://sourcemaking.com/design_patterns/builder
|
174
228
|
|
175
|
-
###
|
229
|
+
### Checking Policies
|
176
230
|
|
177
|
-
|
231
|
+
Use the **instance** method `follow_policies?` to check whether an instance follows policies.
|
232
|
+
|
233
|
+
The method checks all policies and **raises** the `Policy::ViolationError` when the first followed policy is broken.
|
178
234
|
|
179
235
|
```ruby
|
180
|
-
|
181
|
-
|
236
|
+
transfer = Transfer.new(
|
237
|
+
Transaction.new(Account.new("Alice", 50), -100),
|
238
|
+
Transaction.new(Account.new("Bob", 50), 100)
|
239
|
+
)
|
182
240
|
|
183
|
-
|
184
|
-
|
185
|
-
end
|
186
|
-
end
|
241
|
+
transfer.follow_policies?
|
242
|
+
# => <Policy::ViolationError ... > because Alice's limit of 50 is exceeded
|
187
243
|
```
|
188
244
|
|
189
|
-
|
245
|
+
The method doesn't mutate the follower. It collects errors inside the exception `#errors` method, not the follower's one.
|
190
246
|
|
191
247
|
```ruby
|
192
|
-
|
193
|
-
transfer.
|
194
|
-
|
248
|
+
begin
|
249
|
+
transfer.follow_policies?
|
250
|
+
rescue ViolationError => err
|
251
|
+
err.errors
|
252
|
+
end
|
195
253
|
```
|
196
254
|
|
197
|
-
|
255
|
+
You can check subset of policies by calling the method with policy names:
|
198
256
|
|
199
257
|
```ruby
|
200
|
-
|
258
|
+
transfer.follow_policies? :consistent
|
259
|
+
# passes because the transfer is consistent: -100 + 100 = 0
|
260
|
+
# this doesn't check the :limited_or_internal policy
|
261
|
+
```
|
262
|
+
|
263
|
+
The method ignores policies, not declared by `.follows_policies` class method.
|
264
|
+
|
265
|
+
The method has singular alias `follow_policy?(name)` that accepts one argument.
|
266
|
+
|
267
|
+
Scaffolding
|
268
|
+
-----------
|
269
|
+
|
270
|
+
You can scaffold the policy with its specification and necessary translations using the generator:
|
271
|
+
|
201
272
|
```
|
273
|
+
policy new consistency -n policies financial -a withdrawal enrollment -l fr de
|
274
|
+
```
|
275
|
+
|
276
|
+
For a list of available options call the generator with an `-h` option:
|
277
|
+
|
278
|
+
```
|
279
|
+
policy new -h
|
280
|
+
```
|
281
|
+
|
282
|
+
Changelog
|
283
|
+
---------
|
284
|
+
|
285
|
+
Version 2 was redesigned and rewritten from scratch. The main changes are:
|
286
|
+
|
287
|
+
* Instead of building policy with a `Policy.new` method, it is now created by including the `Policy::Base` module.
|
288
|
+
|
289
|
+
In the previous version building a policy was needed to define an order of policy attributes. Now the definition of policy attributes is not the responsibility of the gem.
|
290
|
+
|
291
|
+
* Instead of generating policies in a class scope (in the ActiveModel `validates` style), the `.follows_policy` refers to followers' instance methods (in the ActiveModel `validate` style).
|
292
|
+
|
293
|
+
This allows combining policy objects with logical expressions. Policies themselves becames more DRY, granular and testable in isolation.
|
294
|
+
|
295
|
+
* Instead of mutating the follower, `follow_policy?` method raises an exception.
|
296
|
+
|
297
|
+
This allows follower to be immutable (frozen). The follower doesn't need to be messed with `ActiveModule::Validations` at all.
|
202
298
|
|
203
|
-
|
299
|
+
This approach makes `follow_policy!` method unnecessary.
|
204
300
|
|
205
|
-
|
301
|
+
Compatibility
|
302
|
+
-------------
|
206
303
|
|
207
304
|
Tested under rubies, compatible with MRI 2.0+:
|
208
305
|
|
209
306
|
* MRI rubies 2.0+
|
210
307
|
* Rubinius 2+ (2.0+ mode)
|
211
|
-
* JRuby
|
308
|
+
* JRuby 9000 (2.0+ mode)
|
212
309
|
|
213
310
|
Rubies with API 1.9 are not supported.
|
214
311
|
|
@@ -220,7 +317,8 @@ Uses [RSpec] 3.0+ for testing and [hexx-suit] for dev/test tools collection.
|
|
220
317
|
[hexx-suit]: https://github.com/nepalez/hexx-suit/
|
221
318
|
[ActiveModel::Validations]: http://apidock.com/rails/v3.1.0/ActiveModel/Validations
|
222
319
|
|
223
|
-
|
320
|
+
Contributing
|
321
|
+
------------
|
224
322
|
|
225
323
|
* Fork the project.
|
226
324
|
* Read the [STYLEGUIDE](config/metrics/STYLEGUIDE).
|
@@ -232,6 +330,7 @@ Uses [RSpec] 3.0+ for testing and [hexx-suit] for dev/test tools collection.
|
|
232
330
|
in a commit by itself I can ignore when I pull)
|
233
331
|
* Send me a pull request. Bonus points for topic branches.
|
234
332
|
|
235
|
-
|
333
|
+
License
|
334
|
+
-------
|
236
335
|
|
237
336
|
See [MIT LICENSE](LICENSE).
|
data/config/metrics/flay.yml
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
---
|
2
|
-
minimum_score:
|
2
|
+
minimum_score: 9
|
data/config/metrics/roodi.yml
CHANGED
@@ -7,9 +7,9 @@ ClassNameCheck:
|
|
7
7
|
pattern: !ruby/regexp /^[A-Z][a-zA-Z0-9]*$/
|
8
8
|
ClassVariableCheck:
|
9
9
|
CyclomaticComplexityBlockCheck:
|
10
|
-
complexity:
|
10
|
+
complexity: 3
|
11
11
|
CyclomaticComplexityMethodCheck:
|
12
|
-
complexity:
|
12
|
+
complexity: 3
|
13
13
|
EmptyRescueBodyCheck:
|
14
14
|
ForLoopCheck:
|
15
15
|
MethodLineCountCheck:
|
data/lib/policy.rb
CHANGED
@@ -1,40 +1,59 @@
|
|
1
1
|
# encoding: utf-8
|
2
2
|
|
3
|
-
require "
|
3
|
+
require "active_model"
|
4
4
|
|
5
|
-
|
6
|
-
|
7
|
-
|
5
|
+
require_relative "policy/version"
|
6
|
+
|
7
|
+
require_relative "policy/base"
|
8
|
+
require_relative "policy/base/node"
|
9
|
+
require_relative "policy/base/and"
|
10
|
+
require_relative "policy/base/or"
|
11
|
+
require_relative "policy/base/xor"
|
12
|
+
require_relative "policy/base/not"
|
13
|
+
require_relative "policy/base/negator"
|
14
|
+
|
15
|
+
require_relative "policy/follower"
|
16
|
+
require_relative "policy/follower/name_error"
|
17
|
+
require_relative "policy/follower/policies"
|
18
|
+
require_relative "policy/follower/violation_error"
|
19
|
+
|
20
|
+
# The namespace for the code of the 'policy' gem
|
8
21
|
module Policy
|
9
22
|
|
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
|
-
|
23
|
+
# Builds a composite policy by applying method AND to policies
|
24
|
+
#
|
25
|
+
# @param [Policy::Base, Array<Policy::Base>] policies
|
26
|
+
#
|
27
|
+
# @return [Policy::Base]
|
28
|
+
def self.and(*policies)
|
29
|
+
Base::And.new(*policies)
|
30
|
+
end
|
31
|
+
|
32
|
+
# Builds a composite policy by applying method OR to policies
|
33
|
+
#
|
34
|
+
# @param [Policy::Base, Array<Policy::Base>] policies
|
35
|
+
#
|
36
|
+
# @return [Policy::Base]
|
37
|
+
def self.or(*policies)
|
38
|
+
Base::Or.new(*policies)
|
39
|
+
end
|
40
|
+
|
41
|
+
# Builds a composite policy by applying method XOR to policies
|
42
|
+
#
|
43
|
+
# @param [Policy::Base, Array<Policy::Base>] policies
|
44
|
+
#
|
45
|
+
# @return [Policy::Base]
|
46
|
+
def self.xor(*policies)
|
47
|
+
Base::Xor.new(*policies)
|
48
|
+
end
|
49
|
+
|
50
|
+
# Builds the negation of policy
|
51
|
+
#
|
52
|
+
# @param [Policy::Base] policy
|
53
|
+
#
|
54
|
+
# @return [Policy::Base]
|
55
|
+
def self.not(policy)
|
56
|
+
Base::Not.new(policy)
|
57
|
+
end
|
39
58
|
|
40
59
|
end # module Policy
|