yes 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,42 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ # Validate file glob match. This uess standard unix-style file matching,
6
+ # primarily '*` and `?`, to detrmine a mathing node value.
7
+ # All values are converted to strings (using #to_s) for comparison.
8
+ #--
9
+ # TODO: better name then `FNMatch`?
10
+ #++
11
+ class FNMatch < NodeConstraint
12
+
13
+ #
14
+ # @return [Array<Constraint>]
15
+ def self.checklist(spec, tree, nodes)
16
+ return [] unless applicable?(spec)
17
+ nodes.map do |node|
18
+ new(spec, tree, node)
19
+ end
20
+ end
21
+
22
+ # Only applicable if `fnmatch` field is in spec.
23
+ def self.applicable?(spec)
24
+ spec['fnmatch']
25
+ end
26
+
27
+ # Validate file glob match. This uess standard unix-style file matching,
28
+ # primarily '*` and `?`, to detrmine a mathing node value.
29
+ # All values are converted to strings (using #to_s) for comparison.
30
+ #
31
+ # @return [Boolean] validity
32
+ def validate(spec)
33
+ fnmatch = spec['fnmatch']
34
+
35
+ File.fnmatch(fnmatch, node.value)
36
+ end
37
+
38
+ end
39
+
40
+ end
41
+
42
+ end
@@ -0,0 +1,50 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ # Inclusion can either be a boolean expression in
6
+ # which case it validates that there is at least one matching
7
+ # node. Otherwise, the value is taken to be a ypath and validates
8
+ # that there are matching paths if the main selection is present.
9
+ #--
10
+ # TODO: Provide $parent$ path substitution ?
11
+ #++
12
+ class Inclusive < TreeConstraint
13
+
14
+ #
15
+ # @return [Array<Constraint>]
16
+ def self.checklist(spec, tree, nodes)
17
+ return [] unless applicable?(spec)
18
+ [new(spec, tree, nodes)]
19
+ end
20
+
21
+ # Only applicable if `inclusive` field in in the spec.
22
+ def self.applicable?(spec)
23
+ spec['inclusive']
24
+ end
25
+
26
+ # Validate inclusion - This can either be a boolean expression in
27
+ # which case it validates that there is at least one matching
28
+ # node. Otherwise, the value is taken to be a ypath and validates
29
+ # that there are matching paths if the main selection is present.
30
+ #
31
+ # @return [Boolean] validity
32
+ def valid?
33
+ return true unless applicable?
34
+
35
+ inclusive = spec['inclusive']
36
+
37
+ case inclusive
38
+ when true, false
39
+ nodes.size > 0
40
+ else
41
+ in_nodes = tree.select(inclusive)
42
+ nodes.size == 0 or in_nodes.size > 0
43
+ end
44
+ end
45
+
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,55 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ # Validate if a mapping node's _keys_ conforms to a constraint.
6
+ #
7
+ # //authors:
8
+ # type: map
9
+ # key:
10
+ # type: str
11
+ #
12
+ class Key < NodeConstraint
13
+
14
+ # For key constraint, the work is all handled by the
15
+ # checklist method.
16
+ #
17
+ # @return [Array<Constraint>]
18
+ def self.checklist(spec, tree, nodes)
19
+ return [] unless applicable?(spec)
20
+
21
+ key_spec = spec['key']
22
+ list = []
23
+
24
+ nodes.each do |node|
25
+ case node.kind
26
+ when :map
27
+ YES.constraints.each do |c|
28
+ list.concat(c.checklist(key_spec, tree, node.value.keys))
29
+ end
30
+ else
31
+ raise "key constraint applies only to mappings"
32
+ end
33
+ end
34
+
35
+ list
36
+ end
37
+
38
+ #
39
+ #
40
+ def self.applicable?(spec)
41
+ spec['key']
42
+ end
43
+
44
+ #
45
+ # no-op
46
+ #
47
+ # @return [Boolean] validity
48
+ def validate(spec)
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,34 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ # Validate the <i>kind of node</i>. There are only three kinds
6
+ # of nodes: `scalar`, `map` and `seq`.
7
+ #
8
+ class Kind < NodeConstraint
9
+
10
+ #
11
+ # @return [Array<Constraint>]
12
+ def self.checklist(spec, tree, nodes)
13
+ return [] unless applicable?(spec)
14
+ nodes.map do |node|
15
+ new(spec, tree, node)
16
+ end
17
+ end
18
+
19
+ #
20
+ def self.applicable?(spec)
21
+ spec['kind']
22
+ end
23
+
24
+ # Validate type.
25
+ #
26
+ def validate(spec)
27
+ node.kind.to_s == spec['kind']
28
+ end
29
+
30
+ end
31
+
32
+ end
33
+
34
+ end
@@ -0,0 +1,46 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ # Validate if a node's value is within a certain length.
6
+ # The value is converted to a string using #to_s for the
7
+ # comparison.
8
+ #
9
+ # //code:
10
+ # length: 3
11
+ #
12
+ # A valid code value could then have no more than three characters.
13
+ class Length < NodeConstraint
14
+
15
+ #
16
+ #
17
+ # @return [Array<Constraint>]
18
+ def self.checklist(spec, tree, nodes)
19
+ return [] unless applicable?(spec)
20
+ nodes.map do |node|
21
+ new(spec, tree, node)
22
+ end
23
+ end
24
+
25
+ #
26
+ #
27
+ def self.applicable?(spec)
28
+ spec['length']
29
+ end
30
+
31
+ #
32
+ # Validate if a node value is within a certain length.
33
+ # The value is converted to a string using #to_s for the
34
+ # comparison.
35
+ #
36
+ # @return [Boolean] validity
37
+ def validate(spec)
38
+ length = spec['length']
39
+ match_delta(length, node.value.to_s.size)
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+
46
+ end
@@ -0,0 +1,55 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ # The NodeConstraint class is an abstract class
6
+ # and used for create constraint subclasses that
7
+ # apply constraints on a sigle node.
8
+ #
9
+ class NodeConstraint < AbstractConstraint
10
+
11
+ # Like {Abstract#initialize} but takes a `node` qas well.
12
+ def initialize(spec, tree, node)
13
+ super(spec, tree, [node])
14
+ @node = node
15
+ end
16
+
17
+ public
18
+
19
+ # YAML Node.
20
+ attr :node
21
+
22
+ # Get the applicable node's tag.
23
+ def tag
24
+ node.type_id
25
+ end
26
+
27
+ # Get the applicable node's value.
28
+ def value
29
+ node.value
30
+ end
31
+
32
+ # # Covert a YAML node (Syck) node into a generic representation.
33
+ # #
34
+ # # TODO: Should `style` be part of this? Also, is `kind` the proper term?
35
+ # def catholic_node(node)
36
+ # n = {}
37
+ # n['tag'] = node.type_id
38
+ # #n['type'] = #base_type_id(node)
39
+ # n['kind'] = node.kind.to_s
40
+ # n['value'] = node.value
41
+ # n['style'] = node.instance_variable_get('@style').to_s
42
+ # n
43
+ # end
44
+
45
+ #
46
+ #
47
+ def self.applicable?(spec)
48
+ false
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
@@ -0,0 +1,43 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ # Validate the a nodes value is within a certain range.
6
+ # Primarily this works for numeric values, but it can
7
+ # also work for string in ASCII/UTF-8 order, by using
8
+ # a 2-element array for comparison.
9
+ #
10
+ # //note:
11
+ # range: ['A','G']
12
+ #
13
+ # Valid values for are then only A, B, C, D, E, F and G.
14
+ class Range < NodeConstraint
15
+
16
+ #
17
+ # @return [Array<Validaiton>]
18
+ def self.checklist(spec, tree, nodes)
19
+ return [] unless applicable?(spec)
20
+ nodes.map do |node|
21
+ new(spec, tree, node)
22
+ end
23
+ end
24
+
25
+ #
26
+ def self.applicable?(spec)
27
+ spec['range']
28
+ end
29
+
30
+ # Validate if a node is the only one of it's value in a sequence
31
+ # or mapping.
32
+ #
33
+ # @return [Boolean] validity
34
+ def validate(spec)
35
+ range = spec['range']
36
+ match_delta(range, node.transform) ? true : false
37
+ end
38
+
39
+ end
40
+
41
+ end
42
+
43
+ end
@@ -0,0 +1,52 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ # Validate matching values against a regular expression.
6
+ # All values are converted to strings (using #to_s) for comparison.
7
+ #
8
+ # //pin:
9
+ # regexp: /^\d\s\d\d$/
10
+ #
11
+ class Regexp < NodeConstraint
12
+
13
+ #
14
+ # @return [Array<Validaiton>]
15
+ def self.checklist(spec, tree, nodes)
16
+ return [] unless applicable?(spec)
17
+ nodes.map do |node|
18
+ new(spec, tree, node)
19
+ end
20
+ end
21
+
22
+ #
23
+ def self.applicable?(spec)
24
+ spec['regexp']
25
+ end
26
+
27
+ # Validate matching values against a regular expression.
28
+ # All values are converted to strings (using #to_s) for comparison.
29
+ #
30
+ # @return [Boolean] validity
31
+ def validate(spec)
32
+ regexp = parse_regexp(spec['regexp'])
33
+ regexp =~ node.value ? true : false
34
+ end
35
+
36
+ # The regular expression from `spec`.
37
+ #
38
+ # @return [Regexp] spec's regular expression
39
+ def parse_regexp(re)
40
+ case re
41
+ when /^\/(.*?)\/(\w*)$/
42
+ ::Regexp.new($1) # TODO: modifiers
43
+ else
44
+ ::Regexp.new(re)
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ end
51
+
52
+ end
@@ -0,0 +1,45 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ #
6
+ # TODO: For the moment this is the same as Inclusive.
7
+ # It was originall inteded to work like {RequiresValidation}
8
+ # but it rpoved hard to work out the validation procedure
9
+ # when matching to the subfield. If we can fix it maybe we will
10
+ # keep, but for now THIS IS NOT USED.
11
+ #
12
+ class Required < TreeConstraint
13
+
14
+ #
15
+ # @return [Array<Constraint>]
16
+ def self.checklist(spec, tree, nodes)
17
+ return [] unless applicable?(spec)
18
+ [new(spec, tree, nodes)]
19
+ end
20
+
21
+ # Only applicable if `required` field appears in spec.
22
+ def self.applicable?(spec)
23
+ spec['required']
24
+ end
25
+
26
+ # Validates whether a matching node must be present within it's parent.
27
+ #
28
+ # @return [Boolean] validity
29
+ def validate(spec)
30
+ required = spec['required']
31
+
32
+ case required
33
+ when true, false
34
+ nodes.size > 0
35
+ else
36
+ in_nodes = tree.select(required)
37
+ nodes.size == 0 or in_nodes.size > 0
38
+ end
39
+ end
40
+
41
+ end
42
+
43
+ end
44
+
45
+ end
@@ -0,0 +1,57 @@
1
+ module YES
2
+
3
+ module Constraints
4
+
5
+ # Takes a list of relative YPaths and ensures they are present.
6
+ # This is most useful for ensuring the existance of mapping fields.
7
+ #
8
+ # foo:
9
+ # requires:
10
+ # - bar
11
+ #
12
+ # A valid document would be:
13
+ #
14
+ # foo:
15
+ # bar: true
16
+ #
17
+ # The literal meaing of this example is "if `foo` exists, the make sure
18
+ # `foo/bar` also exists.
19
+ class Requires < NodeConstraint
20
+
21
+ #
22
+ # @return [Array<Constraint>]
23
+ def self.checklist(spec, tree, nodes)
24
+ return [] unless applicable?(spec)
25
+ nodes.map do |node|
26
+ new(spec, tree, node)
27
+ end
28
+ end
29
+
30
+ #
31
+ def self.applicable?(spec)
32
+ spec['requires']
33
+ end
34
+
35
+ # Validates whether a matching node must be present within it's parent.
36
+ #
37
+ # @return [Boolen] validity
38
+ def validate(spec)
39
+ requires = Array(spec['requires'])
40
+
41
+ requires.each do |rq|
42
+ case rq
43
+ when /^\// # absolute path
44
+ rq_nodes = tree.select(rq)
45
+ else
46
+ rq_nodes = node.select(rq)
47
+ end
48
+ return false unless rq_nodes.size > 0
49
+ end
50
+
51
+ end
52
+
53
+ end
54
+
55
+ end
56
+
57
+ end