Class: Travis::Yaml::Parser::Psych

Inherits:
Object
  • Object
show all
Defined in:
lib/travis/yaml/parser/psych.rb

Defined Under Namespace

Classes: ScalarScanner, ScalarSequence, SetNode

Constant Summary collapse

MAP =
/\A(?:tag:yaml\.org,2002:|!!?)map\z/
OMAP =
/\A(?:tag:yaml\.org,2002:|!!?)omap\z/
PAIRS =
/\A(?:tag:yaml\.org,2002:|!!?)pairs\z/
SET =
/\A(?:tag:yaml\.org,2002:|!!?)set\z/
SEQ =
/\A(?:tag:yaml\.org,2002:|!!?)seq\z/
BINARY =
/\A(?:tag:yaml\.org,2002:|!!?)binary\z/
BOOL =
/\A(?:tag:yaml\.org,2002:|!!?)bool\z/
FLOAT =
/\A(?:tag:yaml\.org,2002:|!!?)float\z/
INT =
/\A(?:tag:yaml\.org,2002:|!!?)int\z/
MERGE =
/\A(?:tag:yaml\.org,2002:|!!?)merge\z/
NULL =
/\A(?:tag:yaml\.org,2002:|!!?)null\z/
STR =
/\A(?:tag:yaml\.org,2002:|!!?)str\z/
TIMESTAMP =
/\A(?:tag:yaml\.org,2002:|!!?)timestamp\z/
VALUE =
/\A(?:tag:yaml\.org,2002:|!!?)value\z/
YAML =
/\A(?:tag:yaml\.org,2002:|!!?)yaml\z/
SECURE =
/\A!(?:encrypted|secure|decrypted)\z/
TRUE =
/\A(?:y|Y|yes|Yes|YES|true|True|TRUE|on|On|ON)\z/
FALSE =
/\A(?:n|N|no|No|NO|false|False|FALSE|off|Off|OFF)\z/
REGEXP =
/\A!(?:ruby\/)?regexp\z/
REG_FLAGS =
{ 'i' => Regexp::IGNORECASE, 'm' => Regexp::MULTILINE, 'x' => Regexp::EXTENDED }
FORMATS =
{
  '!bool'      => Regexp.union(TRUE, FALSE),
  '!float'     => ::Psych::ScalarScanner::FLOAT,
  '!null'      => /\A(:?~|null|Null|NULL|)\z/,
  '!timestamp' => ::Psych::ScalarScanner::TIME,
  '!int'       => ::Psych::ScalarScanner::INTEGER,
  '!regexp'    => /\A\/(.*)\/([imx]*)\z/
}
CLASS_LOADER =
::Psych::ClassLoader.new

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(value) ⇒ Psych

Returns a new instance of Psych.



70
71
72
73
74
75
# File 'lib/travis/yaml/parser/psych.rb', line 70

def initialize(value)
  value    = value.to_str if value.respond_to? :to_str
  value    = value.to_io  if value.respond_to? :to_io
  @value   = value
  @scanner = ScalarScanner.new
end

Class Method Details

.parse(value) ⇒ Object



66
67
68
# File 'lib/travis/yaml/parser/psych.rb', line 66

def self.parse(value)
  new(value).parse
end

.parses?(value) ⇒ Boolean

Returns:

  • (Boolean)


59
60
61
62
63
64
# File 'lib/travis/yaml/parser/psych.rb', line 59

def self.parses?(value)
  return true if value.is_a?(::Psych::Nodes::Node)
  return true if value.is_a?(String) or value.is_a?(IO)
  return true if defined?(StringIO) and value.is_a?(StringIO)
  value.respond_to?(:to_str) or value.respond_to?(:to_io)
end

Instance Method Details

#accept(node, value) ⇒ Object



90
91
92
93
94
95
96
97
98
99
100
101
# File 'lib/travis/yaml/parser/psych.rb', line 90

def accept(node, value)
  case value
  when ::Psych::Nodes::Scalar   then accept_scalar   node, value
  when ::Psych::Nodes::Mapping  then accept_mapping  node, value
  when ::Psych::Nodes::Sequence then accept_sequence node, value
  when ::Psych::Nodes::Alias    then accept_alias    node, value
  when ::Psych::Nodes::Document then accept          node, value.root
  when ::Psych::Nodes::Stream   then accept_sequence node, value
  else node.visit_unexpected(self, value) if value
  end
  node.verify
end

#accept_mapping(node, value) ⇒ Object



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
# File 'lib/travis/yaml/parser/psych.rb', line 115

def accept_mapping(node, value)
  case value.tag
  when MAP, OMAP, PAIRS then node.visit_mapping  self, value
  when SET              then node.visit_sequence self, SetNode.new(value)
  when SEQ              then node.visit_sequence self, value
  when nil
    if value.children.size == 2 and value.children.first.value == 'secure'
      secret_value = value.children.last
      if secret_value.is_a? ::Psych::Nodes::Scalar
        secret_value.tag ||= '!secure'
        node.visit_scalar(self, :secure, secret_value, false)
      else
        node.visit_unexpected(self, value, "secret value needs to be a string")
      end
    else
      node.visit_mapping(self, value)
    end
  else
    node.visit_unexpected self, value, "unexpected tag %p for mapping" % value.tag
  end
end

#accept_scalar(node, value) ⇒ Object



137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
# File 'lib/travis/yaml/parser/psych.rb', line 137

