codeclimate-yaml 0.0.1

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