codeclimate-yaml 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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: dac3e3ac75a2d03d789a2f8a444e79e26045698b
4
+ data.tar.gz: c25e962bc3e09d8d9f1ebdbc151189c26ce69f8c
5
+ SHA512:
6
+ metadata.gz: 8973baee70e58c3dbeddfd13fb45ba6d1ffadbd6e86ef79e932c36f71c853fcf1efdcdb35eb29fff6a66c44c92036ff18fe5074d799e20fa70bc6399b7e28fa6
7
+ data.tar.gz: 5f584e502651441c49993fba4743ad2d53473099f37ff7a57099a09f4febd9fddcd332e774974a827696c2bb1aa36fea04ee5d308bd44084c7266fae72c1d5e5
data/lib/cc/yaml.rb ADDED
@@ -0,0 +1,26 @@
1
+ module CC
2
+ module Yaml
3
+ autoload :Nodes, "cc/yaml/nodes"
4
+ autoload :Parser, "cc/yaml/parser"
5
+ autoload :Serializer, "cc/yaml/serializer"
6
+
7
+ def self.parse(value)
8
+ Parser.parse(value)
9
+ end
10
+
11
+ def self.parse!(value, file_name = ".codeclimate.yml", logger = Kernel)
12
+ result = parse(value)
13
+
14
+ result.nested_warnings.each do |key, message|
15
+ logger.warn key.empty? ? "#{file_name}: #{message}" :
16
+ "#{file_name}: #{key.join(?.)} section - #{message}"
17
+ end
18
+
19
+ result
20
+ end
21
+
22
+ def self.new
23
+ Nodes::Root.new
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,23 @@
1
+ module CC
2
+ module Yaml
3
+ module Nodes
4
+ autoload :Engine, "cc/yaml/nodes/engine"
5
+ autoload :EngineList, "cc/yaml/nodes/engine_list"
6
+ autoload :ExcludePath, "cc/yaml/nodes/exclude_path"
7
+ autoload :ExcludePathList, "cc/yaml/nodes/exclude_path_list"
8
+ autoload :Mapping, "cc/yaml/nodes/mapping"
9
+ autoload :Node, "cc/yaml/nodes/node"
10
+ autoload :OpenMapping, "cc/yaml/nodes/open_mapping"
11
+ autoload :Root, "cc/yaml/nodes/root"
12
+ autoload :Scalar, "cc/yaml/nodes/scalar"
13
+ autoload :Sequence, "cc/yaml/nodes/sequence"
14
+
15
+ def self.[](key)
16
+ return key if key.respond_to? :new
17
+ name = constants.detect { |c| c.downcase == key }
18
+ raise ArgumentError, "unknown node type %p" % key unless name
19
+ const_get(name)
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,9 @@
1
+ module CC
2
+ module Yaml
3
+ module Nodes
4
+ class Engine < Mapping
5
+ map :enabled, to: Scalar[:bool], required: true
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module CC
2
+ module Yaml
3
+ module Nodes
4
+ class EngineList < OpenMapping
5
+ default_type Engine
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,8 @@
1
+ module CC
2
+ module Yaml
3
+ module Nodes
4
+ class ExcludePath < Scalar
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module CC
2
+ module Yaml
3
+ module Nodes
4
+ class ExcludePathList < Sequence
5
+ type ExcludePath
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,173 @@
1
+ module CC::Yaml
2
+ module Nodes
3
+ class Mapping < Node
4
+ def self.mapping
5
+ @mapping ||= superclass.respond_to?(:mapping) ? superclass.mapping.dup : {}
6
+ end
7
+
8
+ def self.required
9
+ @required ||= superclass.respond_to?(:required) ? superclass.required.dup : []
10
+ end
11
+
12
+ def self.aliases
13
+ @aliases ||= superclass.respond_to?(:aliases) ? superclass.aliases.dup : {}
14
+ end
15
+
16
+ def self.map(*list)
17
+ options = Hash === list.last ? list.pop : {}
18
+ list.each do |key|
19
+ required << key.to_s if options[:required]
20
+ define_map_accessor(key)
21
+ case options[:to]
22
+ when Symbol then aliases[key.to_s] = options[:to].to_s
23
+ when Module then mapping[key.to_s] = options[:to]
24
+ when nil then mapping[key.to_s] = Nodes[key]
25
+ else raise ArgumentError, 'unexpected value for to: %p' % options[:to]
26
+ end
27
+ end
28
+ end
29
+
30
+ def self.define_map_accessor(key)
31
+ define_method(key) { | | self[key] } unless method_defined? key
32
+ define_method("#{key}=") { |v| self[key] = v } unless method_defined? "#{key}="
33
+ define_method("#{key}?") { | | !!self[key] } unless method_defined? "#{key}?"
34
+ end
35
+
36
+ def self.subnode_for(key)
37
+ mapping[aliases.fetch(key.to_s, key.to_s)]
38
+ end
39
+
40
+ attr_reader :mapping
41
+ alias_method :__getobj__, :mapping
42
+
43
+ def prepare
44
+ @mapping = {}
45
+ super
46
+ end
47
+
48
+ def visit_mapping(visitor, value)
49
+ visitor.apply_mapping(self, value)
50
+ end
51
+
52
+ def visit_pair(visitor, key, value)
53
+ key = visitor.generate_key(self, key)
54
+ visit_key_value(visitor, key, value)
55
+ end
56
+
57
+ def visit_key_value(visitor, key, value)
58
+ return warning("unexpected key %p, dropping", key) unless node = subnode_for(key)
59
+ warning("has multiple %p entries, keeping last entry", key) if self[key]
60
+ self[key] = node
61
+ visitor.accept(node, value)
62
+ end
63
+
64
+ def []=(key, value)
65
+ if mapped_key = mapped_key(key)
66
+ unless value.is_a? Node
67
+ node = subnode_for(mapped_key)
68
+ value = node if Parser::Ruby.new(value).parse(node)
69
+ end
70
+ @mapping[mapped_key] = value
71
+ else
72
+ warning("unexpected key %p, dropping", key)
73
+ end
74
+ end
75
+
76
+ def [](key)
77
+ @mapping[mapped_key(key)]
78
+ end
79
+
80
+ def include?(key)
81
+ @mapping.include? mapped_key(key)
82
+ end
83
+
84
+ def empty?
85
+ @mapping.empty?
86
+ end
87
+
88
+ def mapped_key(key)
89
+ key = self.class.aliases.fetch(key.to_s, key.to_s)
90
+ key if accept_key?(key)
91
+ end
92
+
93
+ def accept_key?(key)
94
+ self.class.mapping.include? key
95
+ end
96
+
97
+ def subnode_for(key)
98
+ type = self.class.subnode_for(key)
99
+ type.new(self) if type
100
+ end
101
+
102
+ def inspect
103
+ @mapping.inspect
104
+ end
105
+
106
+ def ==(other)
107
+ other = other.mapping if other.is_a? Mapping
108
+ if other.respond_to? :to_hash and other.to_hash.size == @mapping.size
109
+ other.to_hash.all? { |k, v| include?(k) and self[k] == v }
110
+ else
111
+ false
112
+ end
113
+ end
114
+
115
+ def verify
116
+ verify_errors
117
+ verify_required
118
+ verify_errors
119
+ end
120
+
121
+ def verify_required
122
+ self.class.required.each do |key|
123
+ next if @mapping.include? key
124
+ type = self.class.subnode_for(key)
125
+ if type.has_default?
126
+ warning "missing key %p, defaulting to %p", key, type.default
127
+ @mapping[key] = type.new(self)
128
+ else
129
+ error "missing key %p", key
130
+ end
131
+ end
132
+ end
133
+
134
+ def verify_errors
135
+ @mapping.delete_if do |key, value|
136
+ if value.errors?
137
+ warning "dropping %p section: %s", key, value.errors.join(', ')
138
+ true
139
+ end
140
+ end
141
+ end
142
+
143
+ def deep_verify
144
+ @mapping.each_value(&:deep_verify)
145
+ super
146
+ end
147
+
148
+ def nested_warnings(*prefix)
149
+ @mapping.inject(super) do |list, (key, value)|
150
+ list = value.nested_warnings(*prefix, key) + list
151
+ end
152
+ end
153
+
154
+ def with_value!(value)
155
+ value = value.mapping while value.is_a? Mapping
156
+ value.each { |key, value| self[key] = value }
157
+ end
158
+
159
+ def each_scalar(type = nil, &block)
160
+ return enum_for(:each_scalar, type) unless block
161
+ @mapping.each_value { |v| v.each_scalar(type, &block) }
162
+ end
163
+
164
+ protected
165
+
166
+ def dup_values
167
+ duped_mapping = @mapping.map { |key, value| [key.dup, value.dup] }
168
+ @mapping = Hash[duped_mapping]
169
+ self
170
+ end
171
+ end
172
+ end
173
+ end
@@ -0,0 +1,122 @@
1
+ module CC
2
+ module Yaml
3
+ module Nodes
4
+ class Node
5
+ def self.has_default?
6
+ false
7
+ end
8
+
9
+ attr_accessor :parent
10
+
11
+ def initialize(parent)
12
+ @nested_warnings = []
13
+ @parent = parent
14
+ prepare
15
+ yield self if block_given?
16
+ end
17
+
18
+ def warnings?
19
+ warnings.any?
20
+ end
21
+
22
+ def errors?
23
+ errors.any?
24
+ end
25
+
26
+ def warnings
27
+ @warnings ||= []
28
+ end
29
+
30
+ def nested_warning(message, *prefix)
31
+ @nested_warnings << [prefix, message]
32
+ end
33
+
34
+ def nested_warnings(*prefix)
35
+ messages = errors + warnings
36
+ prefixed = messages.map { |warning| [prefix, warning] }
37
+ prefixed += @nested_warnings.map { |p, w| [prefix + p, w] }
38
+ prefixed
39
+ end
40
+
41
+ def errors
42
+ @errors ||= []
43
+ end
44
+
45
+ def warning(message, *params)
46
+ warnings << message % params
47
+ end
48
+
49
+ def error(message, *params)
50
+ errors << message % params
51
+ end
52
+
53
+ def prepare
54
+ end
55
+
56
+ def verify
57
+ end
58
+
59
+ def deep_verify
60
+ verify
61
+ end
62
+
63
+ def visit_unexpected(visitor, value, message = nil)
64
+ error(message || "unexpected %p", value)
65
+ end
66
+
67
+ def visit_mapping(visitor, value)
68
+ error("unexpected mapping")
69
+ end
70
+
71
+ def visit_pair(visitor, key, value)
72
+ error("unexpected pair")
73
+ end
74
+
75
+ def visit_scalar(visitor, type, value, implicit = true)
76
+ error("unexpected scalar") unless type == :null
77
+ end
78
+
79
+ def visit_sequence(visitor, value)
80
+ error("unexpected sequence")
81
+ end
82
+
83
+ def visit_child(visitor, value)
84
+ error("unexpected child")
85
+ end
86
+
87
+ def respond_to_missing?(method, include_private = false)
88
+ __getobj__.respond_to?(method, false)
89
+ end
90
+
91
+ def method_missing(method, *args, &block)
92
+ return super unless __getobj__.respond_to?(method)
93
+ __getobj__.public_send(method, *args, &block)
94
+ end
95
+
96
+ def to_s
97
+ __getobj__.to_s
98
+ end
99
+
100
+ def to_json(options = nil)
101
+ Serializer::Json.serialize(self, options)
102
+ end
103
+
104
+ def with_value(value)
105
+ node = dup
106
+ node.with_value!(value)
107
+ node
108
+ end
109
+
110
+ def dup
111
+ super.dup_values
112
+ end
113
+
114
+ protected
115
+
116
+ def dup_values
117
+ self
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,18 @@
1
+ module CC::Yaml
2
+ module Nodes
3
+ class OpenMapping < Mapping
4
+ def self.default_type(identifier = nil)
5
+ @default_type = Nodes[identifier] if identifier
6
+ @default_type ||= superclass.respond_to?(:default_type) ? superclass.default_type : Scalar
7
+ end
8
+
9
+ def self.subnode_for(key)
10
+ super(key) || default_type
11
+ end
12
+
13
+ def accept_key?(key)
14
+ true
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,22 @@
1
+ module CC
2
+ module Yaml
3
+ module Nodes
4
+ class Root < Mapping
5
+ map :exclude_paths, to: ExcludePathList
6
+ map :engines, to: EngineList
7
+
8
+ def initialize
9
+ super(nil)
10
+ end
11
+
12
+ def nested_warnings(*)
13
+ super.uniq
14
+ end
15
+
16
+ def inspect
17
+ "#<#{self.class.name}:#{super}>"
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,112 @@
1
+ module CC::Yaml
2
+ module Nodes
3
+ class Scalar < Node
4
+ def self.[](*types)
5
+ Class.new(self) do
6
+ default_type(types.first)
7
+ cast(*types)
8
+ end
9
+ end
10
+
11
+ def self.cast?(type)
12
+ cast.include? type
13
+ end
14
+
15
+ def self.cast(*types)
16
+ @cast ||= superclass.respond_to?(:cast) ? superclass.cast.dup : []
17
+ @cast.concat(types) if types.any?
18
+ @cast
19
+ end
20
+
21
+ def self.default_type(type = nil)
22
+ @default_type = type if type
23
+ @default_type ||= superclass.respond_to?(:default_type) ? superclass.default_type : :str
24
+ end
25
+
26
+ def self.default(value = nil)
27
+ @default = value if value
28
+ @default ||= nil
29
+ end
30
+
31
+ def self.has_default?
32
+ !default.nil?
33
+ end
34
+
35
+ attr_accessor :value
36
+ alias_method :__getobj__, :value
37
+
38
+ def empty?
39
+ value.nil?
40
+ end
41
+
42
+ def prepare
43
+ self.value = self.class.default
44
+ @multiple = false
45
+ super
46
+ end
47
+
48
+ def ==(other)
49
+ other = other.value if other.is_a? self.class
50
+ other == value
51
+ end
52
+
53
+ def default_type
54
+ self.class.default_type
55
+ end
56
+
57
+ def inspect
58
+ value.inspect
59
+ end
60
+
61
+ def visit_scalar(visitor, type, value, implicit = true)
62
+ return self.value = cast(visitor, type, value) if cast? type
63
+ return self.value = cast(visitor, default_type, value) if implicit
64
+ error "%p not supported, dropping %p", type.to_s, cast(visitor, :str, value)
65
+ end
66
+
67
+ def visit_sequence(visitor, value)
68
+ @multiple = false
69
+ visitor.apply_sequence(self, value)
70
+ end
71
+
72
+ def visit_child(visitor, value)
73
+ if @multiple
74
+ value = cast(visitor, :str, value) rescue value
75
+ warning "does not support multiple values, dropping %p", value
76
+ else
77
+ @multiple = true
78
+ visitor.accept(self, value)
79
+ end
80
+ end
81
+
82
+ def cast(visitor, type, value)
83
+ visitor.cast(type, value)
84
+ rescue ArgumentError => error
85
+ error "failed to parse %p - %s", type.to_s, error.message.sub("():", ":")
86
+ end
87
+
88
+ def cast?(type)
89
+ self.class.cast?(type) or type == default_type
90
+ end
91
+
92
+ def !@
93
+ !value
94
+ end
95
+
96
+ def with_value(value)
97
+ return value.dup if value.is_a? self.class
98
+ value = value.value while value.is_a? Scalar
99
+ super(value)
100
+ end
101
+
102
+ def with_value!(value)
103
+ self.value = value
104
+ end
105
+
106
+ def each_scalar(type = nil, &block)
107
+ return enum_for(:each_scalar, type) unless block
108
+ yield value if type.nil? or type === value
109
+ end
110
+ end
111
+ end
112
+ end
@@ -0,0 +1,122 @@
1
+ module CC::Yaml
2
+ module Nodes
3
+ class Sequence < Node
4
+ attr_reader :children
5
+ alias_method :__getobj__, :children
6
+
7
+ def self.[](node_type)
8
+ node_type = Scalar[node_type] unless node_type.is_a? Node
9
+ Class.new(self) { type(node_type) }
10
+ end
11
+
12
+ def self.type(identifier = nil)
13
+ @type = Nodes[identifier] if identifier
14
+ @type ||= superclass.respond_to?(:type) ? superclass.type : Scalar
15
+ end
16
+
17
+ def prepare
18
+ @children = []
19
+ end
20
+
21
+ def visit_sequence(visitor, value)
22
+ visitor.apply_sequence(self, value)
23
+ end
24
+
25
+ def visit_scalar(visitor, type, value, implicit = true)
26
+ visit_child(visitor, value) if type != :null
27
+ end
28
+
29
+ def visit_mapping(visitor, value)
30
+ visit_child(visitor, value)
31
+ end
32
+
33
+ def visit_child(visitor, value)
34
+ child = self.class.type.new(self)
35
+ visitor.accept(child, value)
36
+ @children << child
37
+ end
38
+
39
+ def nested_warnings(*prefix)
40
+ @children.inject(super) do |list, value|
41
+ list = value.nested_warnings(*prefix) + list
42
+ end
43
+ end
44
+
45
+ def ==(other)
46
+ other = other.children if other.is_a? Sequence
47
+ if other.respond_to? :to_a and other.to_a.size == children.size
48
+ children.zip(other.to_a).all? { |a, b| a == b }
49
+ else
50
+ identifier == other
51
+ end
52
+ end
53
+
54
+ def empty?
55
+ @children.empty?
56
+ end
57
+
58
+ def inspect
59
+ identifier.inspect
60
+ end
61
+
62
+ def to_s
63
+ identifier.to_s
64
+ end
65
+
66
+ def identifier
67
+ @children.size == 1 ? @children.first : @children
68
+ end
69
+
70
+ def verify
71
+ verify_children
72
+ super
73
+ end
74
+
75
+ def verify_children
76
+ @children.delete_if do |child|
77
+ next unless child.errors?
78
+ child.errors.each { |message| warning(message) }
79
+ true
80
+ end
81
+ end
82
+
83
+ def deep_verify
84
+ @children.each(&:deep_verify)
85
+ super
86
+ end
87
+
88
+ def each_scalar(type = nil, &block)
89
+ return enum_for(:each_scalar, type) unless block
90
+ @children.each { |c| c.each_scalar(type, &block) }
91
+ end
92
+
93
+ def with_value(value)
94
+ return value.dup if value.is_a? self.class
95
+ value = value.children if value.is_a? Sequence
96
+ value = value.value while value.is_a? Scalar
97
+ Parser::Ruby.new(Array(value)).parse self.class.new(parent)
98
+ end
99
+
100
+ def with_value!(value)
101
+ children.replace with_value(value).children
102
+ end
103
+
104
+ def add_value(value)
105
+ added = with_value(self)
106
+ added.add_value!(value)
107
+ added
108
+ end
109
+
110
+ def add_value!(value)
111
+ children.concat(with_value(value).children)
112
+ end
113
+
114
+ protected
115
+
116
+ def dup_values
117
+ @children = @children.map { |child| child.dup }
118
+ self
119
+ end
120
+ end
121
+ end
122
+ end
@@ -0,0 +1,11 @@
1
+ module CC
2
+ module Yaml
3
+ module Parser
4
+ autoload :Psych, "cc/yaml/parser/psych"
5
+
6
+ def self.parse(value)
7
+ Psych.parse(value)
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,223 @@
1
+ require 'psych'
2
+ require 'delegate'
3
+
4
+ module CC::Yaml
5
+ module Parser
6
+ class Psych
7
+ class SetNode < DelegateClass(::Psych::Nodes::Mapping)
8
+ def children
9
+ super.select.with_index { |_,i| i.even? }
10
+ end
11
+ end
12
+
13
+ class ScalarSequence < DelegateClass(::Psych::Nodes::Mapping)
14
+ def children
15
+ [__getobj__]
16
+ end
17
+ end
18
+
19
+ MAP = /\A(?:tag:yaml\.org,2002:|!!?)map\z/
20
+ OMAP = /\A(?:tag:yaml\.org,2002:|!!?)omap\z/
21
+ PAIRS = /\A(?:tag:yaml\.org,2002:|!!?)pairs\z/
22
+ SET = /\A(?:tag:yaml\.org,2002:|!!?)set\z/
23
+ SEQ = /\A(?:tag:yaml\.org,2002:|!!?)seq\z/
24
+ BINARY = /\A(?:tag:yaml\.org,2002:|!!?)binary\z/
25
+ BOOL = /\A(?:tag:yaml\.org,2002:|!!?)bool\z/
26
+ FLOAT = /\A(?:tag:yaml\.org,2002:|!!?)float\z/
27
+ INT = /\A(?:tag:yaml\.org,2002:|!!?)int\z/
28
+ MERGE = /\A(?:tag:yaml\.org,2002:|!!?)merge\z/
29
+ NULL = /\A(?:tag:yaml\.org,2002:|!!?)null\z/
30
+ STR = /\A(?:tag:yaml\.org,2002:|!!?)str\z/
31
+ TIMESTAMP = /\A(?:tag:yaml\.org,2002:|!!?)timestamp\z/
32
+ VALUE = /\A(?:tag:yaml\.org,2002:|!!?)value\z/
33
+ YAML = /\A(?:tag:yaml\.org,2002:|!!?)yaml\z/
34
+ SECURE = /\A!(?:encrypted|secure|decrypted)\z/
35
+ TRUE = /\A(?:y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON)\z/
36
+ FALSE = /\A(?:n|N|no|No|NO|false|False|FALSE|off|Off|OFF)\z/
37
+ REGEXP = /\A!(?:ruby\/)?regexp\z/
38
+ REG_FLAGS = { 'i' => Regexp::IGNORECASE, 'm' => Regexp::MULTILINE, 'x' => Regexp::EXTENDED }
39
+ FORMATS = {
40
+ '!bool' => Regexp.union(TRUE, FALSE),
41
+ '!float' => ::Psych::ScalarScanner::FLOAT,
42
+ '!null' => /\A(:?~|null|Null|NULL|)\z/,
43
+ '!timestamp' => ::Psych::ScalarScanner::TIME,
44
+ '!int' => ::Psych::ScalarScanner::INTEGER,
45
+ '!regexp' => /\A\/(.*)\/([imx]*)\z/
46
+ }
47
+
48
+ if defined? ::Psych::ClassLoader
49
+ CLASS_LOADER = ::Psych::ClassLoader.new
50
+ class ScalarScanner < ::Psych::ScalarScanner
51
+ def initialize
52
+ super(CLASS_LOADER)
53
+ end
54
+ end
55
+ else
56
+ ScalarScanner = ::Psych::ScalarScanner
57
+ end
58
+
59
+ def self.parses?(value)
60
+ return true if value.is_a?(::Psych::Nodes::Node)
61
+ return true if value.is_a?(String) or value.is_a?(IO)
62
+ return true if defined?(StringIO) and value.is_a?(StringIO)
63
+ value.respond_to?(:to_str) or value.respond_to?(:to_io)
64
+ end
65
+
66
+ def self.parse(value)
67
+ new(value).parse
68
+ end
69
+
70
+ def initialize(value)
71
+ value = value.to_str if value.respond_to? :to_str
72
+ value = value.to_io if value.respond_to? :to_io
73
+ @value = value
74
+ @scanner = ScalarScanner.new
75
+ end
76
+
77
+ def parse(root = nil)
78
+ root ||= CC::Yaml::Nodes::Root.new
79
+ parsed = @value if @value.is_a? ::Psych::Nodes::Node
80
+ parsed ||= ::Psych.parse(@value)
81
+ accept(root, parsed)
82
+ root
83
+ rescue ::Psych::SyntaxError => error
84
+ root.verify
85
+ root.warnings.clear
86
+ root.error("syntax error: %s", error.message)
87
+ root
88
+ end
89
+
90
+ def accept(node, value)
91
+ case value
92
+ when ::Psych::Nodes::Scalar then accept_scalar node, value
93
+ when ::Psych::Nodes::Mapping then accept_mapping node, value
94
+ when ::Psych::Nodes::Sequence then accept_sequence node, value
95
+ when ::Psych::Nodes::Alias then accept_alias node, value
96
+ when ::Psych::Nodes::Document then accept node, value.root
97
+ when ::Psych::Nodes::Stream then accept_sequence node, value
98
+ else node.visit_unexpected(self, value) if value
99
+ end
100
+ node.verify
101
+ end
102
+
103
+ def accept_sequence(node, value)
104
+ case value.tag
105
+ when SET, SEQ
106
+ node.visit_sequence self, value
107
+ when nil
108
+ value = ScalarSequence.new(value) unless value.is_a? ::Psych::Nodes::Sequence
109
+ node.visit_sequence self, value
110
+ else
111
+ node.visit_sequence self, ScalarSequence.new(value)
112
+ end
113
+ end
114
+
115
+ def accept_mapping(node, value)
116
+ case value.tag
117
+ when MAP, OMAP, PAIRS then node.visit_mapping self, value
118
+ when SET then node.visit_sequence self, SetNode.new(value)
119
+ when SEQ then node.visit_sequence self, value
120
+ when nil
121
+ if value.children.size == 2 and value.children.first.value == 'secure'
122
+ secret_value = value.children.last
123
+ if secret_value.is_a? ::Psych::Nodes::Scalar
124
+ secret_value.tag ||= '!secure'
125
+ node.visit_scalar(self, :secure, secret_value, false)
126
+ else
127
+ node.visit_unexpected(self, value, "secret value needs to be a string")
128
+ end
129
+ else
130
+ node.visit_mapping(self, value)
131
+ end
132
+ else
133
+ node.visit_unexpected self, value, "unexpected tag %p for mapping" % value.tag
134
+ end
135
+ end
136
+
137
+ def accept_scalar(node, value)
138
+ case tag = scalar_tag(value)
139
+ when BINARY then node.visit_scalar self, :binary, value, value.tag.nil?
140
+ when BOOL then node.visit_scalar self, :bool, value, value.tag.nil?
141
+ when FLOAT then node.visit_scalar self, :float, value, value.tag.nil?
142
+ when INT then node.visit_scalar self, :int, value, value.tag.nil?
143
+ when NULL then node.visit_scalar self, :null, value, value.tag.nil?
144
+ when STR then node.visit_scalar self, :str, value, value.tag.nil?
145
+ when TIMESTAMP then node.visit_scalar self, :time, value, value.tag.nil?
146
+ when SECURE then node.visit_scalar self, :secure, value, value.tag.nil?
147
+ when NULL then node.visit_scalar self, :null, value, value.tag.nil?
148
+ when REGEXP then node.visit_scalar self, :regexp, value, value.tag.nil?
149
+ else node.visit_unexpected self, value, "unexpected tag %p for scalar %p" % [tag, simple(value)]
150
+ end
151
+ end
152
+
153
+ def simple(value)
154
+ case value
155
+ when ::Psych::Nodes::Scalar then value.value
156
+ when ::Psych::Nodes::Mapping then simple_mapping(value)
157
+ when ::Psych::Nodes::Sequence then value.children.map { |c| simple(c) }
158
+ when ::Psych::Nodes::Document then simple(value.root)
159
+ when ::Psych::Nodes::Stream then value.children.map { |c| simple(c) }
160
+ else value
161
+ end
162
+ end
163
+
164
+ def simple_mapping(value)
165
+ children = {}
166
+ keys, values = value.children.group_by.with_index { |_,i| i.even? }.values_at(true, false)
167
+ keys.zip(values) { |key, value| children[simple(key)] = simple(value) } if keys and values
168
+ children
169
+ end
170
+
171
+ def scalar_tag(value)
172
+ return value.tag if value.tag
173
+ return '!str' if value.quoted
174
+ FORMATS.each do |tag, format|
175
+ return tag if value.value =~ format
176
+ end
177
+ '!str'
178
+ end
179
+
180
+ def regexp(pattern)
181
+ return pattern if pattern.is_a? Regexp
182
+ return Regexp.new(pattern) unless pattern =~ FORMATS['!regexp']
183
+ flag = $2.chars.inject(0) { |f,c| f | REG_FLAGS.fetch(c, 0) }
184
+ Regexp.new($1, flag)
185
+ rescue RegexpError => error
186
+ raise ArgumentError, "broken regular expression - #{error.message}"
187
+ end
188
+
189
+ def cast(type, value)
190
+ case type
191
+ when :str then value.value
192
+ when :binary then value.value.unpack('m').first
193
+ when :bool then value.value !~ FALSE
194
+ when :float then Float @scanner.tokenize(value.value)
195
+ when :int then Integer @scanner.tokenize(value.value)
196
+ when :time then @scanner.parse_time(value.value)
197
+ when :secure then SecureString.new(value.value, value.tag != '!decrypted')
198
+ when :regexp then regexp(value.value)
199
+ when :null then nil
200
+ else raise ArgumentError, 'unknown scalar type %p' % type
201
+ end
202
+ end
203
+
204
+ def apply_mapping(node, value)
205
+ keys, values = value.children.group_by.with_index { |_,i| i.even? }.values_at(true, false)
206
+ keys.zip(values) { |key, value| node.visit_pair(self, key, value) } if keys and values
207
+ end
208
+
209
+ def apply_sequence(node, value)
210
+ value.children.each { |child| node.visit_child(self, child) }
211
+ end
212
+
213
+ def generate_key(node, value)
214
+ if value.respond_to? :value and (value.tag.nil? || value.tag == STR)
215
+ value = value.value.to_s
216
+ value.start_with?(?:) ? value[1..-1] : value
217
+ else
218
+ node.visit_unexpected(self, value, "expected string as key")
219
+ end
220
+ end
221
+ end
222
+ end
223
+ end
@@ -0,0 +1,10 @@
1
+ module CC
2
+ module Yaml
3
+ module Serializer
4
+ NotSupportedError = Class.new(ArgumentError)
5
+
6
+ autoload :Generic, "cc/yaml/serializer/generic"
7
+ autoload :Json, "cc/yaml/serializer/json"
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,114 @@
1
+ module CC::Yaml
2
+ module Serializer
3
+ class Generic
4
+ attr_reader :options
5
+
6
+ def self.serialize(node, options = nil)
7
+ new(options).serialize(node)
8
+ end
9
+
10
+ def initialize(options = nil)
11
+ @options = options || {}
12
+ end
13
+
14
+ def symbol_keys?
15
+ !!options[:symbol_keys]
16
+ end
17
+
18
+ def serialize(node)
19
+ case node
20
+ when Nodes::Root then serialize_root(node)
21
+ when Nodes::Scalar then serialize_scalar(node)
22
+ when Nodes::Mapping then serialize_mapping(node)
23
+ when Nodes::Sequence then serialize_sequence(node)
24
+ else raise NotSupportedError, 'do not know how to serialize %p' % node.class
25
+ end
26
+ end
27
+
28
+ def serialize_scalar(node)
29
+ case value = node.value
30
+ when true, false then serialize_bool(value)
31
+ when Float then serialize_float(value)
32
+ when Integer then serialize_integer(value)
33
+ when Time then serialize_time(value)
34
+ when SecureString then serialize_secure(value)
35
+ when Regexp then serialize_regexp(value)
36
+ when String
37
+ value.encoding == Encoding::BINARY ? serialize_binary(value) : serialize_str(value)
38
+ else
39
+ serialize_value(node)
40
+ end
41
+ end
42
+
43
+ def serialize_bool(value)
44
+ serialize_value(value)
45
+ end
46
+
47
+ def serialize_float(value)
48
+ serialize_value(value)
49
+ end
50
+
51
+ def serialize_time(value)
52
+ serialize_value(value)
53
+ end
54
+
55
+ def serialize_secure(value)
56
+ case options[:secure]
57
+ when :decrypted
58
+ raise ArgumentError, 'secure option is set decrypted, but a secure value is not decrypted' unless value.decrypted?
59
+ serialize_decrypted(value)
60
+ when :encrypted
61
+ raise ArgumentError, 'secure option is set encrypted, but a secure value is not encrypted' unless value.encrypted?
62
+ serialize_encrypted(value)
63
+ else
64
+ raise ArgumentError, 'unexpected value for secure option: %p' % options[:secure] if options[:secure]
65
+ value.encrypted? ? serialize_encrypted(value) : serialize_decrypted(value)
66
+ end
67
+ end
68
+
69
+ def serialize_encrypted(value)
70
+ serialize_value(value)
71
+ end
72
+
73
+ def serialize_decrypted(value)
74
+ serialize_value(value)
75
+ end
76
+
77
+ def serialize_regexp(value)
78
+ serialize_value(value)
79
+ end
80
+
81
+ def serialize_str(value)
82
+ serialize_value(value)
83
+ end
84
+
85
+ def serialize_binary(value)
86
+ serialize_str(value)
87
+ end
88
+
89
+ def serialize_bool(value)
90
+ serialize_value(value)
91
+ end
92
+
93
+ def serialize_value(node)
94
+ raise NotSupportedError, 'cannot serialize %p with %p' % [node.class, self.class]
95
+ end
96
+
97
+ def serialize_root(node)
98
+ serialize_mapping(node)
99
+ end
100
+
101
+ def serialize_mapping(node)
102
+ node.map { |key, value| [serialize_key(key), serialize(value)] }
103
+ end
104
+
105
+ def serialize_sequence(node)
106
+ node.map { |value| serialize(value) }
107
+ end
108
+
109
+ def serialize_key(value)
110
+ symbol_keys? ? value.to_sym : value.to_s
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,74 @@
1
+ module CC
2
+ module Yaml
3
+ module Serializer
4
+ class Json < Generic
5
+ MAP = { # mapping stolen from json gem
6
+ "\x0" => '\u0000', "\x1" => '\u0001', "\x2" => '\u0002', "\x3" => '\u0003', "\x4" => '\u0004', "\x5" => '\u0005',
7
+ "\x6" => '\u0006', "\x7" => '\u0007', "\b" => '\b', "\t" => '\t', "\n" => '\n', "\xb" => '\u000b',
8
+ "\f" => '\f', "\r" => '\r', "\xe" => '\u000e', "\xf" => '\u000f', "\x10" => '\u0010', "\x11" => '\u0011',
9
+ "\x12" => '\u0012', "\x13" => '\u0013', "\x14" => '\u0014', "\x15" => '\u0015', "\x16" => '\u0016', "\x17" => '\u0017',
10
+ "\x18" => '\u0018', "\x19" => '\u0019', "\x1a" => '\u001a', "\x1b" => '\u001b', "\x1c" => '\u001c', "\x1d" => '\u001d',
11
+ "\x1e" => '\u001e', "\x1f" => '\u001f', '"' => '\"', '\\' => '\\\\'
12
+ }
13
+
14
+ def pretty?
15
+ !!options[:pretty]
16
+ end
17
+
18
+ def serialize_float(value)
19
+ raise NotSupportedError, 'cannot serialize infinity as JSON' if value.infinite?
20
+ "#{value}"
21
+ end
22
+
23
+ def serialize_encrypted(value)
24
+ key_value("secure", serialize_str(value.encrypted_string), "{%s}")
25
+ end
26
+
27
+ def serialize_decrypted(value)
28
+ serialize_str(value.decrypted_string)
29
+ end
30
+
31
+ def serialize_str(value)
32
+ string = value.encode('utf-8')
33
+ string.force_encoding('binary')
34
+ string.gsub!(/["\\\x0-\x1f]/) { MAP[$&] }
35
+ string.force_encoding('utf-8')
36
+ "\"#{string}\""
37
+ end
38
+
39
+ def serialize_binary(value)
40
+ raise NotSupportedError, 'cannot serialize binary data as JSON'
41
+ end
42
+
43
+ def serialize_bool(value)
44
+ value ? "true" : "false"
45
+ end
46
+
47
+ def serialize_mapping(node)
48
+ lines('{%s}', super.map { |key, value| key_value(key, value) })
49
+ end
50
+
51
+ def serialize_sequence(node)
52
+ lines('[%s]', super)
53
+ end
54
+
55
+ def key_value(key, value, wrapper = "%s")
56
+ space = pretty? ? " " : ""
57
+ wrapper % "#{serialize_str(key)}:#{space}#{value}"
58
+ end
59
+
60
+ def lines(wrapper, lines)
61
+ return wrapper % lines.join(',') unless pretty?
62
+ return wrapper % "" if lines.empty?
63
+ return wrapper % " #{lines.first} " unless lines.size > 1 or lines.first.include?("\n") or lines.first.size > 50
64
+ lines = "\n " + lines.join(",\n").strip.gsub("\n", "\n ") + "\n"
65
+ wrapper % lines
66
+ end
67
+
68
+ def serialize_key(value)
69
+ value.to_s
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,5 @@
1
+ module CC
2
+ module Yaml
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,61 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: codeclimate-yaml
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Code Climate
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-08 00:00:00.000000000 Z
12
+ dependencies: []
13
+ description: Code Climate YAML parser
14
+ email: hello@codeclimate.com
15
+ executables: []
16
+ extensions: []
17
+ extra_rdoc_files: []
18
+ files:
19
+ - lib/cc/yaml.rb
20
+ - lib/cc/yaml/nodes.rb
21
+ - lib/cc/yaml/nodes/engine.rb
22
+ - lib/cc/yaml/nodes/engine_list.rb
23
+ - lib/cc/yaml/nodes/exclude_path.rb
24
+ - lib/cc/yaml/nodes/exclude_path_list.rb
25
+ - lib/cc/yaml/nodes/mapping.rb
26
+ - lib/cc/yaml/nodes/node.rb
27
+ - lib/cc/yaml/nodes/open_mapping.rb
28
+ - lib/cc/yaml/nodes/root.rb
29
+ - lib/cc/yaml/nodes/scalar.rb
30
+ - lib/cc/yaml/nodes/sequence.rb
31
+ - lib/cc/yaml/parser.rb
32
+ - lib/cc/yaml/parser/psych.rb
33
+ - lib/cc/yaml/serializer.rb
34
+ - lib/cc/yaml/serializer/generic.rb
35
+ - lib/cc/yaml/serializer/json.rb
36
+ - lib/cc/yaml/version.rb
37
+ homepage: https://codeclimate.com
38
+ licenses:
39
+ - MIT
40
+ metadata: {}
41
+ post_install_message:
42
+ rdoc_options: []
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ version: '0'
50
+ required_rubygems_version: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ requirements: []
56
+ rubyforge_project:
57
+ rubygems_version: 2.4.5
58
+ signing_key:
59
+ specification_version: 4
60
+ summary: Code Climate YAML
61
+ test_files: []