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.
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