attestor 0.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.
- checksums.yaml +7 -0
- data/.coveralls.yml +2 -0
- data/.gitignore +9 -0
- data/.metrics +9 -0
- data/.rspec +2 -0
- data/.rubocop.yml +2 -0
- data/.travis.yml +10 -0
- data/.yardopts +3 -0
- data/Gemfile +5 -0
- data/Guardfile +15 -0
- data/LICENSE +21 -0
- data/README.md +308 -0
- data/Rakefile +22 -0
- data/attestor.gemspec +24 -0
- data/config/metrics/STYLEGUIDE +230 -0
- data/config/metrics/cane.yml +5 -0
- data/config/metrics/churn.yml +6 -0
- data/config/metrics/flay.yml +2 -0
- data/config/metrics/metric_fu.yml +15 -0
- data/config/metrics/reek.yml +1 -0
- data/config/metrics/roodi.yml +24 -0
- data/config/metrics/rubocop.yml +75 -0
- data/config/metrics/saikuro.yml +3 -0
- data/config/metrics/simplecov.yml +6 -0
- data/config/metrics/yardstick.yml +37 -0
- data/lib/attestor/invalid_error.rb +44 -0
- data/lib/attestor/policy/and.rb +36 -0
- data/lib/attestor/policy/factory.rb +88 -0
- data/lib/attestor/policy/negator.rb +53 -0
- data/lib/attestor/policy/node.rb +58 -0
- data/lib/attestor/policy/not.rb +48 -0
- data/lib/attestor/policy/or.rb +36 -0
- data/lib/attestor/policy/xor.rb +36 -0
- data/lib/attestor/policy.rb +121 -0
- data/lib/attestor/validations/collection.rb +73 -0
- data/lib/attestor/validations/item.rb +87 -0
- data/lib/attestor/validations/message.rb +55 -0
- data/lib/attestor/validations.rb +81 -0
- data/lib/attestor/version.rb +9 -0
- data/lib/attestor.rb +26 -0
- data/spec/spec_helper.rb +14 -0
- data/spec/support/policies.rb +49 -0
- data/spec/tests/invalid_error_spec.rb +57 -0
- data/spec/tests/policy/and_spec.rb +40 -0
- data/spec/tests/policy/factory_spec.rb +100 -0
- data/spec/tests/policy/negator_spec.rb +57 -0
- data/spec/tests/policy/node_spec.rb +44 -0
- data/spec/tests/policy/not_spec.rb +40 -0
- data/spec/tests/policy/or_spec.rb +40 -0
- data/spec/tests/policy/xor_spec.rb +48 -0
- data/spec/tests/policy_spec.rb +111 -0
- data/spec/tests/validations/collection_spec.rb +100 -0
- data/spec/tests/validations/item_spec.rb +153 -0
- data/spec/tests/validations/message_spec.rb +71 -0
- data/spec/tests/validations_spec.rb +126 -0
- metadata +143 -0
@@ -0,0 +1,73 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attestor
|
4
|
+
|
5
|
+
module Validations
|
6
|
+
|
7
|
+
# The collection of validations used by class instances
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Collection
|
11
|
+
include Enumerable
|
12
|
+
|
13
|
+
# @!scope class
|
14
|
+
# @!method new(items = [])
|
15
|
+
# Creates an immutable collection with optional list of items
|
16
|
+
#
|
17
|
+
# @param [Array<Attestor::Collection::Item>] items
|
18
|
+
#
|
19
|
+
# @return [Attestor::Collection]
|
20
|
+
|
21
|
+
# @private
|
22
|
+
def initialize(items = [])
|
23
|
+
@items = items
|
24
|
+
freeze
|
25
|
+
end
|
26
|
+
|
27
|
+
# Iterates through the collection
|
28
|
+
#
|
29
|
+
# @yield the block
|
30
|
+
# @yieldparam [Attestor::Collection::Item] item
|
31
|
+
# items from the collection
|
32
|
+
#
|
33
|
+
# @return [Enumerator]
|
34
|
+
def each
|
35
|
+
return to_enum unless block_given?
|
36
|
+
items.map(&:name).uniq.each { |item| yield(item) }
|
37
|
+
end
|
38
|
+
|
39
|
+
# Returns the collection, updated with new item
|
40
|
+
#
|
41
|
+
# @param [#to_sym] name
|
42
|
+
# @param [Hash] options
|
43
|
+
# @option options [Array<#to_sym>] :except
|
44
|
+
# @option options [Array<#to_sym>] :only
|
45
|
+
#
|
46
|
+
# @return [Attestor::Collection]
|
47
|
+
def add(name, options = {})
|
48
|
+
item = Item.new(name, options)
|
49
|
+
return self if items.include? item
|
50
|
+
|
51
|
+
self.class.new(items + [item])
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns the collection of items used in given context
|
55
|
+
#
|
56
|
+
# @param [#to_sym] context
|
57
|
+
#
|
58
|
+
# @return [Attestor::Collection]
|
59
|
+
def set(context)
|
60
|
+
collection = items.select { |item| item.used_in_context? context }
|
61
|
+
|
62
|
+
self.class.new(collection)
|
63
|
+
end
|
64
|
+
|
65
|
+
private
|
66
|
+
|
67
|
+
attr_reader :items
|
68
|
+
|
69
|
+
end # class Collection
|
70
|
+
|
71
|
+
end # module Validations
|
72
|
+
|
73
|
+
end # module Attestor
|
@@ -0,0 +1,87 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attestor
|
4
|
+
|
5
|
+
module Validations
|
6
|
+
|
7
|
+
# Describes an item of validations' collection
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Item
|
11
|
+
|
12
|
+
# @!scope class
|
13
|
+
# @!method new(name, except: [], only: [])
|
14
|
+
# Creates a named item with blacklist or whitelist of contexts
|
15
|
+
#
|
16
|
+
# @param [#to_sym] name
|
17
|
+
# @option [#to_sym, Array<#to_sym>] :except
|
18
|
+
# @option [#to_sym, Array<#to_sym>] :only
|
19
|
+
#
|
20
|
+
# @return [Attestor::Collection::Item]
|
21
|
+
|
22
|
+
# @private
|
23
|
+
def initialize(name, except: nil, only: nil)
|
24
|
+
@name = name.to_sym
|
25
|
+
@whitelist = normalize(only)
|
26
|
+
@blacklist = normalize(except)
|
27
|
+
generate_id
|
28
|
+
freeze
|
29
|
+
end
|
30
|
+
|
31
|
+
# @!attribute [r] name
|
32
|
+
# The name of the item
|
33
|
+
# @return [Symbol]
|
34
|
+
attr_reader :name
|
35
|
+
|
36
|
+
# Compares an item to another one
|
37
|
+
#
|
38
|
+
# @param [Object] other
|
39
|
+
#
|
40
|
+
# @return [Boolean]
|
41
|
+
def ==(other)
|
42
|
+
other.instance_of?(self.class) ? id.equal?(other.id) : false
|
43
|
+
end
|
44
|
+
|
45
|
+
# Checks if the item should be used in given context
|
46
|
+
#
|
47
|
+
# @param [#to_sym] context
|
48
|
+
#
|
49
|
+
# @return [Boolean]
|
50
|
+
def used_in_context?(context)
|
51
|
+
symbol = context.to_sym
|
52
|
+
whitelisted?(symbol) && !blacklisted?(symbol)
|
53
|
+
end
|
54
|
+
|
55
|
+
protected
|
56
|
+
|
57
|
+
# @!attribute [r] id
|
58
|
+
# The item's identity
|
59
|
+
#
|
60
|
+
# @return [String]
|
61
|
+
attr_reader :id
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
attr_reader :whitelist, :blacklist
|
66
|
+
|
67
|
+
def whitelisted?(symbol)
|
68
|
+
whitelist.empty? || whitelist.include?(symbol)
|
69
|
+
end
|
70
|
+
|
71
|
+
def blacklisted?(symbol)
|
72
|
+
blacklist.include? symbol
|
73
|
+
end
|
74
|
+
|
75
|
+
def generate_id
|
76
|
+
@id = [name, whitelist, blacklist].hash
|
77
|
+
end
|
78
|
+
|
79
|
+
def normalize(list)
|
80
|
+
Array(list).map(&:to_sym).uniq
|
81
|
+
end
|
82
|
+
|
83
|
+
end # class Item
|
84
|
+
|
85
|
+
end # module Validations
|
86
|
+
|
87
|
+
end # module Attestor
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attestor
|
4
|
+
|
5
|
+
module Validations
|
6
|
+
|
7
|
+
# Bulder for error messages
|
8
|
+
#
|
9
|
+
# @api private
|
10
|
+
class Message < String
|
11
|
+
|
12
|
+
# @!scope class
|
13
|
+
# @!method new(value, object, options = {})
|
14
|
+
# Builds a string from value
|
15
|
+
#
|
16
|
+
# @param [#to_s] value
|
17
|
+
# @param [Object] object
|
18
|
+
# @param [Hash] options
|
19
|
+
# options for translating symbolic value
|
20
|
+
#
|
21
|
+
# @return [String]
|
22
|
+
# either translation of symbolic value or stringified value argument
|
23
|
+
|
24
|
+
# @private
|
25
|
+
def initialize(value, object, options = {})
|
26
|
+
@value = value
|
27
|
+
@object = object
|
28
|
+
@options = options
|
29
|
+
super(@value.instance_of?(Symbol) ? translation : @value.to_s)
|
30
|
+
freeze
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def translation
|
36
|
+
I18n.t @value, @options.merge(scope: scope, default: default)
|
37
|
+
end
|
38
|
+
|
39
|
+
def scope
|
40
|
+
%W(attestor errors #{ class_scope })
|
41
|
+
end
|
42
|
+
|
43
|
+
def class_scope
|
44
|
+
@object.class.to_s.split("::").map(&:snake_case).join("/")
|
45
|
+
end
|
46
|
+
|
47
|
+
def default
|
48
|
+
"#{ @object } is invalid (#{ @value })"
|
49
|
+
end
|
50
|
+
|
51
|
+
end # class Message
|
52
|
+
|
53
|
+
end # module Validations
|
54
|
+
|
55
|
+
end # module Attestor
|
@@ -0,0 +1,81 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
module Attestor
|
4
|
+
|
5
|
+
# API for objects to be validated
|
6
|
+
module Validations
|
7
|
+
|
8
|
+
# Calls all validations used in the selected context
|
9
|
+
#
|
10
|
+
# @raise [Attestor::Validations::InvalidError] if validations fail
|
11
|
+
# @raise [NoMethodError] if some of validations are not implemented
|
12
|
+
#
|
13
|
+
# @return [undefined]
|
14
|
+
def validate(context = :all)
|
15
|
+
self.class.validations.set(context).each(&method(:__send__))
|
16
|
+
end
|
17
|
+
|
18
|
+
# Raises InvalidError with a corresponding message
|
19
|
+
#
|
20
|
+
# @overload invalid(name, options = {})
|
21
|
+
#
|
22
|
+
# @param [Symbol] name
|
23
|
+
# the name of the error
|
24
|
+
# @param [Hash] options
|
25
|
+
# the options for symbolic name translation
|
26
|
+
#
|
27
|
+
# @return [String]
|
28
|
+
# translation of symbolic name in the current object's scope
|
29
|
+
#
|
30
|
+
# @overload invalid(name)
|
31
|
+
#
|
32
|
+
# @param [#to_s] name
|
33
|
+
# the error message (not a symbol)
|
34
|
+
#
|
35
|
+
# @return [String]
|
36
|
+
# the name converted to string
|
37
|
+
def invalid(name, options = {})
|
38
|
+
message = Message.new(name, self, options)
|
39
|
+
fail InvalidError.new self, [message]
|
40
|
+
end
|
41
|
+
|
42
|
+
# @private
|
43
|
+
module ClassMethods
|
44
|
+
|
45
|
+
# Returns a collection of items describing applied validations
|
46
|
+
#
|
47
|
+
# @return [Attestor::Collection]
|
48
|
+
#
|
49
|
+
# @api private
|
50
|
+
def validations
|
51
|
+
@validations ||= Collection.new
|
52
|
+
end
|
53
|
+
|
54
|
+
# Adds an item to {#validations}
|
55
|
+
#
|
56
|
+
# Mutates the class by changing its {#validations} attribute!
|
57
|
+
#
|
58
|
+
# @param [#to_sym] name
|
59
|
+
# @param [Hash] options
|
60
|
+
# @option options [#to_sym, Array<#to_sym>] :except
|
61
|
+
# the black list of contexts for validation
|
62
|
+
# @option options [#to_sym, Array<#to_sym>] :only
|
63
|
+
# the white list of contexts for validation
|
64
|
+
#
|
65
|
+
# @return [Attestor::Collection] the updated collection
|
66
|
+
def validate(name, options = {})
|
67
|
+
@validations = validations.add(name, options)
|
68
|
+
end
|
69
|
+
|
70
|
+
end # module ClassMethods
|
71
|
+
|
72
|
+
# @private
|
73
|
+
def self.included(klass)
|
74
|
+
klass.instance_eval { extend ClassMethods }
|
75
|
+
end
|
76
|
+
|
77
|
+
# @!parse extend Attestor::Validations::ClassMethods
|
78
|
+
|
79
|
+
end # module Validations
|
80
|
+
|
81
|
+
end # module Attestor
|
data/lib/attestor.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require "extlib"
|
4
|
+
|
5
|
+
require_relative "attestor/version"
|
6
|
+
|
7
|
+
require_relative "attestor/invalid_error"
|
8
|
+
|
9
|
+
require_relative "attestor/validations"
|
10
|
+
require_relative "attestor/validations/item"
|
11
|
+
require_relative "attestor/validations/collection"
|
12
|
+
require_relative "attestor/validations/message"
|
13
|
+
|
14
|
+
require_relative "attestor/policy/factory"
|
15
|
+
require_relative "attestor/policy"
|
16
|
+
require_relative "attestor/policy/node"
|
17
|
+
require_relative "attestor/policy/and"
|
18
|
+
require_relative "attestor/policy/or"
|
19
|
+
require_relative "attestor/policy/xor"
|
20
|
+
require_relative "attestor/policy/not"
|
21
|
+
require_relative "attestor/policy/negator"
|
22
|
+
|
23
|
+
# Namespace for the code of the 'attestor' gem
|
24
|
+
module Attestor
|
25
|
+
|
26
|
+
end # module Attestor
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# Definitions for testing compound policies
|
4
|
+
|
5
|
+
def valid_policy
|
6
|
+
double valid?: true, invalid?: false
|
7
|
+
end
|
8
|
+
|
9
|
+
def invalid_policy
|
10
|
+
double valid?: false, invalid?: true
|
11
|
+
end
|
12
|
+
|
13
|
+
shared_examples "creating a node" do
|
14
|
+
|
15
|
+
it { is_expected.to be_kind_of Attestor::Policy::Node }
|
16
|
+
|
17
|
+
end # shared examples
|
18
|
+
|
19
|
+
shared_examples "creating an immutable object" do
|
20
|
+
|
21
|
+
it "[freezes a policy]" do
|
22
|
+
expect(subject).to be_frozen
|
23
|
+
end
|
24
|
+
|
25
|
+
end # shared examples
|
26
|
+
|
27
|
+
shared_examples "failing validation" do
|
28
|
+
|
29
|
+
it "[raises exception]" do
|
30
|
+
expect { subject.validate }.to raise_error Attestor::InvalidError
|
31
|
+
end
|
32
|
+
|
33
|
+
it "[adds itself to exception]" do
|
34
|
+
begin
|
35
|
+
subject.validate
|
36
|
+
rescue => error
|
37
|
+
expect(error.object).to eq subject
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end # shared examples
|
42
|
+
|
43
|
+
shared_examples "passing validation" do
|
44
|
+
|
45
|
+
it "[raises exception]" do
|
46
|
+
expect { subject.validate }.not_to raise_error
|
47
|
+
end
|
48
|
+
|
49
|
+
end # shared examples
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe Attestor::InvalidError do
|
4
|
+
|
5
|
+
let(:object) { double :object }
|
6
|
+
subject { described_class.new object }
|
7
|
+
|
8
|
+
describe ".new" do
|
9
|
+
|
10
|
+
it "creates a RuntimeError" do
|
11
|
+
expect(subject).to be_kind_of RuntimeError
|
12
|
+
end
|
13
|
+
|
14
|
+
it "creates immutable object" do
|
15
|
+
expect(subject).to be_frozen
|
16
|
+
end
|
17
|
+
|
18
|
+
it "doesn't freeze object" do
|
19
|
+
subject
|
20
|
+
expect(object).not_to be_frozen
|
21
|
+
end
|
22
|
+
|
23
|
+
it "doesn't freeze messages" do
|
24
|
+
messages = %i(foo bar)
|
25
|
+
described_class.new object, messages
|
26
|
+
|
27
|
+
expect(messages).not_to be_frozen
|
28
|
+
end
|
29
|
+
|
30
|
+
end # describe .new
|
31
|
+
|
32
|
+
describe "#object" do
|
33
|
+
|
34
|
+
it "is initialized" do
|
35
|
+
expect(subject.object).to be_eql object
|
36
|
+
end
|
37
|
+
|
38
|
+
end # describe #object
|
39
|
+
|
40
|
+
describe "#messages" do
|
41
|
+
|
42
|
+
it "returns an empty array" do
|
43
|
+
expect(subject.messages).to eq []
|
44
|
+
end
|
45
|
+
|
46
|
+
it "can be initialized" do
|
47
|
+
subject = described_class.new(object, %w(cad cam))
|
48
|
+
expect(subject.messages).to eq %w(cad cam)
|
49
|
+
end
|
50
|
+
|
51
|
+
it "is immutable" do
|
52
|
+
expect(subject.messages).to be_frozen
|
53
|
+
end
|
54
|
+
|
55
|
+
end # describe #messages
|
56
|
+
|
57
|
+
end # describe Attestor::ValidError
|
@@ -0,0 +1,40 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
# describe #valid_policy and #invalid_policy builders
|
4
|
+
# also describes shared examples for all policies
|
5
|
+
require "support/policies"
|
6
|
+
|
7
|
+
describe Attestor::Policy::And do
|
8
|
+
|
9
|
+
subject { described_class.new items }
|
10
|
+
|
11
|
+
describe ".new" do
|
12
|
+
|
13
|
+
let(:items) { [valid_policy] }
|
14
|
+
|
15
|
+
it_behaves_like "creating a node"
|
16
|
+
it_behaves_like "creating an immutable object"
|
17
|
+
|
18
|
+
end # context
|
19
|
+
|
20
|
+
describe "#validate" do
|
21
|
+
|
22
|
+
context "when all the parts are valid" do
|
23
|
+
|
24
|
+
let(:items) { 3.times.map { valid_policy } }
|
25
|
+
|
26
|
+
it_behaves_like "passing validation"
|
27
|
+
|
28
|
+
end # context
|
29
|
+
|
30
|
+
context "when a part is invalid" do
|
31
|
+
|
32
|
+
let(:items) { [valid_policy, valid_policy, invalid_policy] }
|
33
|
+
|
34
|
+
it_behaves_like "failing validation"
|
35
|
+
|
36
|
+
end # context
|
37
|
+
|
38
|
+
end # describe #validate
|
39
|
+
|
40
|
+
end # describe Policy::Base::Not
|
@@ -0,0 +1,100 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe Attestor::Policy::Factory do
|
4
|
+
|
5
|
+
let(:test_class) { Class.new.send :include, described_class }
|
6
|
+
let(:subject) { test_class.new }
|
7
|
+
let(:policy) { double :policy }
|
8
|
+
let(:others) { 2.times.map { double } }
|
9
|
+
|
10
|
+
shared_examples "creating a node" do |composer|
|
11
|
+
|
12
|
+
it "[creates a Node]" do
|
13
|
+
expect(result).to be_kind_of composer
|
14
|
+
end
|
15
|
+
|
16
|
+
it "[sets policies]" do
|
17
|
+
expect(result.branches).to match_array [policy, *others]
|
18
|
+
end
|
19
|
+
|
20
|
+
end # shared examples
|
21
|
+
|
22
|
+
shared_examples "creating a negator" do |composer|
|
23
|
+
|
24
|
+
it "[creates a Negator]" do
|
25
|
+
expect(result).to be_kind_of Attestor::Policy::Negator
|
26
|
+
end
|
27
|
+
|
28
|
+
it "[sets a policy]" do
|
29
|
+
expect(result.policy).to eq policy
|
30
|
+
end
|
31
|
+
|
32
|
+
it "[sets a composer]" do
|
33
|
+
expect(result.composer).to eq composer
|
34
|
+
end
|
35
|
+
|
36
|
+
end # shared examples
|
37
|
+
|
38
|
+
describe "#and" do
|
39
|
+
|
40
|
+
context "with one argument" do
|
41
|
+
|
42
|
+
let(:result) { subject.and(policy, []) }
|
43
|
+
it_behaves_like "creating a negator", Attestor::Policy::And
|
44
|
+
|
45
|
+
end # context
|
46
|
+
|
47
|
+
context "with several arguments" do
|
48
|
+
|
49
|
+
let(:result) { subject.and(policy, others) }
|
50
|
+
it_behaves_like "creating a node", Attestor::Policy::And
|
51
|
+
|
52
|
+
end # context
|
53
|
+
|
54
|
+
end # describe #and
|
55
|
+
|
56
|
+
describe "#or" do
|
57
|
+
|
58
|
+
context "with one argument" do
|
59
|
+
|
60
|
+
let(:result) { subject.or(policy, []) }
|
61
|
+
it_behaves_like "creating a negator", Attestor::Policy::Or
|
62
|
+
|
63
|
+
end # context
|
64
|
+
|
65
|
+
context "with several arguments" do
|
66
|
+
|
67
|
+
let(:result) { subject.or(policy, others) }
|
68
|
+
it_behaves_like "creating a node", Attestor::Policy::Or
|
69
|
+
|
70
|
+
end # context
|
71
|
+
|
72
|
+
end # describe #or
|
73
|
+
|
74
|
+
describe "#xor" do
|
75
|
+
|
76
|
+
context "with one argument" do
|
77
|
+
|
78
|
+
let(:result) { subject.xor(policy, []) }
|
79
|
+
it_behaves_like "creating a negator", Attestor::Policy::Xor
|
80
|
+
|
81
|
+
end # context
|
82
|
+
|
83
|
+
context "with several arguments" do
|
84
|
+
|
85
|
+
let(:result) { subject.xor(policy, others) }
|
86
|
+
it_behaves_like "creating a node", Attestor::Policy::Xor
|
87
|
+
|
88
|
+
end # context
|
89
|
+
|
90
|
+
end # describe #or
|
91
|
+
|
92
|
+
describe "#not" do
|
93
|
+
|
94
|
+
let(:others) { [] }
|
95
|
+
let(:result) { subject.not(policy) }
|
96
|
+
it_behaves_like "creating a node", Attestor::Policy::Not
|
97
|
+
|
98
|
+
end # describe #or
|
99
|
+
|
100
|
+
end # describe Attestor::Policy::Factory
|
@@ -0,0 +1,57 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe Attestor::Policy::Negator do
|
4
|
+
|
5
|
+
let(:composer) { Attestor::Policy::Node }
|
6
|
+
let(:not_class) { Attestor::Policy::Not }
|
7
|
+
let(:policy) { double :policy }
|
8
|
+
|
9
|
+
subject { described_class.new composer, policy }
|
10
|
+
|
11
|
+
describe ".new" do
|
12
|
+
|
13
|
+
it "creates immutable object" do
|
14
|
+
expect(subject).to be_frozen
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
19
|
+
describe "#policy" do
|
20
|
+
|
21
|
+
it "is initialized" do
|
22
|
+
expect(subject.policy).to eq policy
|
23
|
+
end
|
24
|
+
|
25
|
+
end # describe #policy
|
26
|
+
|
27
|
+
describe "#composer" do
|
28
|
+
|
29
|
+
it "is initialized" do
|
30
|
+
expect(subject.composer).to eq composer
|
31
|
+
end
|
32
|
+
|
33
|
+
end # describe #composer
|
34
|
+
|
35
|
+
describe "#not" do
|
36
|
+
|
37
|
+
let(:another) { double :another }
|
38
|
+
let(:result) { subject.not(another) }
|
39
|
+
|
40
|
+
it "creates a composer object" do
|
41
|
+
expect(result).to be_kind_of composer
|
42
|
+
end
|
43
|
+
|
44
|
+
it "sends its policy to the composer" do
|
45
|
+
expect(result.branches).to include policy
|
46
|
+
end
|
47
|
+
|
48
|
+
it "sends the negated arguments to the composer" do
|
49
|
+
negation = double :negation
|
50
|
+
expect(not_class).to receive(:new).with(another).and_return(negation)
|
51
|
+
|
52
|
+
expect(result.branches).to include negation
|
53
|
+
end
|
54
|
+
|
55
|
+
end # describe #not
|
56
|
+
|
57
|
+
end # describe Policy::Base::Negator
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
describe Attestor::Policy::Node do
|
4
|
+
|
5
|
+
let(:policy_class) { Attestor::Policy }
|
6
|
+
let(:invalid_error) { Attestor::InvalidError }
|
7
|
+
|
8
|
+
describe ".new" do
|
9
|
+
|
10
|
+
it "creates a policy" do
|
11
|
+
expect(subject).to be_kind_of policy_class
|
12
|
+
end
|
13
|
+
|
14
|
+
it "creates immutable object" do
|
15
|
+
expect(subject).to be_frozen
|
16
|
+
end
|
17
|
+
|
18
|
+
end # describe .new
|
19
|
+
|
20
|
+
describe "#branches" do
|
21
|
+
|
22
|
+
let(:branches) { 3.times.map { double } }
|
23
|
+
|
24
|
+
it "are initialized from list" do
|
25
|
+
subject = described_class.new(*branches)
|
26
|
+
expect(subject.branches).to match_array branches
|
27
|
+
end
|
28
|
+
|
29
|
+
it "are initialized from array" do
|
30
|
+
subject = described_class.new(branches)
|
31
|
+
expect(subject.branches).to match_array branches
|
32
|
+
end
|
33
|
+
|
34
|
+
end # describe #branches
|
35
|
+
|
36
|
+
describe "#validate" do
|
37
|
+
|
38
|
+
it "raises InvalidError" do
|
39
|
+
expect { subject.validate }.to raise_error invalid_error
|
40
|
+
end
|
41
|
+
|
42
|
+
end # describe #validate
|
43
|
+
|
44
|
+
end # describe Attestor::Policy::Node
|