policy 1.2.0 → 2.0.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
data/lib/policy/cli/locale.erb
CHANGED
data/lib/policy/cli/policy.erb
CHANGED
@@ -16,18 +16,45 @@
|
|
16
16
|
|
17
17
|
<%= " " * tabs %># Implements the following policy (invariant):
|
18
18
|
<%= " " * tabs %>#
|
19
|
-
<%= " " * tabs %># @todo
|
19
|
+
<%= " " * tabs %># @todo Describe the policy
|
20
20
|
<%= " " * tabs %>#
|
21
21
|
<%= " " * tabs %># @example
|
22
|
-
<%= " " * tabs %># <%= policy.type %>.new(
|
22
|
+
<%= " " * tabs %># <%= project.type %>::<%= policy.type %>.new(
|
23
|
+
<% attributes.each do |item| -%>
|
24
|
+
<%= " " * tabs %># <%= item.name %>: @todo,
|
25
|
+
<% end -%>
|
26
|
+
<%= " " * tabs %># )
|
23
27
|
<%= " " * tabs %>#
|
28
|
+
<%= " " * tabs %>class <%= policy.const %>
|
29
|
+
<%= " " * tabs %> include Policy::Base
|
30
|
+
|
31
|
+
<%= " " * tabs %> # @!scope class
|
32
|
+
<%= " " * tabs %> # @!method new(attributes)
|
33
|
+
<%= " " * tabs %> # Creates the policy object with a list of attributes
|
34
|
+
<%= " " * tabs %> #
|
35
|
+
<%= " " * tabs %> # @param [Hash] attributes
|
36
|
+
<%= " " * tabs %> #
|
37
|
+
<%= " " * tabs %> # @return [<%= project.type %>::<%= policy.type %>]
|
38
|
+
<% if attributes.any? -%>
|
39
|
+
|
40
|
+
<%= " " * tabs %> # @private
|
41
|
+
<%= " " * tabs %> def initialize(<%= attributes.map { |item| "#{ item.name }:" }.join(", ") -%>)
|
24
42
|
<% attributes.each do |item| -%>
|
25
|
-
<%= " " * tabs
|
26
|
-
|
43
|
+
<%= " " * tabs %> @<%= item.name %> = <%= item.name %>
|
44
|
+
<% end -%>
|
45
|
+
<%= " " * tabs %> end
|
46
|
+
<% attributes.each do |item| -%>
|
47
|
+
|
48
|
+
<%= " " * tabs %> # # @!attribute [r] <%= item.name %>
|
49
|
+
<%= " " * tabs %> # # @todo Describe the attribute
|
50
|
+
<%= " " * tabs %> # #
|
51
|
+
<%= " " * tabs %> # # @return [<%= item.type %>]
|
52
|
+
<%= " " * tabs %> # attr_reader :<%= item.name %>
|
53
|
+
<% end -%>
|
27
54
|
<% end -%>
|
28
|
-
<%= " " * tabs %>class <%= policy.const %> < Hexx::Policy.new(<%= attributes.map { |item| ":#{ item }" }.join(", ") %>)
|
29
55
|
|
30
|
-
<%= " " * tabs %> # Define necessary validations using `validates`
|
56
|
+
<%= " " * tabs %> # Define necessary validations using either `validates`
|
57
|
+
<%= " " * tabs %> # or `validate` declarations.
|
31
58
|
|
32
59
|
<%= " " * tabs %>end # class <%= policy.const %>
|
33
60
|
<% policy.namespaces.reverse.each do |item| -%>
|
data/lib/policy/cli/spec.erb
CHANGED
@@ -7,24 +7,44 @@ describe <%= project.type %>::<%= policy.type %> do
|
|
7
7
|
|
8
8
|
# default attributes for a valid policy
|
9
9
|
<% attributes.each do |item| -%>
|
10
|
-
# let(:<%= item %>) { @todo }
|
10
|
+
# let(:<%= item.name %>) { @todo }
|
11
11
|
<% end -%>
|
12
12
|
|
13
|
-
subject
|
13
|
+
subject do
|
14
|
+
described_class.new(
|
15
|
+
<% attributes.each do |item| -%>
|
16
|
+
<%= item.name %>: <%= item.name %>,
|
17
|
+
<% end -%>
|
18
|
+
)
|
19
|
+
end
|
20
|
+
<% attributes.each do |item| -%>
|
14
21
|
|
15
|
-
|
22
|
+
describe "#<%= item.name %>" do
|
16
23
|
|
17
|
-
it
|
24
|
+
it "is initialized" do
|
25
|
+
expect(subject.<%= item.name %>).to eq <%= item.name %>
|
26
|
+
end
|
18
27
|
|
19
|
-
end #
|
20
|
-
<%
|
28
|
+
end # describe #<%= item.name %>
|
29
|
+
<% end -%>
|
21
30
|
|
22
|
-
|
31
|
+
describe "#valid?" do
|
23
32
|
|
24
|
-
|
25
|
-
it { is_expected.not_to be_valid }
|
33
|
+
context "when @todo: describe the context" do
|
26
34
|
|
27
|
-
|
28
|
-
|
35
|
+
it { is_expected.to be_valid }
|
36
|
+
|
37
|
+
end # context
|
38
|
+
<% attributes.each do |item| -%>
|
39
|
+
|
40
|
+
context "when the #<%= item.name %> is @todo" do
|
41
|
+
|
42
|
+
# before { allow(subject).to receive(:<%= item.name %>) { @todo } }
|
43
|
+
it { is_expected.to be_invalid }
|
44
|
+
|
45
|
+
end # context
|
46
|
+
<% end -%>
|
47
|
+
|
48
|
+
end # describe #valid?
|
29
49
|
|
30
50
|
end # describe <%= policy.type %>
|
data/lib/policy/follower.rb
CHANGED
@@ -2,140 +2,100 @@
|
|
2
2
|
|
3
3
|
module Policy
|
4
4
|
|
5
|
-
#
|
5
|
+
# Interface for the class that follows policies
|
6
6
|
module Follower
|
7
7
|
|
8
|
-
|
9
|
-
require_relative "follower/followed_policy"
|
10
|
-
require_relative "follower/followed_policies"
|
11
|
-
|
12
|
-
# Methods to be added to the class the module is included to
|
13
|
-
#
|
14
|
-
# @private
|
8
|
+
# Methods to be added to the follower class
|
15
9
|
module ClassMethods
|
16
10
|
|
17
|
-
#
|
18
|
-
# The collection of policies to be followed by instances of the class
|
19
|
-
#
|
20
|
-
# @return [Policy::Follower::FollowedPolicies]
|
21
|
-
#
|
22
|
-
# @private
|
23
|
-
def followed_policies
|
24
|
-
@followed_policies ||= FollowedPolicies.new
|
25
|
-
end
|
26
|
-
|
27
|
-
# Adds a policy to the list of {#followed_policies}
|
11
|
+
# Declares a policy to be followed
|
28
12
|
#
|
29
|
-
#
|
30
|
-
# the policy object klass
|
31
|
-
# @param [Array<#to_sym>] attributes
|
32
|
-
# the list of attributes of the instance the policy should be applied to
|
13
|
+
# Mutates the object by adding an item to its {#policies}.
|
33
14
|
#
|
34
|
-
# @
|
35
|
-
# the name for the policy to be used for selecting it
|
36
|
-
# in {#follow_policies!} and {#follow_policies?} methods
|
15
|
+
# @param [Symbol] name
|
37
16
|
#
|
38
|
-
# @return [
|
39
|
-
def
|
40
|
-
|
41
|
-
|
17
|
+
# @return [:follows_policy] the name of the method
|
18
|
+
def follows_policy(name)
|
19
|
+
policies.add name
|
20
|
+
|
21
|
+
:follows_policy
|
42
22
|
end
|
43
23
|
|
44
|
-
#
|
24
|
+
# Declares policies to be followed
|
45
25
|
#
|
46
|
-
#
|
47
|
-
# use_policies Policies::Finances do
|
48
|
-
# apply_policy :TransferConstistency, :debet, :credit
|
49
|
-
# end
|
26
|
+
# Mutates the object by adding items to its {#policies}.
|
50
27
|
#
|
51
|
-
# @param [
|
28
|
+
# @param [Symbol, Array<Symbol>] names
|
52
29
|
#
|
53
|
-
# @
|
54
|
-
|
55
|
-
|
56
|
-
def use_policies(namespace, &block)
|
57
|
-
@__policies__ = namespace
|
58
|
-
instance_eval(&block)
|
59
|
-
ensure
|
60
|
-
@__policies__ = nil
|
61
|
-
end
|
30
|
+
# @return [:follows_policies] the name of the method
|
31
|
+
def follows_policies(*names)
|
32
|
+
names.each(&method(:follows_policy))
|
62
33
|
|
63
|
-
|
64
|
-
|
65
|
-
def __policies__
|
66
|
-
@__policies__ ||= self
|
34
|
+
:follows_policies
|
67
35
|
end
|
68
36
|
|
69
|
-
|
37
|
+
# @!attribute [r] policies
|
38
|
+
# The collection of followed policies
|
39
|
+
#
|
40
|
+
# @return [Policy::Follower::Policies]
|
41
|
+
def policies
|
42
|
+
@policies ||= Policies.new(self)
|
43
|
+
end
|
70
44
|
|
71
|
-
# Checks whether an instance meets selected policies
|
72
|
-
#
|
73
|
-
# Mutates the object by adding new #errors
|
74
|
-
#
|
75
|
-
# @param [Array<#to_sym>] names
|
76
|
-
# the ordered list of names to select policies by
|
77
|
-
# when not names selected all policies will be applied
|
78
|
-
#
|
79
|
-
# @raise [Policy::ViolationError]
|
80
|
-
# unless all selected policies has been met
|
81
|
-
#
|
82
|
-
# @return [undefined]
|
83
|
-
def follow_policies!(*names)
|
84
|
-
followed_policies.apply_to self, *names
|
85
|
-
rescue ViolationError => error
|
86
|
-
collect_errors_from(error)
|
87
|
-
raise
|
88
45
|
end
|
89
46
|
|
90
|
-
#
|
47
|
+
# Checks whether an object follows all its policies or subset of policies
|
91
48
|
#
|
92
|
-
# @
|
93
|
-
#
|
49
|
+
# @overload follow_policies?()
|
50
|
+
# Checks whether an object follows all registered policies
|
94
51
|
#
|
95
|
-
# @
|
52
|
+
# @overload follow_policies?(names)
|
53
|
+
# Checks whether an object follows given policies
|
96
54
|
#
|
97
|
-
#
|
98
|
-
def follow_policy!(name)
|
99
|
-
follow_policies! name
|
100
|
-
end
|
101
|
-
|
102
|
-
# Safely checks whether an instance meets selected policies
|
55
|
+
# @param [#to_sym, Array<#to_sym>] names
|
103
56
|
#
|
104
|
-
#
|
57
|
+
# @raise [Policy::Follower::NameError]
|
58
|
+
# if the policy is not registered by name
|
105
59
|
#
|
106
|
-
# @
|
60
|
+
# @raise [NoMethodError]
|
61
|
+
# if the name not implemented as follower's instance method
|
62
|
+
# @raise [ViolationError]
|
63
|
+
# if the policy is violated by the follower
|
107
64
|
#
|
108
|
-
# @return [
|
65
|
+
# @return [true] if no exception being raised
|
109
66
|
def follow_policies?(*names)
|
110
|
-
|
67
|
+
__policies__
|
68
|
+
.subset(names) # raises NameError
|
69
|
+
.map(&method(:__send__)) # raises NoMethodError
|
70
|
+
.each(&method(:__validate_policy__)) # raises ViolationError
|
111
71
|
true
|
112
|
-
rescue ViolationError
|
113
|
-
false
|
114
72
|
end
|
115
73
|
|
116
|
-
#
|
74
|
+
# Checks whether an object follows given policy
|
117
75
|
#
|
118
|
-
# @param
|
76
|
+
# @param [#to_sym] name
|
77
|
+
# the name of the policy to follow
|
78
|
+
#
|
79
|
+
# @raise (see #follow_policies?)
|
119
80
|
#
|
120
81
|
# @return (see #follow_policies?)
|
121
82
|
def follow_policy?(name)
|
122
83
|
follow_policies? name
|
123
84
|
end
|
124
85
|
|
125
|
-
private
|
126
|
-
|
127
|
-
# @!parse extend Policy::Follower::ClassMethods
|
128
|
-
# @!parse include ActiveModel::Validations
|
86
|
+
# @private
|
129
87
|
def self.included(klass)
|
130
|
-
klass.extend
|
88
|
+
klass.instance_eval { extend ClassMethods }
|
131
89
|
end
|
132
90
|
|
133
|
-
|
134
|
-
|
91
|
+
private
|
92
|
+
|
93
|
+
def __validate_policy__(policy)
|
94
|
+
fail ViolationError.new(self, policy) unless policy.valid?
|
135
95
|
end
|
136
96
|
|
137
|
-
def
|
138
|
-
|
97
|
+
def __policies__
|
98
|
+
self.class.policies
|
139
99
|
end
|
140
100
|
|
141
101
|
end # module Follower
|
@@ -0,0 +1,53 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Policy
|
4
|
+
|
5
|
+
module Follower
|
6
|
+
|
7
|
+
# An exception to be risen when a policy name is not followed by a follower
|
8
|
+
class NameError < RuntimeError
|
9
|
+
|
10
|
+
# @!scope class
|
11
|
+
# @!method new(follower, name)
|
12
|
+
# Constructs an exception for the follower class and the policy name
|
13
|
+
#
|
14
|
+
# @param [Class] follower
|
15
|
+
# @param [#to_sym] name
|
16
|
+
#
|
17
|
+
# @return [Policy::Follower::NameError]
|
18
|
+
def initialize(follower, name)
|
19
|
+
@follower = follower
|
20
|
+
@name = name.to_sym
|
21
|
+
end
|
22
|
+
|
23
|
+
# @!attribute [r] follower
|
24
|
+
# The follower class that doesn't follow given policy
|
25
|
+
#
|
26
|
+
# @return [Class]
|
27
|
+
attr_reader :follower
|
28
|
+
|
29
|
+
# @!attribute [r] name
|
30
|
+
# The name of the policy that is not followed by the follower
|
31
|
+
#
|
32
|
+
# @return [String]
|
33
|
+
attr_reader :name
|
34
|
+
|
35
|
+
# The human-readable exception message
|
36
|
+
#
|
37
|
+
# @return [String]
|
38
|
+
def message
|
39
|
+
"#{ follower.inspect } hasn't registered the policy \"#{ name }\""
|
40
|
+
end
|
41
|
+
|
42
|
+
# The human-readable description for the exception
|
43
|
+
#
|
44
|
+
# @return [String]
|
45
|
+
def inspect
|
46
|
+
"#<#{ self }: #{ message }>"
|
47
|
+
end
|
48
|
+
|
49
|
+
end # class NameError
|
50
|
+
|
51
|
+
end # module Follower
|
52
|
+
|
53
|
+
end # module Policy
|
@@ -0,0 +1,104 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Policy
|
4
|
+
|
5
|
+
module Follower
|
6
|
+
|
7
|
+
# Collection of policy names to be followed
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Policies
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
# @!scope class
|
14
|
+
# @!method new(follower)
|
15
|
+
# Creates the empty collection for the follower class
|
16
|
+
#
|
17
|
+
# @param [Class] follower
|
18
|
+
#
|
19
|
+
# @return [Policy::Follower::Policies] the collection object
|
20
|
+
def initialize(follower)
|
21
|
+
@follower = follower
|
22
|
+
@names = []
|
23
|
+
end
|
24
|
+
|
25
|
+
# Adds the policy name to the list of followed policies
|
26
|
+
#
|
27
|
+
# @example
|
28
|
+
# Policies.new.add("some_policy")
|
29
|
+
#
|
30
|
+
# @param [#to_sym] policy
|
31
|
+
#
|
32
|
+
# @return [self] itself
|
33
|
+
def add(policy)
|
34
|
+
name = policy.to_sym
|
35
|
+
names << name unless names.include? name
|
36
|
+
self
|
37
|
+
end
|
38
|
+
|
39
|
+
# Checks whether the name is included to the {#names}
|
40
|
+
#
|
41
|
+
# @param [Symbol] name
|
42
|
+
#
|
43
|
+
# @raise [Policy::Followers::NameError] if the name not registered yet
|
44
|
+
#
|
45
|
+
# @return [true]
|
46
|
+
def include?(name)
|
47
|
+
fail(NameError.new(follower, name)) unless names.include?(name)
|
48
|
+
true
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns the subset of current collection
|
52
|
+
#
|
53
|
+
# @overload subset()
|
54
|
+
# @return [self] itself
|
55
|
+
#
|
56
|
+
# @overload subset(*names)
|
57
|
+
# @example
|
58
|
+
# policies = Policies.new(follower, %i(foo bar baz))
|
59
|
+
# policies.subset(:baz, :foo)
|
60
|
+
# # => #<Policies @names=[:baz, :foo]>
|
61
|
+
#
|
62
|
+
# @param [Symbol, Array<Symbol>] names
|
63
|
+
#
|
64
|
+
# @raise [Policy::Follower::NameError]
|
65
|
+
# if subset contains unregistered policies
|
66
|
+
#
|
67
|
+
# @return [Policy::Follower::Policies]
|
68
|
+
def subset(*names)
|
69
|
+
return self unless names.flatten.any?
|
70
|
+
policies = self.class.new(follower)
|
71
|
+
valid(names).each(&policies.method(:add))
|
72
|
+
|
73
|
+
policies
|
74
|
+
end
|
75
|
+
|
76
|
+
# Iterates through names of the policies
|
77
|
+
#
|
78
|
+
# @return [Enumerator]
|
79
|
+
#
|
80
|
+
# @yieldparam [Symbol] the name of the registered policy
|
81
|
+
def each
|
82
|
+
return to_enum unless block_given?
|
83
|
+
names.each { |name| yield(name) }
|
84
|
+
end
|
85
|
+
|
86
|
+
# @!attribute [r] follower
|
87
|
+
# The class that follows policies
|
88
|
+
#
|
89
|
+
# @return [Class]
|
90
|
+
attr_reader :follower
|
91
|
+
|
92
|
+
private
|
93
|
+
|
94
|
+
attr_reader :names
|
95
|
+
|
96
|
+
def valid(names)
|
97
|
+
names.flatten.map(&:to_sym).select(&method(:include?))
|
98
|
+
end
|
99
|
+
|
100
|
+
end # class Policies
|
101
|
+
|
102
|
+
end # module Follower
|
103
|
+
|
104
|
+
end # module Policy
|