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