def accept_scalar(node, value)
  case tag = scalar_tag(value)
  when BINARY    then node.visit_scalar self, :binary, value, value.tag.nil?
  when BOOL      then node.visit_scalar self, :bool,   value, value.tag.nil?
  when FLOAT     then node.visit_scalar self, :float,  value, value.tag.nil?
  when INT       then node.visit_scalar self, :int,    value, value.tag.nil?
  when NULL      then node.visit_scalar self, :null,   value, value.tag.nil?
  when STR       then node.visit_scalar self, :str,    value, value.tag.nil?
  when TIMESTAMP then node.visit_scalar self, :time,   value, value.tag.nil?
  when SECURE    then node.visit_scalar self, :secure, value, value.tag.nil?
  when NULL      then node.visit_scalar self, :null,   value, value.tag.nil?
  when REGEXP    then node.visit_scalar self, :regexp, value, value.tag.nil?
  else node.visit_unexpected self, value, "unexpected tag %p for scalar %p" % [tag, simple(value)]
  end
end

#accept_sequence(node, value) ⇒ Object



103
104
105
106
107
108
109
110
111
112
113
# File 'lib/travis/yaml/parser/psych.rb', line 103

def accept_sequence(node, value)
  case value.tag
  when SET, SEQ
    node.visit_sequence self, value
  when nil
    value = ScalarSequence.new(value) unless value.is_a? ::Psych::Nodes::Sequence
    node.visit_sequence self, value
  else
    node.visit_sequence self, ScalarSequence.new(value)
  end
end

#apply_mapping(node, value) ⇒ Object



204
205
206
207
# File 'lib/travis/yaml/parser/psych.rb', line 204

def apply_mapping(node, value)
  keys, values = value.children.group_by.with_index { |_,i| i.even? }.values_at(true, false)
  keys.zip(values) { |key, value| node.visit_pair(self, key, value) } if keys and values
end

#apply_sequence(node, value) ⇒ Object



209
210
211
# File 'lib/travis/yaml/parser/psych.rb', line 209

def apply_sequence(node, value)
  value.children.each { |child| node.visit_child(self, child) }
end

#cast(type, value) ⇒ Object



189
190
191
192
193
194
195
196
197
198
199
200
201
202
# File 'lib/travis/yaml/parser/psych.rb', line 189

def cast(type, value)
  case type
  when :str    then value.value
  when :binary then value.value.unpack('m').first
  when :bool   then value.value !~ FALSE
  when :float  then Float   @scanner.tokenize(value.value)
  when :int    then Integer @scanner.tokenize(value.value)
  when :time   then @scanner.parse_time(value.value)
  when :secure then SecureString.new(value.value, value.tag != '!decrypted')
  when :regexp then regexp(value.value)
  when :null   then nil
  else raise ArgumentError, 'unknown scalar type %p' % type
  end
end

#generate_key(node, value) ⇒ Object



213
214
215
216
217
218
219
220
# File 'lib/travis/yaml/parser/psych.rb', line 213

def generate_key(node, value)
  if value.respond_to? :value and (value.tag.nil? || value.tag == STR)
    value = value.value.to_s
    value.start_with?(?:) ? value[1..-1] : value
  else
    node.visit_unexpected(self, value, "expected string as key")
  end
end

#parse(root = nil) ⇒ Object



77
78
79
80
81
82
83
84
85
86
87
88
# File 'lib/travis/yaml/parser/psych.rb', line 77

def parse(root = nil)
  root   ||= Travis::Yaml::Nodes::Root.new
  parsed   = @value if @value.is_a? ::Psych::Nodes::Node
  parsed ||= ::Psych.parse(@value)
  accept(root, parsed)
  root
rescue ::Psych::SyntaxError => error
  root.verify
  root.warnings.clear
  root.error("syntax error: %s", error.message)
  root
end

#regexp(pattern) ⇒ Object



180
181
182
183
184
185
186
187
# File 'lib/travis/yaml/parser/psych.rb', line 180

def regexp(pattern)
  return pattern if pattern.is_a? Regexp
  return Regexp.new(pattern) unless pattern =~ FORMATS['!regexp']
  flag = $2.chars.inject(0) { |f,c| f | REG_FLAGS.fetch(c, 0) }
  Regexp.new($1, flag)
rescue RegexpError => error
  raise ArgumentError, "broken regular expression - #{error.message}"
end

#scalar_tag(value) ⇒ Object



171
172
173
174
175
176
177
178
# File 'lib/travis/yaml/parser/psych.rb', line 171

def scalar_tag(value)
  return value.tag if value.tag
  return '!str' if value.quoted
  FORMATS.each do |tag, format|
    return tag if value.value =~ format
  end
  '!str'
end

#simple(value) ⇒ Object



153
154
155
156
157
158
159
160
161
162
# File 'lib/travis/yaml/parser/psych.rb', line 153

def simple(value)
  case value
  when ::Psych::Nodes::Scalar   then value.value
  when ::Psych::Nodes::Mapping  then simple_mapping(value)
  when ::Psych::Nodes::Sequence then value.children.map { |c| simple(c) }
  when ::Psych::Nodes::Document then simple(value.root)
  when ::Psych::Nodes::Stream   then value.children.map { |c| simple(c) }
  else value
  end
end

#simple_mapping(value) ⇒ Object



164
165
166
167
168
169
# File 'lib/travis/yaml/parser/psych.rb', line 164

def simple_mapping(value)
  children     = {}
  keys, values = value.children.group_by.with_index { |_,i| i.even? }.values_at(true, false)
  keys.zip(values) { |key, value| children[simple(key)] = simple(value) } if keys and values
  children
end