class TwitterCldr::Utils::YAML

Constants

ESCAPE_SEQ

non-break characters

ESCAPE_SEQ_LB

line breaks

ESCAPE_SEQ_NS

non-breaking space

ESCAPE_SEQ_WS

white spaces

REX_ANY_LB
REX_BOOL

regexps for language-Independent types for YAML1.1

REX_CR
REX_CRLF
REX_FLOAT
REX_INT
REX_LF

regexps for line breaks

REX_LS
REX_MERGE
REX_NEL
REX_NORMAL_LB
REX_NULL
REX_PS
REX_SYMBOL
REX_TIMESTAMP
REX_VALUE
UCS_0X2028
UCS_0X2029
UCS_0X85
UCS_0XA0

Public Class Methods

dump(obj, opts = {}) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 130
def dump(obj, opts = {})
  @options = opts.dup
  @options[:indent_size] = 2          if @options[:indent_size].to_i <= 0
  @options[:minimum_block_length] = 0 if @options[:minimum_block_length].to_i <= 0
  @options.update(
    {
      printable_with_syck:  true,
      escape_b_specific:    true,
      escape_as_utf8:       true,
    }
  ) if @options[:syck_compatible]

  "--- #{emit(obj, 1)}\n"
rescue SystemStackError
  raise ArgumentError, "TwitterCLDR yaml dumper can't handle circular references"
end

Private Class Methods

emit(obj, level) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 149
def emit(obj, level)
  case obj
    when Array
      if (obj.length == 0)
        '[]'
      else
        indent = "\n#{s_indent(level - 1)}"
        obj.collect do |o|
          "#{indent}- #{emit(o, level + 1)}"
        end.join('')
      end
    when Hash
      if (obj.length == 0)
        '{}'
      else
        indent = "\n#{s_indent(level - 1)}"
        hash_order = @options[:hash_order]
        if (hash_order && level == 1)
          hash_keys = obj.keys.sort do |x, y|
            x_order = hash_order.index(x) ? hash_order.index(x) : Float::MAX
            y_order = hash_order.index(y) ? hash_order.index(y) : Float::MAX
            o = (x_order <=> y_order)
            (o != 0) ? o : (x.to_s <=> y.to_s)
          end
        elsif @options[:preserve_order]
          hash_keys = obj.keys
        else
          hash_keys = obj.keys.sort { |x, y| x.to_s <=> y.to_s }
        end
        hash_keys.collect do |k|
          key = emit(k, level + 1)
          if (
            is_one_plain_line?(key) ||
            key =~ /\A(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_NULL}|#{REX_SYMBOL})\z/x
          )
            "#{indent}#{key}: #{emit(obj[k], level + 1)}"
          else
            "#{indent}? #{key}#{indent}: #{emit(obj[k], level + 1)}"
          end
        end.join('')
      end
    when NilClass
      '~'
    when String
      emit_string(obj, level)
    when TrueClass, FalseClass
      obj.to_s
    when Integer, Float
      obj.to_s
    when Date
      obj.to_s
    when Time
      offset = obj.gmtoff
      off_hm = sprintf(
        '%+.2d:%.2d',
        (offset / 3600.0).to_i,
        (offset % 3600.0) / 60
      )
      u_sec = (obj.usec != 0) ? sprintf(".%.6d", obj.usec) : ''
      obj.strftime("%Y-%m-%d %H:%M:%S#{u_sec} #{off_hm}")
    when Symbol
      prefix = @options[:use_natural_symbols] && is_one_plain_line?(obj.to_s) ? ":" : "!ruby/symbol "
      "#{prefix}#{emit_string(obj, level)}"
    when Range
      '!ruby/range ' + obj.to_s
    when Regexp
      '!ruby/regexp ' + obj.inspect
    else
      case
        when obj.is_a?(Struct)
          struct_members = {}
          obj.each_pair { |k, v| struct_members[k.to_s] = v }
          "!ruby/struct:#{obj.class.to_s.sub(/^(Struct::(.+)|.*)$/, '\2')} #{emit(struct_members, level + 1)}"
        else
          # serialized as a generic object
          object_members = {}
          obj.instance_variables.each do |k, v|
            object_members[k.to_s.sub(/^@/, '')] = obj.instance_variable_get(k)
          end
          "!ruby/object:#{obj.class.to_s} #{emit(object_members, level + 1)}"
      end
  end
end
emit_base64_binary(str, level) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 297
def emit_base64_binary(str, level)
  indent = "\n#{s_indent(level)}"
  base64 = [str].pack('m')
  "!binary |#{indent}#{base64.gsub(/\n(?!\z)/, indent)}"
