policy 1.2.0 → 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/Guardfile +4 -4
  3. data/README.md +177 -78
  4. data/config/metrics/flay.yml +1 -1
  5. data/config/metrics/roodi.yml +2 -2
  6. data/lib/policy.rb +52 -33
  7. data/lib/policy/base.rb +122 -0
  8. data/lib/policy/base/and.rb +38 -0
  9. data/lib/policy/base/negator.rb +52 -0
  10. data/lib/policy/base/node.rb +59 -0
  11. data/lib/policy/base/not.rb +42 -0
  12. data/lib/policy/base/or.rb +39 -0
  13. data/lib/policy/base/xor.rb +39 -0
  14. data/lib/policy/cli.rb +8 -3
  15. data/lib/policy/cli/attribute.rb +49 -0
  16. data/lib/policy/cli/locale.erb +1 -2
  17. data/lib/policy/cli/policy.erb +33 -6
  18. data/lib/policy/cli/spec.erb +31 -11
  19. data/lib/policy/follower.rb +54 -94
  20. data/lib/policy/follower/name_error.rb +53 -0
  21. data/lib/policy/follower/policies.rb +104 -0
  22. data/lib/policy/follower/violation_error.rb +60 -0
  23. data/lib/policy/version.rb +2 -2
  24. data/policy.gemspec +2 -3
  25. data/spec/support/composer.rb +28 -0
  26. data/spec/tests/lib/policy/base/and_spec.rb +62 -0
  27. data/spec/tests/lib/policy/base/negator_spec.rb +49 -0
  28. data/spec/tests/lib/policy/base/not_spec.rb +50 -0
  29. data/spec/tests/lib/policy/base/or_spec.rb +62 -0
  30. data/spec/tests/lib/policy/base/xor_spec.rb +73 -0
  31. data/spec/tests/lib/policy/base_spec.rb +123 -0
  32. data/spec/tests/lib/policy/cli/attribute_spec.rb +52 -0
  33. data/spec/tests/{policy → lib/policy}/cli_spec.rb +25 -24
  34. data/spec/tests/lib/policy/follower/name_error_spec.rb +51 -0
  35. data/spec/tests/lib/policy/follower/policies_spec.rb +156 -0
  36. data/spec/tests/lib/policy/follower/violation_error_spec.rb +60 -0
  37. data/spec/tests/lib/policy/follower_spec.rb +153 -0
  38. data/spec/tests/lib/policy_spec.rb +52 -0
  39. metadata +43 -44
  40. data/lib/policy/follower/followed_policies.rb +0 -45
  41. data/lib/policy/follower/followed_policy.rb +0 -104
  42. data/lib/policy/follower/names.rb +0 -29
  43. data/lib/policy/interface.rb +0 -48
  44. data/lib/policy/validations.rb +0 -28
  45. data/lib/policy/violation_error.rb +0 -52
  46. data/spec/features/follower_spec.rb +0 -95
  47. data/spec/tests/policy/follower/followed_policies_spec.rb +0 -87
  48. data/spec/tests/policy/follower/followed_policy_spec.rb +0 -117
  49. data/spec/tests/policy/follower/names_spec.rb +0 -19
  50. data/spec/tests/policy/follower_spec.rb +0 -220
  51. data/spec/tests/policy/interface_spec.rb +0 -83
  52. data/spec/tests/policy/validations_spec.rb +0 -13
  53. data/spec/tests/policy/violation_error_spec.rb +0 -75
  54. data/spec/tests/policy_spec.rb +0 -35
@@ -0,0 +1,60 @@
1
+ # encoding: utf-8
2
+
3
+ module Policy
4
+
5
+ module Follower
6
+
7
+ # An exception to be risen when a follower violates its policy
8
+ class ViolationError < RuntimeError
9
+
10
+ # @!scope class
11
+ # @!method new(follower, policy)
12
+ # Constructs an exception for the policy violated by the follower
13
+ #
14
+ # @param [Object] follower
15
+ # @param [#errors] policy
16
+ #
17
+ # @return [Policy::ViolationError]
18
+ def initialize(follower, policy)
19
+ @follower = follower
20
+ @policy = policy
21
+ end
22
+
23
+ # @!attribute [r] follower
24
+ # The follower object that causes the exception
25
+ #
26
+ # @return [Object]
27
+ attr_reader :follower
28
+
29
+ # @!attribute [r] policy
30
+ # The violated policy object
31
+ #
32
+ # @return [Policy::Base]
33
+ attr_reader :policy
34
+
35
+ # Returns the list of policy errors
36
+ #
37
+ # @return [ActiveModel::Errors]
38
+ def errors
39
+ policy.errors
40
+ end
41
+
42
+ # The human-readable exception message
43
+ #
44
+ # @return [String]
45
+ def message
46
+ "#{ follower.inspect } violates the policy #{ policy }"
47
+ end
48
+
49
+ # The human-readable description for the exception
50
+ #
51
+ # @return [String]
52
+ def inspect
53
+ "#<#{ self }: #{ message }>"
54
+ end
55
+
56
+ end # class ViolationError
57
+
58
+ end # module Follower
59
+
60
+ end # module Policy
@@ -4,6 +4,6 @@ module Policy
4
4
 
