policy 1.0.1

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.
Files changed (45) hide show
  1. checksums.yaml +7 -0
  2. data/.coveralls.yml +2 -0
  3. data/.metrics +5 -0
  4. data/.rspec +2 -0
  5. data/.rubocop.yml +2 -0
  6. data/.travis.yml +18 -0
  7. data/.yardopts +3 -0
  8. data/Gemfile +7 -0
  9. data/Guardfile +15 -0
  10. data/LICENSE +21 -0
  11. data/README.md +223 -0
  12. data/Rakefile +17 -0
  13. data/config/metrics/STYLEGUIDE +231 -0
  14. data/config/metrics/cane.yml +5 -0
  15. data/config/metrics/churn.yml +6 -0
  16. data/config/metrics/flay.yml +2 -0
  17. data/config/metrics/metric_fu.yml +14 -0
  18. data/config/metrics/pippi.yml +3 -0
  19. data/config/metrics/reek.yml +1 -0
  20. data/config/metrics/roodi.yml +24 -0
  21. data/config/metrics/rubocop.yml +87 -0
  22. data/config/metrics/saikuro.yml +3 -0
  23. data/config/metrics/simplecov.yml +5 -0
  24. data/config/metrics/yardstick.yml +37 -0
  25. data/lib/policy/follower/followed_policies.rb +45 -0
  26. data/lib/policy/follower/followed_policy.rb +104 -0
  27. data/lib/policy/follower/names.rb +29 -0
  28. data/lib/policy/follower.rb +143 -0
  29. data/lib/policy/interface.rb +48 -0
  30. data/lib/policy/validations.rb +28 -0
  31. data/lib/policy/version.rb +9 -0
  32. data/lib/policy/violation_error.rb +52 -0
  33. data/lib/policy.rb +40 -0
  34. data/policy.gemspec +23 -0
  35. data/spec/features/follower_spec.rb +95 -0
  36. data/spec/spec_helper.rb +10 -0
  37. data/spec/tests/policy/follower/followed_policies_spec.rb +87 -0
  38. data/spec/tests/policy/follower/followed_policy_spec.rb +117 -0
  39. data/spec/tests/policy/follower/names_spec.rb +19 -0
  40. data/spec/tests/policy/follower_spec.rb +220 -0
  41. data/spec/tests/policy/interface_spec.rb +83 -0
  42. data/spec/tests/policy/validations_spec.rb +13 -0
  43. data/spec/tests/policy/violation_error_spec.rb +75 -0
  44. data/spec/tests/policy_spec.rb +35 -0
  45. metadata +142 -0
