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
@@ -0,0 +1,52 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
require "policy/cli"
|
3
|
+
|
4
|
+
describe Policy::CLI::Attribute do
|
5
|
+
|
6
|
+
subject { described_class.new "FooBar{bar/baz}" }
|
7
|
+
|
8
|
+
describe "#name" do
|
9
|
+
|
10
|
+
context "when the name is present in the source" do
|
11
|
+
|
12
|
+
it "returns a name for a method" do
|
13
|
+
expect(subject.name).to eq "foo_bar"
|
14
|
+
end
|
15
|
+
|
16
|
+
end # context
|
17
|
+
|
18
|
+
context "when the name is absent in the source" do
|
19
|
+
|
20
|
+
subject { described_class.new "{bar/baz}" }
|
21
|
+
|
22
|
+
it "returns @todo" do
|
23
|
+
expect(subject.name).to eq "@todo"
|
24
|
+
end
|
25
|
+
|
26
|
+
end # context
|
27
|
+
|
28
|
+
end # describe #name
|
29
|
+
|
30
|
+
describe "#type" do
|
31
|
+
|
32
|
+
context "when the type is present in the source" do
|
33
|
+
|
34
|
+
it "returns a name for a constant" do
|
35
|
+
expect(subject.type).to eq "Bar::Baz"
|
36
|
+
end
|
37
|
+
|
38
|
+
end # context
|
39
|
+
|
40
|
+
context "when the type is absent in the source" do
|
41
|
+
|
42
|
+
subject { described_class.new "FooBar" }
|
43
|
+
|
44
|
+
it "returns @todo" do
|
45
|
+
expect(subject.type).to eq "@todo"
|
46
|
+
end
|
47
|
+
|
48
|
+
end # context
|
49
|
+
|
50
|
+
end # describe #type
|
51
|
+
|
52
|
+
end # describe Policy::CLI::Attribute
|
@@ -50,28 +50,6 @@ describe Policy::CLI, :sandbox, :capture do
|
|
50
50
|
|
51
51
|
end # context
|
52
52
|
|
53
|
-
context "foo -a bar baz" do
|
54
|
-
|
55
|
-
let(:options) { %w(foo -a bar baz) }
|
56
|
-
before { subject }
|
57
|
-
|
58
|
-
it_behaves_like "adding a policy", folder: "policies"
|
59
|
-
it_behaves_like "adding a spec", folder: "policies"
|
60
|
-
it_behaves_like "adding locales", %w(en ru), folder: "policies"
|
61
|
-
|
62
|
-
it "adds attributes to the policy" do
|
63
|
-
content = read_in_sandbox "app/policies/foo.rb"
|
64
|
-
expect(content).to include "class Foo < Hexx::Policy.new(:bar, :baz)"
|
65
|
-
end
|
66
|
-
|
67
|
-
it "checks attributes" do
|
68
|
-
content = read_in_sandbox "spec/tests/policies/foo_spec.rb"
|
69
|
-
expect(content).to include "let(:bar) {"
|
70
|
-
expect(content).to include "let(:baz) {"
|
71
|
-
end
|
72
|
-
|
73
|
-
end # context
|
74
|
-
|
75
53
|
context "foo -n bar/baz" do
|
76
54
|
|
77
55
|
let(:options) { %w(foo -n bar/baz) }
|
@@ -101,6 +79,29 @@ describe Policy::CLI, :sandbox, :capture do
|
|
101
79
|
|
102
80
|
end # context
|
103
81
|
|
82
|
+
context "foo -a bar baz" do
|
83
|
+
|
84
|
+
let(:options) { %w(foo -a bar baz) }
|
85
|
+
before { subject }
|
86
|
+
|
87
|
+
it_behaves_like "adding a policy", folder: "policies"
|
88
|
+
it_behaves_like "adding a spec", folder: "policies"
|
89
|
+
it_behaves_like "adding locales", %w(en ru), folder: "policies"
|
90
|
+
|
91
|
+
it "adds attributes to the policy" do
|
92
|
+
content = read_in_sandbox "app/policies/foo.rb"
|
93
|
+
expect(content).to include "attr_reader :bar"
|
94
|
+
expect(content).to include "attr_reader :baz"
|
95
|
+
end
|
96
|
+
|
97
|
+
it "checks attributes" do
|
98
|
+
content = read_in_sandbox "spec/tests/policies/foo_spec.rb"
|
99
|
+
expect(content).to include "let(:bar) {"
|
100
|
+
expect(content).to include "let(:baz) {"
|
101
|
+
end
|
102
|
+
|
103
|
+
end # context
|
104
|
+
|
104
105
|
context "foo -n bar baz" do
|
105
106
|
|
106
107
|
let(:options) { %w(foo -n bar baz) }
|
@@ -112,9 +113,9 @@ describe Policy::CLI, :sandbox, :capture do
|
|
112
113
|
|
113
114
|
end # context
|
114
115
|
|
115
|
-
context "foo -l
|
116
|
+
context "foo -l UA jP" do
|
116
117
|
|
117
|
-
let(:options) { %w(foo -l
|
118
|
+
let(:options) { %w(foo -l UA jP) }
|
118
119
|
before { subject }
|
119
120
|
|
120
121
|
it_behaves_like "adding a policy", folder: "policies"
|
@@ -0,0 +1,51 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe Policy::Follower::NameError do
|
4
|
+
|
5
|
+
let(:follower) { double :follower }
|
6
|
+
let(:name) { "foo" }
|
7
|
+
subject { described_class.new follower, name }
|
8
|
+
|
9
|
+
describe ".new" do
|
10
|
+
|
11
|
+
it "creates the RuntimeError" do
|
12
|
+
expect(subject).to be_kind_of ::RuntimeError
|
13
|
+
end
|
14
|
+
|
15
|
+
end # describe .new
|
16
|
+
|
17
|
+
describe "#follower" do
|
18
|
+
|
19
|
+
it "is initialized" do
|
20
|
+
expect(subject.follower).to eq follower
|
21
|
+
end
|
22
|
+
|
23
|
+
end # describe #follower
|
24
|
+
|
25
|
+
describe "#name" do
|
26
|
+
|
27
|
+
it "is initialized" do
|
28
|
+
expect(subject.name).to eq name.to_sym
|
29
|
+
end
|
30
|
+
|
31
|
+
end # describe #follower
|
32
|
+
|
33
|
+
describe "#message" do
|
34
|
+
|
35
|
+
it "returns a correct string" do
|
36
|
+
expect(subject.message)
|
37
|
+
.to eq "#{ follower.inspect } hasn't registered the policy \"foo\""
|
38
|
+
end
|
39
|
+
|
40
|
+
end # describe #message
|
41
|
+
|
42
|
+
describe "#inspect" do
|
43
|
+
|
44
|
+
it "returns a correct string" do
|
45
|
+
expect(subject.inspect)
|
46
|
+
.to eq "#<#{ described_class.name }: #{ subject.message }>"
|
47
|
+
end
|
48
|
+
|
49
|
+
end # describe #inspect
|
50
|
+
|
51
|
+
end # describe Policy::Follower::NameError
|
@@ -0,0 +1,156 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe Policy::Follower::Policies do
|
4
|
+
|
5
|
+
let(:follower) { double :follower }
|
6
|
+
subject { described_class.new follower }
|
7
|
+
|
8
|
+
describe ".new" do
|
9
|
+
|
10
|
+
it "creates the collection" do
|
11
|
+
expect(subject).to be_kind_of Enumerable
|
12
|
+
end
|
13
|
+
|
14
|
+
end # describe .new
|
15
|
+
|
16
|
+
describe "#follower" do
|
17
|
+
|
18
|
+
it "is initialized" do
|
19
|
+
expect(subject.follower).to eq follower
|
20
|
+
end
|
21
|
+
|
22
|
+
end # describe #follower
|
23
|
+
|
24
|
+
describe "#add" do
|
25
|
+
|
26
|
+
it "registers the item" do
|
27
|
+
expect { subject.add "foo" }
|
28
|
+
.to change { subject.map { |item| item } }
|
29
|
+
.from([]).to([:foo])
|
30
|
+
end
|
31
|
+
|
32
|
+
it "registers the item once" do
|
33
|
+
["foo", :foo, "foo"].each(&subject.method(:add))
|
34
|
+
|
35
|
+
expect(subject.map { |item| item }).to eq [:foo]
|
36
|
+
end
|
37
|
+
|
38
|
+
it "returns itself" do
|
39
|
+
expect(subject.add "foo").to eq subject
|
40
|
+
end
|
41
|
+
|
42
|
+
end # describe #add
|
43
|
+
|
44
|
+
describe "#include?" do
|
45
|
+
|
46
|
+
context "registered name" do
|
47
|
+
|
48
|
+
before { subject.add "foo" }
|
49
|
+
|
50
|
+
it "returns true" do
|
51
|
+
expect(subject.include? :foo).to eq true
|
52
|
+
end
|
53
|
+
|
54
|
+
end # context
|
55
|
+
|
56
|
+
context "unregistered name" do
|
57
|
+
|
58
|
+
it "fails" do
|
59
|
+
expect { subject.include? :foo }
|
60
|
+
.to raise_error Policy::Follower::NameError
|
61
|
+
end
|
62
|
+
|
63
|
+
it "preserves name and follower in the exception" do
|
64
|
+
begin
|
65
|
+
subject.include? :foo
|
66
|
+
rescue => err
|
67
|
+
expect(err.name).to eq :foo
|
68
|
+
expect(err.follower).to eq subject.follower
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end # context
|
73
|
+
|
74
|
+
end # describe #include?
|
75
|
+
|
76
|
+
describe "#subset" do
|
77
|
+
|
78
|
+
subject { described_class.new follower }
|
79
|
+
before { %i(foo bar baz).each(&subject.method(:add)) }
|
80
|
+
|
81
|
+
let(:subset) { subject.subset(names) }
|
82
|
+
|
83
|
+
context "without names" do
|
84
|
+
|
85
|
+
it "returns itself" do
|
86
|
+
expect(subject.subset []).to eq subject
|
87
|
+
end
|
88
|
+
|
89
|
+
end # context
|
90
|
+
|
91
|
+
context "with registered names" do
|
92
|
+
|
93
|
+
let(:names) { %i(baz foo) }
|
94
|
+
|
95
|
+
it "creates a collection" do
|
96
|
+
expect(subset).to be_kind_of described_class
|
97
|
+
end
|
98
|
+
|
99
|
+
it "preserves a follower" do
|
100
|
+
expect(subset.follower).to eq subject.follower
|
101
|
+
end
|
102
|
+
|
103
|
+
it "leaves selected names only" do
|
104
|
+
expect(subset.map { |item| item }).to eq names
|
105
|
+
end
|
106
|
+
|
107
|
+
end # context
|
108
|
+
|
109
|
+
context "with repetitive names" do
|
110
|
+
|
111
|
+
let(:names) { ["baz", :baz, "baz"] }
|
112
|
+
|
113
|
+
it "leaves uniq names only" do
|
114
|
+
expect(subset.map { |item| item }).to eq [:baz]
|
115
|
+
end
|
116
|
+
|
117
|
+
end # context
|
118
|
+
|
119
|
+
context "with non-registered names" do
|
120
|
+
|
121
|
+
let(:names) { %i(baz foo cad) }
|
122
|
+
|
123
|
+
it "fails" do
|
124
|
+
expect { subset }.to raise_error Policy::Follower::NameError
|
125
|
+
end
|
126
|
+
|
127
|
+
it "adds follower and name to the error" do
|
128
|
+
begin
|
129
|
+
subset
|
130
|
+
rescue => err
|
131
|
+
expect(err.name).to eq :cad
|
132
|
+
expect(err.follower).to eq subject.follower
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
end # context
|
137
|
+
|
138
|
+
end # describe #subset
|
139
|
+
|
140
|
+
describe "#each" do
|
141
|
+
|
142
|
+
it "retuns enumerator" do
|
143
|
+
expect(subject.each).to be_kind_of Enumerator
|
144
|
+
end
|
145
|
+
|
146
|
+
it "iterates through names" do
|
147
|
+
names = %i(foo bar baz)
|
148
|
+
subject = described_class.new follower
|
149
|
+
names.each(&subject.method(:add))
|
150
|
+
|
151
|
+
expect(subject.map { |item| item }).to eq names
|
152
|
+
end
|
153
|
+
|
154
|
+
end # describe #each
|
155
|
+
|
156
|
+
end # describe Policy::Follower::Policies
|
@@ -0,0 +1,60 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe Policy::Follower::ViolationError do
|
4
|
+
|
5
|
+
let(:errors) { double :errors }
|
6
|
+
let(:follower) { double :follower }
|
7
|
+
let(:policy) { double :policy, errors: errors }
|
8
|
+
subject { described_class.new follower, policy }
|
9
|
+
|
10
|
+
describe ".new" do
|
11
|
+
|
12
|
+
it "creates the RuntimeError" do
|
13
|
+
expect(subject).to be_kind_of ::RuntimeError
|
14
|
+
end
|
15
|
+
|
16
|
+
end # describe .new
|
17
|
+
|
18
|
+
describe "#follower" do
|
19
|
+
|
20
|
+
it "is initialized" do
|
21
|
+
expect(subject.follower).to eq follower
|
22
|
+
end
|
23
|
+
|
24
|
+
end # describe #follower
|
25
|
+
|
26
|
+
describe "#policy" do
|
27
|
+
|
28
|
+
it "is initialized" do
|
29
|
+
expect(subject.policy).to eq policy
|
30
|
+
end
|
31
|
+
|
32
|
+
end # describe #policy
|
33
|
+
|
34
|
+
describe "#errors" do
|
35
|
+
|
36
|
+
it "is initialized" do
|
37
|
+
expect(subject.errors).to eq errors
|
38
|
+
end
|
39
|
+
|
40
|
+
end # describe #errors
|
41
|
+
|
42
|
+
describe "#message" do
|
43
|
+
|
44
|
+
it "returns a correct string" do
|
45
|
+
expect(subject.message)
|
46
|
+
.to eq "#{ follower.inspect } violates the policy #{ policy }"
|
47
|
+
end
|
48
|
+
|
49
|
+
end # describe #message
|
50
|
+
|
51
|
+
describe "#inspect" do
|
52
|
+
|
53
|
+
it "returns a correct string" do
|
54
|
+
expect(subject.inspect)
|
55
|
+
.to eq "#<#{ described_class.name }: #{ subject.message }>"
|
56
|
+
end
|
57
|
+
|
58
|
+
end # describe #inspect
|
59
|
+
|
60
|
+
end # describe Policy::Follower::NameError
|
@@ -0,0 +1,153 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe Policy::Follower do
|
4
|
+
|
5
|
+
let(:test_class) { Class.new.send(:include, described_class) }
|
6
|
+
subject { test_class.new }
|
7
|
+
|
8
|
+
describe ".policies" do
|
9
|
+
|
10
|
+
it "is a collection of policies" do
|
11
|
+
expect(test_class.policies).to be_kind_of Policy::Follower::Policies
|
12
|
+
end
|
13
|
+
|
14
|
+
it "refers to itself" do
|
15
|
+
expect(test_class.policies.follower).to eq test_class
|
16
|
+
end
|
17
|
+
|
18
|
+
end # describe .policies
|
19
|
+
|
20
|
+
describe ".follows_policy" do
|
21
|
+
|
22
|
+
it "adds a name to the collection of policies" do
|
23
|
+
expect(test_class.policies).to receive(:add).with("foo")
|
24
|
+
test_class.follows_policy "foo"
|
25
|
+
end
|
26
|
+
|
27
|
+
it "returns the name of the method" do
|
28
|
+
expect(test_class.follows_policy "foo").to eq :follows_policy
|
29
|
+
end
|
30
|
+
|
31
|
+
end # describe .follows_policy
|
32
|
+
|
33
|
+
describe ".follows_policies" do
|
34
|
+
|
35
|
+
it "adds all names to the collection of policies" do
|
36
|
+
expect(test_class.policies).to receive(:add).with("foo").ordered
|
37
|
+
expect(test_class.policies).to receive(:add).with("bar").ordered
|
38
|
+
test_class.follows_policies "foo", "bar"
|
39
|
+
end
|
40
|
+
|
41
|
+
it "returns the name of the method" do
|
42
|
+
expect(test_class.follows_policies "foo").to eq :follows_policies
|
43
|
+
end
|
44
|
+
|
45
|
+
end # describe .follows_policies
|
46
|
+
|
47
|
+
describe "#follow_policies?" do
|
48
|
+
|
49
|
+
before { test_class.follows_policies :one, :two }
|
50
|
+
|
51
|
+
context "with valid policies" do
|
52
|
+
|
53
|
+
let(:one) { double valid?: true }
|
54
|
+
before { allow(subject).to receive(:one) { one } }
|
55
|
+
|
56
|
+
it "returns true" do
|
57
|
+
expect(subject.follow_policies? :one).to eq true
|
58
|
+
end
|
59
|
+
|
60
|
+
end # context
|
61
|
+
|
62
|
+
context "with invalid policy's name" do
|
63
|
+
|
64
|
+
let(:one) { double valid?: false, errors: [] }
|
65
|
+
before { allow(subject).to receive(:one) { one } }
|
66
|
+
|
67
|
+
it "raises ViolationError" do
|
68
|
+
expect { subject.follow_policies? :one }
|
69
|
+
.to raise_error Policy::Follower::ViolationError
|
70
|
+
end
|
71
|
+
|
72
|
+
it "stores both the follower and policy in the exception" do
|
73
|
+
begin
|
74
|
+
subject.follow_policies? :one
|
75
|
+
rescue => exception
|
76
|
+
expect(exception.follower).to eq subject
|
77
|
+
expect(exception.policy).to eq one
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
end # context
|
82
|
+
|
83
|
+
context "with undefined name" do
|
84
|
+
|
85
|
+
it "raises NoMethodError" do
|
86
|
+
expect { subject.follow_policies? :one }
|
87
|
+
.to raise_error NoMethodError
|
88
|
+
end
|
89
|
+
|
90
|
+
end # context
|
91
|
+
|
92
|
+
context "with unregistered name" do
|
93
|
+
|
94
|
+
it "raises NameError" do
|
95
|
+
expect { subject.follow_policies? :three }
|
96
|
+
.to raise_error Policy::Follower::NameError
|
97
|
+
end
|
98
|
+
|
99
|
+
it "stores both the follower class and name in the exception" do
|
100
|
+
begin
|
101
|
+
subject.follow_policies? :three
|
102
|
+
rescue => exception
|
103
|
+
expect(exception.follower).to eq test_class
|
104
|
+
expect(exception.name).to eq :three
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
end # context
|
109
|
+
|
110
|
+
context "with a list of names" do
|
111
|
+
|
112
|
+
let(:one) { double valid?: true }
|
113
|
+
let(:two) { double valid?: true }
|
114
|
+
|
115
|
+
before { allow(subject).to receive(:one) { one } }
|
116
|
+
before { allow(subject).to receive(:two) { two } }
|
117
|
+
|
118
|
+
it "checks listed policies in given order" do
|
119
|
+
expect(two).to receive(:valid?).ordered
|
120
|
+
expect(one).to receive(:valid?).ordered
|
121
|
+
subject.follow_policies? :two, :one
|
122
|
+
end
|
123
|
+
|
124
|
+
end # context
|
125
|
+
|
126
|
+
context "without names" do
|
127
|
+
|
128
|
+
let(:one) { double valid?: true }
|
129
|
+
let(:two) { double valid?: true }
|
130
|
+
|
131
|
+
before { allow(subject).to receive(:one) { one } }
|
132
|
+
before { allow(subject).to receive(:two) { two } }
|
133
|
+
|
134
|
+
it "checks listed policies in predefined order" do
|
135
|
+
expect(one).to receive(:valid?).ordered
|
136
|
+
expect(two).to receive(:valid?).ordered
|
137
|
+
subject.follow_policies? []
|
138
|
+
end
|
139
|
+
|
140
|
+
end # context
|
141
|
+
|
142
|
+
end # describe #follow_policies?
|
143
|
+
|
144
|
+
describe "#follow_policy?" do
|
145
|
+
|
146
|
+
it "is an alias for #follow_policies?" do
|
147
|
+
expect(subject).to receive(:follow_policies?).with "foo"
|
148
|
+
subject.follow_policy? "foo"
|
149
|
+
end
|
150
|
+
|
151
|
+
end # describe #follow_policy?
|
152
|
+
|
153
|
+
end # describe Policy::Follower
|