5
5
  # The semantic version of the module.
6
6
  # @see http://semver.org/ Semantic versioning 2.0
7
- VERSION = "1.2.0".freeze
7
+ VERSION = "2.0.0".freeze
8
8
 
9
- end # module Hexx
9
+ end # module Policy
@@ -9,7 +9,7 @@ Gem::Specification.new do |gem|
9
9
  gem.email = "andrew.kozin@gmail.com"
10
10
  gem.homepage = "https://github.com/nepalez/policy"
11
11
  gem.summary = "Policy Objects for Ruby."
12
- gem.description = "A tiny library implementing the Policy Object pattern."
12
+ gem.description = "A small library implementing the Policy Object pattern."
13
13
  gem.license = "MIT"
14
14
 
15
15
  gem.files = `git ls-files`.split($INPUT_RECORD_SEPARATOR)
@@ -20,8 +20,7 @@ Gem::Specification.new do |gem|
20
20
 
21
21
  gem.required_ruby_version = "~> 2.0"
22
22
  gem.add_runtime_dependency "activemodel", ">= 3.1"
23
- gem.add_runtime_dependency "adamantium", "~> 0.2"
24
- gem.add_runtime_dependency "hexx-cli", "~> 0.0"
23
+ gem.add_development_dependency "hexx-cli", "~> 0.0"
25
24
  gem.add_development_dependency "hexx-rspec", "~> 0.3"
26
25
 
27
26
  end # Gem::Specification