end
emit_block_string(str, level) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 261
def emit_block_string(str, level)
  str = normalize_line_break(str)

  indent = s_indent(level)
  indentation_indicator = (str =~ /\A /) ? indent.size.to_s : ''
  str =~ /(#{REX_NORMAL_LB}*)\z/
  chomping_indicator = case $1.length
    when 0
      '-'
    when 1
      ''
    else
      '+'
  end

  str.chomp!
  str.gsub!(/#{REX_NORMAL_LB}/) { $1 + indent }
  "|#{indentation_indicator}#{chomping_indicator}\n#{indent}#{str}"
end
emit_quoted_string(str, level) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 281
def emit_quoted_string(str, level)
  str = yaml_escape(normalize_line_break(str))
  if (str.length < @options[:minimum_block_length])
    str.gsub!(/#{REX_NORMAL_LB}/) { ESCAPE_SEQ_LB[$1] }
  else
    str.gsub!(/#{REX_NORMAL_LB}$/) { ESCAPE_SEQ_LB[$1] }
    str.gsub!(/(#{REX_NORMAL_LB}+)(.)/) do
      trail_c = $3
      $1 + trail_c.sub(/([\t ])/) { ESCAPE_SEQ_WS[$1] }
    end
    indent = s_indent(level)
    str.gsub!(/#{REX_NORMAL_LB}/) { "#{ESCAPE_SEQ_LB[$1]}\\\n#{indent}" }
  end
  %Q("#{str}")
end
emit_simple_string(str, level) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 257
def emit_simple_string(str, level)
  str
end
emit_string(str, level) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 233
def emit_string(str, level)
  if @options[:quote_all_strings] && !str.is_a?(Symbol)
    emit_quoted_string(str, level)
  else
    str = str.to_s
    (is_string, is_printable, is_one_line, is_one_plain_line) = string_type(str)
    if is_string
      if is_printable
        if is_one_plain_line
          emit_simple_string(str, level)
        else
          (is_one_line || str.length < @options[:minimum_block_length]) ?
            emit_quoted_string(str, level) :
            emit_block_string(str, level)
        end
      else
        emit_quoted_string(str, level)
      end
    else
      emit_base64_binary(str, level)
    end
  end
end
is_one_line?(str) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 340
def is_one_line?(str)
  str !~ /#{REX_ANY_LB}(?!\z)/
end
is_one_plain_line?(str) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 344
def is_one_plain_line?(str)
  # YAML 1.1 / 4.6.11.
  str !~ /^([\-\?:,\[\]\{\}\#&\*!\|>'"%@`\s]|---|\.\.\.)/    &&
  str !~ /[:\#\s\[\]\{\},]/                                  &&
  str !~ /#{REX_ANY_LB}/                                     &&
  str !~ /^(#{REX_BOOL}|#{REX_FLOAT}|#{REX_INT}|#{REX_MERGE}
    |#{REX_NULL}|#{REX_TIMESTAMP}|#{REX_VALUE})$/x
end
is_printable?(ucs_code) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 324
def is_printable?(ucs_code)
  # YAML 1.1 / 4.1.1.
  (
    [0x09, 0x0a, 0x0d, 0x85].include?(ucs_code)   ||
    (ucs_code <=     0x7e && ucs_code >=    0x20) ||
    (ucs_code <=   0xd7ff && ucs_code >=    0xa0) ||
    (ucs_code <=   0xfffd && ucs_code >=  0xe000) ||
    (ucs_code <= 0x10ffff && ucs_code >= 0x10000)
  ) &&
  !(
    # treat LS/PS as non-printable characters
    @options[:escape_b_specific] &&
    (ucs_code == 0x2028 || ucs_code == 0x2029)
  )
end
normalize_line_break(str) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 358
def normalize_line_break(str)
  # YAML 1.1 / 4.1.4.
  str.gsub(/(#{REX_CRLF}|#{REX_CR}|#{REX_NEL})/, "\n")
end
s_indent(level) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 353
def s_indent(level)
  # YAML 1.1 / 4.2.2.
  ' ' * (level * @options[:indent_size])
end
string_type(str) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 303
def string_type(str)
  if str.respond_to?(:encoding) && (!str.valid_encoding? || str.encoding == Encoding::ASCII_8BIT)
    return false, false, false, false
  end
  (ucs_codes = str.unpack('U*')) rescue (
    # ArgumentError -> binary data
    return false, false, false, false
  )
  if (
    @options[:printable_with_syck] &&
    str =~ /\A#{REX_ANY_LB}* | #{REX_ANY_LB}*\z|#{REX_ANY_LB}{2}\z/
  )
    # detour Syck bug
    return true, false, nil, false
  end
  ucs_codes.each {|ucs_code|
    return true, false, nil, false unless is_printable?(ucs_code)
  }
  return true, true, is_one_line?(str), is_one_plain_line?(str)
end
yaml_escape(str) click to toggle source
# File lib/twitter_cldr/utils/yaml.rb, line 363
def yaml_escape(str)
  # YAML 1.1 / 4.1.6.
  str.gsub(/[^a-zA-Z0-9]/u) do |c|
    ucs_code, = (c.unpack('U') rescue [??])
    case
      when ESCAPE_SEQ[c]
        ESCAPE_SEQ[c]
      when is_printable?(ucs_code)
        c
      when @options[:escape_as_utf8]
        c.respond_to?(:bytes) ?
          c.bytes.collect { |b| '\\x%.2x' % b }.join :
          '\\x' + c.unpack('H2' * c.size).join('\\x')
      when ucs_code == 0x2028 || ucs_code == 0x2029
        ESCAPE_SEQ_LB[c]
      when ucs_code <= 0x7f
        sprintf('\\x%.2x', ucs_code)
      when ucs_code <= 0xffff
        sprintf('\\u%.4x', ucs_code)
      else
        sprintf('\\U%.8x', ucs_code)
    end
  end
end