attestor 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- 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
|