@@ -0,0 +1,28 @@
1
+ # encoding: utf-8
2
+
3
+ # Mocks a valid policy object
4
+ #
5
+ # @return [Policy::Base::Node]
6
+ def valid_policy
7
+ policy = Policy::Base::Node.new
8
+ allow(policy).to receive(:valid?) { true }
9
+ allow(policy).to receive(:invalid?) { false }
10
+ policy
11
+ end
12
+
13
+ # Mocks an invalid policy object
14
+ #
15
+ # @return [Policy::Base::Node]
16
+ def invalid_policy(error: "error")
17
+ policy = Policy::Base::Node.new
18
+
19
+ add_errors = lambda do
20
+ policy.errors.clear
21
+ policy.errors.add(:base, error)
22
+ end
23
+
24
+ allow(policy).to receive(:valid?) { add_errors.call && false }
25
+ allow(policy).to receive(:invalid?) { add_errors.call && true }
26
+
27
+ policy
28
+ end
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Base::And do
4
+
5
+ # defines mock builders #valid_policy and #invalid_policy
6
+ require "support/composer"
7
+
8
+ let(:one) { valid_policy }
9
+ let(:two) { valid_policy }
10
+ let(:three) { valid_policy }
11
+
12
+ subject { described_class.new one, two, three }
13
+
14
+ describe ".new" do
15
+
16
+ it "creates a Node" do
17
+ expect(subject).to be_kind_of Policy::Base::Node
18
+ end
19
+
20
+ end # new
21
+
22
+ describe "#policies" do
23
+
24
+ it "is initialized" do
25
+ expect(subject.policies).to contain_exactly one, two, three
26
+ end
27
+
28
+ end # describe #policies
29
+
30
+ describe "#valid?" do
31
+
32
+ context "when all the policies are valid" do
33
+
34
+ it "returns true" do
35
+ expect(subject).to be_valid
36
+ end
37
+
38
+ it "clears previous errors" do
39
+ subject.errors.add :base, :subject
40
+ expect { subject.valid? }.to change { subject.errors.blank? }.to true
41
+ end
42
+
43
+ end # context
44
+
45
+ context "when any policy is invalid" do
46
+
47
+ let(:three) { invalid_policy error: "three" }
48
+ let!(:result) { subject.valid? }
49
+
50
+ it "returns false" do
51
+ expect(result).to eq false
52
+ end
53
+
54
+ it "picks up error from invalid policy" do
55
+ expect(subject.errors.full_messages).to contain_exactly "three"
56
+ end
57
+
58
+ end # context
59
+
60
+ end # describe #valid?
61
+
62
+ end # describe Policy::Base::Not
@@ -0,0 +1,49 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Base::Negator do
4
+
5
+ let(:composer) { Policy::Base::Node }
6
+ let(:policy) { double :policy }
7
+
8
+ subject { described_class.new policy, composer }
9
+
10
+ describe "#policy" do
11
+
12
+ it "is initialized" do
13
+ expect(subject.policy).to eq policy
14
+ end
15
+
16
+ end # describe #policy
17
+
18
+ describe "#composer" do
19
+
20
+ it "is initialized" do
21
+ expect(subject.composer).to eq composer
22
+ end
23
+
24
+ end # describe #composer
25
+
26
+ describe "#not" do
27
+
28
+ let(:one) { double :one }
29
+
30
+ let(:result) { subject.not(one) }
31
+
32
+ it "creates a composer object" do
33
+ expect(result).to be_kind_of composer
34
+ end
35
+
36
+ it "sends the policy to the composer" do
37
+ expect(result.policies).to include policy
38
+ end
39
+
40
+ it "sends the negated arguments to the composer" do
41
+ not_one = double :not_one
42
+ expect(Policy::Base::Not).to receive(:new).with(one).and_return(not_one)
43
+
44
+ expect(result.policies).to include not_one
45
+ end
46
+
47
+ end # describe #not
48
+
49
+ end # describe Policy::Base::Negator
@@ -0,0 +1,50 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Base::Not do
4
+
5
+ # defines mock builders #valid_policy and #invalid_policy
6
+ require "support/composer"
7
+
8
+ let(:policy) { valid_policy }
9
+ subject { described_class.new policy }
10
+
11
+ it "is a Node" do
12
+ expect(subject).to be_kind_of Policy::Base::Node
13
+ end
14
+
15
+ describe "#policies" do
16
+
17
+ it "is initialized" do
18
+ expect(subject.policies).to eq [policy]
19
+ end
20
+
21
+ end # describe #policies
22
+
23
+ describe "#valid?" do
24
+
25
+ context "when the policy is valid" do
26
+
27
+ it "returns false" do
28
+ expect(subject).not_to be_valid
29
+ end
30
+
31
+ end # context
32
+
33
+ context "when the policy is invalid" do
34
+
35
+ let(:policy) { invalid_policy }
36
+
37
+ it "returns true" do
38
+ expect(subject).to be_valid
39
+ end
40
+
41
+ it "clears previous errors" do
42
+ subject.errors.add :base, :subject
43
+ expect { subject.valid? }.to change { subject.errors.blank? }.to true
44
+ end
45
+
46
+ end # context
47
+
48
+ end # describe #valid?
49
+
50
+ end # describe Policy::Base::Not
@@ -0,0 +1,62 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Base::Or do
4
+
5
+ # defines mock builders #valid_policy and #invalid_policy
6
+ require "support/composer"
7
+
8
+ let(:one) { invalid_policy error: "one" }
9
+ let(:two) { invalid_policy error: "two" }
10
+ let(:three) { valid_policy }
11
+
12
+ subject { described_class.new one, two, three }
13
+
14
+ describe ".new" do
15
+
16
+ it "creates a Node" do
17
+ expect(subject).to be_kind_of Policy::Base::Node
18
+ end
19
+
20
+ end # new
21
+
22
+ describe "#policies" do
23
+
24
+ it "is initialized" do
25
+ expect(subject.policies).to contain_exactly one, two, three
26
+ end
27
+
28
+ end # describe #policies
29
+
30
+ describe "#valid?" do
31
+
32
+ context "when any policy is valid" do
33
+
34
+ it "returns true" do
35
+ expect(subject).to be_valid
36
+ end
37
+
38
+ it "clears previous errors" do
39
+ subject.errors.add :base, :subject
40
+ expect { subject.valid? }.to change { subject.errors.blank? }.to true
41
+ end
42
+
43
+ end # context
44
+
45
+ context "when all policies are invalid" do
46
+
47
+ let(:three) { invalid_policy error: "three" }
48
+ let!(:result) { subject.valid? }
49
+
50
+ it "returns false" do
51
+ expect(result).to eq false
52
+ end
53
+
54
+ it "picks up errors from all policies" do
55
+ expect(subject.errors).to contain_exactly "one", "two", "three"
56
+ end
57
+
58
+ end # context
59
+
60
+ end # describe #valid?
61
+
62
+ end # describe Policy::Base::Not
@@ -0,0 +1,73 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Base::Xor do
4
+
5
+ # defines mock builders #valid_policy and #invalid_policy
6
+ require "support/composer"
7
+
8
+ let(:one) { invalid_policy error: "one" }
9
+ let(:two) { invalid_policy error: "two" }
10
+ let(:three) { valid_policy }
11
+
12
+ subject { described_class.new one, two, three }
13
+
14
+ describe ".new" do
15
+
16
+ it "creates a Node" do
17
+ expect(subject).to be_kind_of Policy::Base::Node
18
+ end
19
+
20
+ end # new
21
+
22
+ describe "#policies" do
23
+
24
+ it "is initialized" do
25
+ expect(subject.policies).to contain_exactly one, two, three
26
+ end
27
+
28
+ end # describe #policies
29
+
30
+ describe "#valid?" do
31
+
32
+ context "when both valid and invalid policies are present" do
33
+
34
+ it "returns true" do
35
+ expect(subject).to be_valid
36
+ end
37
+
38
+ it "clears previous errors" do
39
+ subject.errors.add :base, :subject
40
+ expect { subject.valid? }.to change { subject.errors.blank? }.to true
41
+ end
42
+
43
+ end # context
44
+
45
+ context "when all policies are valid" do
46
+
47
+ let(:one) { valid_policy }
48
+ let(:two) { valid_policy }
49
+
50
+ it "returns false" do
51
+ expect(subject).not_to be_valid
52
+ end
53
+
54
+ end # context
55
+
56
+ context "when all policies are invalid" do
57
+
58
+ let(:three) { invalid_policy error: "three" }
59
+ let!(:result) { subject.valid? }
60
+
61
+ it "returns false" do
62
+ expect(result).to eq false
63
+ end
64
+
65
+ it "picks up errors from all policies" do
66
+ expect(subject.errors).to contain_exactly "one", "two", "three"
67
+ end
68
+
69
+ end # context
70
+
71
+ end # describe #valid?
72
+
73
+ end # describe Policy::Base::Not
@@ -0,0 +1,123 @@
1
+ # encoding: utf-8
2
+
3
+ describe Policy::Base do
4
+
5
+ let(:test_class) { Class.new.send(:include, described_class) }
6
+ subject { test_class.new }
7
+
8
+ it "includes ActiveModel::Validations" do
9
+ expect(test_class).to include ActiveModel::Validations
10
+ end
11
+
12
+ let(:one) { double :one }
13
+ let(:two) { double :two }
14
+
15
+ describe "#and" do
16
+
17
+ context "without policies" do
18
+
19
+ let(:result) { subject.and }
20
+
21
+ it "creates the negator object" do
22
+ expect(result).to be_kind_of Policy::Base::Negator
23
+ end
24
+
25
+ it "assigns self as a negator policy" do
26
+ expect(result.policy).to eq subject
27
+ end
28
+
29
+ it "assings And as a negator composer" do
30
+ expect(result.composer).to eq Policy::Base::And
31
+ end
32
+
33
+ end # context
34
+
35
+ context "with policies" do
36
+
37
+ let(:result) { subject.and(one, two) }
38
+
39
+ it "creates an And composition" do
40
+ expect(result).to be_kind_of Policy::Base::And
41
+ end
42
+
43
+ it "assigns given parts to the composition" do
44
+ expect(result.policies).to contain_exactly subject, one, two
45
+ end
46
+
47
+ end # context
48
+
49
+ end # describe #and
50
+
51
+ describe "#or" do
52
+
53
+ context "without policies" do
54
+
55
+ let(:result) { subject.or }
56
+
57
+ it "creates the negator object" do
58
+ expect(result).to be_kind_of Policy::Base::Negator
59
+ end
60
+
61
+ it "assigns self as a negator policy" do
62
+ expect(result.policy).to eq subject
63
+ end
64
+
65
+ it "assings Or as a negator composer" do
66
+ expect(result.composer).to eq Policy::Base::Or
67
+ end
68
+
69
+ end # context
70
+
71
+ context "with policies" do
72
+
73
+ let(:result) { subject.or(one, two) }
74
+
75
+ it "creates an Or composition" do
76
+ expect(result).to be_kind_of Policy::Base::Or
77
+ end
78
+
79
+ it "assigns given parts to the composition" do
80
+ expect(result.policies).to contain_exactly subject, one, two
81
+ end
82
+
83
+ end # context
84
+
85
+ end # describe #or
86
+
87
+ describe "#xor" do
88
+
89
+ context "without policies" do
90
+
91
+ let(:result) { subject.xor }
92
+
93
+ it "creates the negator object" do
94
+ expect(result).to be_kind_of Policy::Base::Negator
95
+ end
96
+
97
+ it "assigns self as a negator policy" do
98
+ expect(result.policy).to eq subject
99
+ end
100
+
101
+ it "assings Xor as a negator composer" do
102
+ expect(result.composer).to eq Policy::Base::Xor
103
+ end
104
+
105
+ end # context
106
+
107
+ context "with policies" do
108
+
109
+ let(:result) { subject.xor(one, two) }
110
+
111
+ it "creates an Xor composition" do
112
+ expect(result).to be_kind_of Policy::Base::Xor
113
+ end
114
+
115
+ it "assigns given parts to the composition" do
116
+ expect(result.policies).to contain_exactly subject, one, two
117
+ end
118
+
119
+ end # context
120
+
121
+ end # describe #xor
122
+
123
+ end # describe Policy::Base