File: constants.rb

package info (click to toggle)
ruby-multi-xml 0.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 472 kB
  • sloc: ruby: 2,822; sh: 4; makefile: 2
file content (134 lines) | stat: -rw-r--r-- 4,119 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
module MultiXml
  # Hash key for storing text content within element hashes
  #
  # @api public
  # @return [String] the key "__content__" used for text content
  # @example Accessing text content
  #   result = MultiXml.parse('<name>John</name>')
  #   result["name"] #=> "John" (simplified, but internally uses __content__)
  TEXT_CONTENT_KEY = "__content__".freeze

  # Maps Ruby class names to XML type attribute values
  #
  # @api public
  # @return [Hash{String => String}] mapping of Ruby class names to XML types
  # @example Check XML type for a Ruby class
  #   RUBY_TYPE_TO_XML["Integer"] #=> "integer"
  RUBY_TYPE_TO_XML = {
    "Symbol" => "symbol",
    "Integer" => "integer",
    "BigDecimal" => "decimal",
    "Float" => "float",
    "TrueClass" => "boolean",
    "FalseClass" => "boolean",
    "Date" => "date",
    "DateTime" => "datetime",
    "Time" => "datetime",
    "Array" => "array",
    "Hash" => "hash"
  }.freeze

  # XML type attributes disallowed by default for security
  #
  # These types are blocked to prevent code execution vulnerabilities.
  #
  # @api public
  # @return [Array<String>] list of disallowed type names
  # @example Check default disallowed types
  #   DISALLOWED_TYPES #=> ["symbol", "yaml"]
  DISALLOWED_TYPES = %w[symbol yaml].freeze

  # Values that represent false in XML boolean attributes
  #
  # @api public
  # @return [Set<String>] values considered false
  # @example Check false values
  #   FALSE_BOOLEAN_VALUES.include?("0") #=> true
  FALSE_BOOLEAN_VALUES = Set.new(%w[0 false]).freeze

  # Default parsing options
  #
  # @api public
  # @return [Hash] default options for parse method
  # @example View defaults
  #   DEFAULT_OPTIONS[:symbolize_keys] #=> false
  DEFAULT_OPTIONS = {
    typecast_xml_value: true,
    disallowed_types: DISALLOWED_TYPES,
    symbolize_keys: false
  }.freeze

  # Parser libraries in preference order (fastest first)
  #
  # @api public
  # @return [Array<Array>] pairs of [require_path, parser_symbol]
  # @example View parser order
  #   PARSER_PREFERENCE.first #=> ["ox", :ox]
  PARSER_PREFERENCE = [
    ["ox", :ox],
    ["libxml", :libxml],
    ["nokogiri", :nokogiri],
    ["rexml/document", :rexml],
    ["oga", :oga]
  ].freeze

  # Parses datetime strings, trying Time first then DateTime
  #
  # @api private
  # @return [Proc] lambda that parses datetime strings
  PARSE_DATETIME = lambda do |string|
    Time.parse(string).utc
  rescue ArgumentError
    DateTime.parse(string).to_time.utc
  end

  # Creates a file-like StringIO from base64-encoded content
  #
  # @api private
  # @return [Proc] lambda that creates file objects
  FILE_CONVERTER = lambda do |content, entity|
    StringIO.new(content.unpack1("m")).tap do |io|
      io.extend(FileLike)
      file_io = io # : FileIO
      file_io.original_filename = entity["name"]
      file_io.content_type = entity["content_type"]
    end
  end

  # Type converters for XML type attributes
  #
  # Maps type attribute values to lambdas that convert string content.
  # Converters with arity 2 receive the content and the full entity hash.
  #
  # @api public
  # @return [Hash{String => Proc}] mapping of type names to converter procs
  # @example Using a converter
  #   TYPE_CONVERTERS["integer"].call("42") #=> 42
  TYPE_CONVERTERS = {
    # Primitive types
    "symbol" => :to_sym.to_proc,
    "string" => :to_s.to_proc,
    "integer" => :to_i.to_proc,
    "float" => :to_f.to_proc,
    "double" => :to_f.to_proc,
    "decimal" => ->(s) { BigDecimal(s) },
    "boolean" => ->(s) { !FALSE_BOOLEAN_VALUES.include?(s.strip) },

    # Date and time types
    "date" => Date.method(:parse),
    "datetime" => PARSE_DATETIME,
    "dateTime" => PARSE_DATETIME,

    # Binary types
    "base64Binary" => ->(s) { s.unpack1("m") },
    "binary" => ->(s, entity) { (entity["encoding"] == "base64") ? s.unpack1("m") : s },
    "file" => FILE_CONVERTER,

    # Structured types
    "yaml" => lambda do |string|
      YAML.safe_load(string, permitted_classes: [Symbol, Date, Time])
    rescue ArgumentError, Psych::SyntaxError
      string
    end
  }.freeze
end