@@ -0,0 +1,95 @@
1
+ # encoding: utf-8
2
+
3
+ # The integration test for policy follower
4
+ describe Policy::Follower do
5
+
6
+ before do
7
+
8
+ Transaction = Class.new Struct.new(:sum)
9
+
10
+ module Policies
11
+
12
+ class Consistency < Policy.new(:debet, :credit)
13
+
14
+ validates :debet, :credit, presence: true
15
+ validates :sum, numericality: { equal_to: 0 }, allow_nil: true
16
+
17
+ private
18
+
19
+ def sum
20
+ debet && credit && (credit.sum + debet.sum)
21
+ end
22
+
23
+ end # class Consistency
24
+
25
+ end # module Policy
26
+
27
+ # The follower class to be tested
28
+ class Transfer < Struct.new(:withdrawal, :enrollment)
29
+ include Policy::Follower
30
+
31
+ use_policies Policies do
32
+ follow_policy :Consistency, :withdrawal, :enrollment, as: :consistency
33
+ end
34
+
35
+ end # class Transfer
36
+
37
+ end # before
38
+
39
+ let(:withdrawal) { Transaction.new(-100) }
40
+ subject { Transfer.new withdrawal, enrollment }
41
+
42
+ describe "#follow_policies!" do
43
+
44
+ context "when policy is met" do
45
+
46
+ let(:enrollment) { Transaction.new(100) }
47
+
48
+ it "passes if the policy is met" do
49
+ expect { subject.follow_policies! }.not_to raise_error
50
+ end
51
+
52
+ end # context
53
+
54
+ context "when policy is broken" do
55
+
56
+ let(:enrollment) { Transaction.new(200) }
57
+
58
+ it "fails if the policy is broken" do
59
+ expect { subject.follow_policies! }.to raise_error
60
+ end
61
+
62
+ end # context
63
+
64
+ end # describe #follow_policies!
65
+
66
+ describe "#follow_policies?" do
67
+
68
+ context "when policy is met" do
69
+
70
+ let(:enrollment) { Transaction.new(100) }
71
+
72
+ it "returns true if the policy is met" do
73
+ expect(subject).to be_follow_policies
74
+ end
75
+
76
+ end # context
77
+
78
+ context "when policy is broken" do
79
+
80
+ let(:enrollment) { Transaction.new(200) }
81
+
82
+ it "returns false if the policy is broken" do
83
+ expect(subject).not_to be_follow_policies
84
+ end
85
+
86
+ end # context
87
+
88
+ end # describe #follow_policies!
89
+
90
+ after do
91
+ %i(Transaction Transfer Policies)
92
+ .each { |const| Object.__send__(:remove_const, const) }
93
+ end
94
+
95
+ end # describe Policy::Follower
@@ -0,0 +1,10 @@
1
+ # encoding: utf-8
2
+
3
+ # Loads the RSpec test suit.
4
+ require "hexx-suit"
5
+
6
+ # Loads runtime metrics in the current scope
7
+ Hexx::Suit.load_metrics_for(self)
8
+
9
+ # Loads the code of the module.
10
+ require "policy"
@@ -0,0 +1,87 @@
1
+ # encoding: utf-8
2
+ require "ostruct"
3
+
4
+ describe Policy::Follower::FollowedPolicies do
5
+
6
+ it "is a hash" do
7
+ expect(subject).to be_kind_of Hash
8
+ end
9
+
10
+ describe "#add" do
11
+
12
+ let(:policy) { double :policy, name: :foo }
13
+
14
+ it "registers a policy" do
15
+ expect { subject.add policy }.to change { subject }.to(foo: policy)
16
+ end
17
+
18
+ end # describe #add
19
+
20
+ describe "#apply_to" do
21
+
22
+ let(:follower) { double }
23
+
24
+ before do
25
+ %i(first second third).each do |item|
26
+ subject.add double(name: item, apply_to: nil)
27
+ end
28
+ end
29
+
30
+ shared_examples "applying policies" do |applied_policies = nil|
31
+
32
+ before { applied_policies ||= subject.keys }
33
+ let(:skipped_policies) { subject.keys - applied_policies }
34
+
35
+ it "[applies policies]" do
36
+ policies = applied_policies.map(&subject.method(:[]))
37
+
38
+ policies.each do |policy|
39
+ expect(policy).to receive(:apply_to).with(follower).ordered
40
+ end
41
+ end
42
+
43
+ it "[skips policies]" do
44
+ policies = skipped_policies.map(&subject.method(:[]))
45
+
46
+ policies.each do |policy|
47
+ expect(policy).not_to receive(:apply_to)
48
+ end
49
+ end
50
+
51
+ end # shared examples
52
+
53
+ context "by default" do
54
+
55
+ after { subject.apply_to follower }
56
+
57
+ it_behaves_like "applying policies"
58
+
59
+ end # context
60
+
61
+ context "with a list of names" do
62
+
63
+ after { subject.apply_to follower, :third, :first }
64
+
65
+ it_behaves_like "applying policies", %i(third first)
66
+
67
+ end # context
68
+
69
+ context "with a repetitive names" do
70
+
71
+ after { subject.apply_to follower, :third, :third, :third }
72
+
73
+ it_behaves_like "applying policies", %i(third third third)
74
+
75
+ end # context
76
+
77
+ context "with an array of names" do
78
+
79
+ after { subject.apply_to follower, %w(third first forth) }
80
+
81
+ it_behaves_like "applying policies", %i(third first)
82
+
83
+ end # context
84
+
85
+ end # describe #apply_to
86
+
87
+ end # describe Policy::Follower::FollowedPolicies
@@ -0,0 +1,117 @@
1
+ # encoding: utf-8
2
+ require "ostruct"
3
+
4
+ describe Policy::Follower::FollowedPolicy do
5
+
6
+ before { module Namespace; class Policy < ::Policy.new(:foo, :bar); end; end }
7
+ after { Object.send :remove_const, :Namespace }
8
+
9
+ let(:namespace) { Namespace }
10
+ let(:policy_class) { Namespace::Policy }
11
+
12
+ describe ".new" do
13
+
14
+ shared_examples "refusing wrong number of attributes" do |*list|
15
+
16
+ subject { described_class.new nil, policy_class, :policy, *list }
17
+
18
+ it "raises ArgumentError" do
19
+ expect { subject }.to raise_error(ArgumentError)
20
+ end
21
+
22
+ it "sets a proper message for the exception" do
23
+ begin
24
+ subject
25
+ rescue => err
26
+ expect(err.message).to eq(
27
+ "#{ policy_class } requires 2 attribute(s)." \
28
+ " #{ list } cannot be assigned."
29
+ )
30
+ end
31
+ end
32
+
33
+ end # shared examples
34
+
35
+ it_behaves_like "refusing wrong number of attributes", :foo
36
+ it_behaves_like "refusing wrong number of attributes", :foo, :bar, :baz
37
+
38
+ end # describe .new
39
+
40
+ describe "#name" do
41
+
42
+ context "when name is set to nil" do
43
+
44
+ subject { described_class.new nil, policy_class, nil, :foo, :bar }
45
+
46
+ it "assigns uuid" do
47
+ expect(subject.name.to_s).to match(/^\h{8}-(\h{4}-){3}\h{12}$/)
48
+ end
49
+
50
+ it "is a symbol" do
51
+ expect(subject.name).to be_kind_of Symbol
52
+ end
53
+
54
+ end # context
55
+
56
+ context "when name is set explicitly" do
57
+
58
+ subject { described_class.new nil, policy_class, "policy", :foo, :bar }
59
+
60
+ it "returns the symbolized name" do
61
+ expect(subject.name).to eq :policy
62
+ end
63
+
64
+ end # context
65
+
66
+ end # describe #name
67
+
68
+ describe "#policy" do
69
+
70
+ let(:policy_name) { :Policy }
71
+
72
+ shared_examples "policy object class" do
73
+
74
+ it "returns the constant" do
75
+ expect(subject.policy).to eq policy_class
76
+ end
77
+
78
+ end # context
79
+
80
+ it_behaves_like "policy object class" do
81
+ subject { described_class.new :foo, policy_class, :foo, :bar, :baz }
82
+ end
83
+
84
+ it_behaves_like "policy object class" do
85
+ subject { described_class.new namespace, policy_name, :foo, :bar, :baz }
86
+ end
87
+
88
+ end # describe #policy
89
+
90
+ describe "#attributes" do
91
+
92
+ let(:attributes) { %i(foo bar) }
93
+ subject { described_class.new nil, policy_class, :foo, *attributes }
94
+
95
+ it "is set by the initializer" do
96
+ expect(subject.attributes).to eq attributes
97
+ end
98
+
99
+ end # describe #attributes
100
+
101
+ describe "#apply_to" do
102
+
103
+ let(:attributes) { { foo: :bar, bar: :baz } }
104
+ let(:follower) { OpenStruct.new(attributes) }
105
+
106
+ subject do
107
+ described_class.new nil, policy_class, nil, *attributes.keys
108
+ end
109
+
110
+ it "calls policy_class validation with follower attributes" do
111
+ expect(policy_class).to receive(:apply).with(*attributes.values)
112
+ subject.apply_to(follower)
113
+ end
114
+
115
+ end # describe #apply_to
116
+
117
+ end # describe Policy::Follower::Policy
@@ -0,0 +1,19 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Follower::Names do
4
+
5
+ describe ".from" do
6
+
7
+ shared_examples "normalizing from" do |*source|
8
+
9
+ subject { described_class.from(*source) }
10
+ it { is_expected.to eq %i(foo bar baz foo) }
11
+
12
+ end # shared examples
13
+
14
+ it_behaves_like "normalizing from", %w(foo bar baz foo)
15
+ it_behaves_like "normalizing from", *%w(foo bar baz foo)
16
+
17
+ end # describe .from
18
+
19
+ end # describe Policy::Follower::Names
@@ -0,0 +1,220 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Follower do
4
+
5
+ before { Test = Class.new.__send__(:include, described_class) }
6
+ after { Object.send :remove_const, :Test }
7
+
8
+ let(:test_class) { Test }
9
+ subject { test_class.new }
10
+
11
+ it "includes Policy::Validations" do
12
+ expect(test_class).to include Policy::Validations
13
+ end
14
+
15
+ describe ".followed_policies" do
16
+
17
+ let(:followed_policies) { Policy::Follower::FollowedPolicies }
18
+
19
+ it "returns the AppiedPolicies object" do
20
+ expect(test_class.followed_policies).to be_kind_of followed_policies
21
+ end
22
+
23
+ end # describe .followed_policies
24
+
25
+ describe ".follow_policy" do
26
+
27
+ let(:followed_policy) { Policy::Follower::FollowedPolicy }
28
+
29
+ let(:policy) { double name: :foo }
30
+ let(:policy_class) { double }
31
+ let(:name) { double }
32
+ let(:attributes) { [double, double] }
33
+
34
+ before { allow(followed_policy).to receive(:new) { policy } }
35
+
36
+ context "by default" do
37
+
38
+ after { test_class.follow_policy policy_class, *attributes }
39
+
40
+ it "creates new followed policy" do
41
+ expect(followed_policy)
42
+ .to receive(:new)
43
+ .with(test_class, policy_class, nil, *attributes)
44
+ end
45
+
46
+ it "adds new policy to .followed_policies" do
47
+ expect(test_class.followed_policies).to receive(:add).with(policy)
48
+ end
49
+
50
+ end # context
51
+
52
+ context "as: name" do
53
+
54
+ after { test_class.follow_policy(policy_class, *attributes, as: name) }
55
+
56
+ it "creates named followed policy" do
57
+ expect(followed_policy)
58
+ .to receive(:new)
59
+ .with(test_class, policy_class, name, *attributes)
60
+ end
61
+
62
+ end # context
63
+
64
+ context "inside #use_policies" do
65
+
66
+ let(:namespace) { double }
67
+
68
+ subject do
69
+ test_class.use_policies namespace do
70
+ follow_policy :foo, as: :bar
71
+ end
72
+ end
73
+
74
+ it "sends the namespace to followed policy" do
75
+ expect(followed_policy)
76
+ .to receive(:new).with(namespace, :foo, :bar)
77
+ subject
78
+ end
79
+
80
+ it "doesn't change default namespace" do
81
+ subject
82
+ expect(followed_policy)
83
+ .to receive(:new).with(test_class, :foo, :bar)
84
+ test_class.follow_policy :foo, as: :bar
85
+ end
86
+
87
+ end # context
88
+
89
+ end # describe .follow_policy
90
+
91
+ describe "#follow_policies!" do
92
+
93
+ context "without names" do
94
+
95
+ after { subject.follow_policies! }
96
+
97
+ it "applies .followed_policies to itself" do
98
+ expect(test_class.followed_policies)
99
+ .to receive(:apply_to).with(subject)
100
+ end
101
+
102
+ end # context
103
+
104
+ context "with names" do
105
+
106
+ let(:names) { %i(foo bar baz) }
107
+ after { subject.follow_policies!(*names) }
108
+
109
+ it "applies .followed_policies to itself with names" do
110
+ expect(test_class.followed_policies)
111
+ .to receive(:apply_to).with(subject, *names)
112
+ end
113
+
114
+ end # context
115
+
116
+ context "when a ViolationError is raised" do
117
+
118
+ let(:error) { Policy::ViolationError.allocate }
119
+ let(:messages) { %w(foo bar baz) }
120
+
121
+ before do
122
+ allow(error).to receive(:messages) { messages }
123
+ allow(test_class.followed_policies).to receive(:apply_to) { fail error }
124
+ end
125
+
126
+ it "populates messages with errors" do
127
+ expect { subject.follow_policies! rescue nil }
128
+ .to change { subject.__send__(:errors).messages }
129
+ .to(base: messages)
130
+ end
131
+
132
+ it "re-raises the exception" do
133
+ expect { subject.follow_policies! }.to raise_error(error)
134
+ end
135
+
136
+ end # context
137
+
138
+ end # describe #follow_policies!
139
+
140
+ describe "#follow_policies?" do
141
+
142
+ before { allow(subject).to receive(:follow_policies!) }
143
+
144
+ it "calls #follow_policies! without names" do
145
+ expect(subject).to receive(:follow_policies!)
146
+ subject.follow_policies?
147
+ end
148
+
149
+ it "calls #follow_policies! with names" do
150
+ names = %i(foo bar baz)
151
+
152
+ expect(subject).to receive(:follow_policies!).with(*names)
153
+ subject.follow_policies?(*names)
154
+ end
155
+
156
+ context "wheh #follow_policies! doesn't raise an error" do
157
+
158
+ before { allow(subject).to receive(:follow_policies!) { nil } }
159
+
160
+ it "returns true" do
161
+ expect(subject.follow_policies?).to eq true
162
+ end
163
+
164
+ end # context
165
+
166
+ context "wheh #folow_policies! raises ViolationError" do
167
+
168
+ let(:error) { Policy::ViolationError.allocate }
169
+ before { allow(subject).to receive(:follow_policies!) { fail error } }
170
+
171
+ it "returns false" do
172
+ expect(subject.follow_policies?).to eq false
173
+ end
174
+
175
+ end # context
176
+
177
+ context "wheh #follow_policies! raises RuntimeError" do
178
+
179
+ let(:error) { StandardError.allocate }
180
+ before { allow(subject).to receive(:follow_policies!) { fail error } }
181
+
182
+ it "fails" do
183
+ expect { subject.follow_policies? }.to raise_error(error)
184
+ end
185
+
186
+ end # context
187
+
188
+ end # describe #follow_policies?
189
+
190
+ describe "#follow_policy!" do
191
+
192
+ before { allow(subject).to receive(:follow_policies!) }
193
+
194
+ it "is an alias for #follow_policies!" do
195
+ expect(subject).to receive(:follow_policies!).with :foo
196
+ subject.follow_policy! :foo
197
+ end
198
+
199
+ it "requires an argument" do
200
+ expect(subject.method(:follow_policy!).arity).to eq 1
201
+ end
202
+
203
+ end # describe #follow_policy!
204
+
205
+ describe "#follow_policy?" do
206
+
207
+ before { allow(subject).to receive(:follow_policies?) }
208
+
209
+ it "is an alias for #follow_policies?" do
210
+ expect(subject).to receive(:follow_policies?).with :foo
211
+ subject.follow_policy? :foo
212
+ end
213
+
214
+ it "requires an argument" do
215
+ expect(subject.method(:follow_policy?).arity).to eq 1
216
+ end
217
+
218
+ end # describe #follow_policy?
219
+
220
+ end # describe Policy::Follower
@@ -0,0 +1,83 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Interface do
4
+
5
+ before { Test = Class.new.send :include, described_class }
6
+ after { Object.send :remove_const, :Test }
7
+
8
+ let(:test_class) { Test }
9
+ subject { test_class.new }
10
+
11
+ it "includes Policy::Validations" do
12
+ expect(test_class).to include Policy::Validations
13
+ end
14
+
15
+ describe "#apply" do
16
+
17
+ context "when #valid? returns true" do
18
+
19
+ before { allow(subject).to receive(:valid?).and_return true }
20
+
21
+ it "doesn't raise error" do
22
+ expect { subject.apply }.not_to raise_error
23
+ end
24
+ end
25
+
26
+ context "when #valid? returns false" do
27
+
28
+ before { allow(subject).to receive(:valid?).and_return false }
29
+
30
+ it "raises ViolationError" do
31
+ expect { subject.apply }.to raise_error(Policy::ViolationError)
32
+ end
33
+
34
+ it "adds the policy to Exception" do
35
+ expect(Policy::ViolationError).to receive(:new).with(subject).once
36
+ subject.apply rescue nil
37
+ end
38
+ end
39
+
40
+ end # describe #apply
41
+
42
+ describe "#messages" do
43
+
44
+ context "when #errors are present" do
45
+
46
+ let(:messages) { %w(foo bar) }
47
+ let(:errors) { double :errors, messages: { foo: messages } }
48
+
49
+ it "extracts a plain array of error messages" do
50
+ allow(subject).to receive(:errors) { errors }
51
+ expect(subject.messages).to eq messages
52
+ end
53
+
54
+ end # context
55
+
56
+ context "when #errors are absent" do
57
+
58
+ it "returns an empty array" do
59
+ expect(subject.messages).to eq []
60
+ end
61
+
62
+ end # context
63
+
64
+ end # describe #messages
65
+
66
+ describe ".apply" do
67
+
68
+ let(:attributes) { %i(foo bar) }
69
+ let(:follower) { double apply: nil }
70
+
71
+ it "creates a policy object with the attributes" do
72
+ expect(test_class).to receive(:new).with(attributes) { follower }
73
+ test_class.apply attributes
74
+ end
75
+
76
+ it "validates the policy object" do
77
+ expect(test_class).to receive_message_chain :new, :apply
78
+ test_class.apply attributes
79
+ end
80
+
81
+ end # describe .apply
82
+
83
+ end # describe Policy::Inteface
@@ -0,0 +1,13 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Validations do
4
+
5
+ let(:validations) { ActiveModel::Validations }
6
+ let(:methods) { validations.public_instance_methods }
7
+ let(:test_class) { Class.new.send :include, described_class }
8
+
9
+ it "includes ActiveModel::Validatons" do
10
+ expect(test_class).to include validations
11
+ end
12
+
13
+ end