File: multi_xml.rbs

package info (click to toggle)
ruby-multi-xml 0.8.1-1
  • links: PTS, VCS
  • area: main
  • in suites: sid
  • size: 472 kB
  • sloc: ruby: 2,822; sh: 4; makefile: 2
file content (227 lines) | stat: -rw-r--r-- 8,031 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
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
# Type signatures for MultiXml

# Recursive type alias for parsed XML values
# XML parsing produces nested structures of hashes, arrays, and primitive values
type MultiXml::xmlValue = String
                        | Integer
                        | Float
                        | bool
                        | Symbol
                        | Time
                        | Date
                        | BigDecimal
                        | StringIO
                        | nil
                        | Array[MultiXml::xmlValue]
                        | Hash[String, MultiXml::xmlValue]
                        | Hash[Symbol, MultiXml::xmlValue]

# Type for hash with string keys used internally during parsing
type MultiXml::xmlHash = Hash[String, MultiXml::xmlValue]

# Interface for parser modules
interface MultiXml::_Parser
  def parse: (StringIO io) -> MultiXml::xmlHash?
  def parse_error: () -> singleton(Exception)
end

module MultiXml
  VERSION: Gem::Version

  TEXT_CONTENT_KEY: String

  RUBY_TYPE_TO_XML: Hash[String, String]

  DISALLOWED_TYPES: Array[String]

  FALSE_BOOLEAN_VALUES: Set[String]

  DEFAULT_OPTIONS: Hash[Symbol, bool | Array[String]]

  # Array of [library_name, parser_symbol] pairs
  PARSER_PREFERENCE: Array[Array[String | Symbol]]

  PARSE_DATETIME: ^(String) -> Time

  # Lambda for creating file-like StringIO from base64 content
  # Uses untyped for content because unpack1 returns various types
  # Uses untyped for entity because hash values are xmlValue but we access specific String keys
  FILE_CONVERTER: ^(untyped, untyped) -> StringIO

  # Type converters keyed by XML type attribute string
  # Uses untyped key because hash["type"] returns xmlValue, and Hash#[] with non-String returns nil
  TYPE_CONVERTERS: Hash[untyped, Proc | Method]

  LOADED_PARSER_CHECKS: Hash[Symbol, Symbol]

  self.@parser: Module

  extend Helpers

  # Public API: Get the current XML parser module
  def self.parser: () -> Module

  # Public API: Set the XML parser to use
  def self.parser=: (Symbol | String | Module new_parser) -> Module

  # Public API: Parse XML into a Ruby Hash
  # Uses untyped for options because values vary by key (:parser, :symbolize_keys, :disallowed_types, :typecast_xml_value)
  def self.parse: (String | StringIO xml, ?Hash[Symbol, untyped] options) -> xmlHash

  private

  # Resolve a parser specification (Symbol, String, or Module) to a parser
  def self.resolve_parser: (Symbol | String | Module spec) -> Module

  # Load a parser module by name
  def self.load_parser: (Symbol | String name) -> Module

  # Convert snake_case to CamelCase
  def self.camelize: (String name) -> String

  # Detect the best available parser
  def self.detect_parser: () -> (Symbol | String)

  # Find an already-loaded parser library
  def self.find_loaded_parser: () -> Symbol?

  # Try to find an available parser by requiring libraries
  def self.find_available_parser: () -> (String | Symbol | nil)

  # Attempt to require a library, returning success/failure
  # Kernel#require accepts String; library may be Symbol from PARSER_PREFERENCE (coerced at runtime)
  def self.try_require: (untyped library) -> bool

  # Raise NoParserError - never returns
  def self.raise_no_parser_error: () -> bot

  # Convert String to StringIO, pass through IO-like objects
  # Uses respond_to?(:read) duck typing - returns input unchanged if IO-like
  def self.normalize_input: (String | StringIO xml) -> untyped

  # Parse with error handling and key normalization
  # xml_parser implements _Parser interface; original_input uses respond_to? duck typing
  def self.parse_with_error_handling: (StringIO io, untyped original_input, untyped xml_parser) -> xmlHash

  module Helpers
    # Recursively convert all hash keys to symbols
    # Uses case/when type dispatch - Steep can't track flow narrowing
    def self?.symbolize_keys: (untyped data) -> untyped

    # Recursively convert dashes in hash keys to underscores
    # Uses case/when type dispatch - Steep can't track flow narrowing
    def self?.undasherize_keys: (untyped data) -> untyped

    # Recursively typecast XML values based on type attributes
    # Uses case/when type dispatch - Steep can't track flow narrowing
    def self?.typecast_xml_value: (untyped value, ?Array[String] disallowed_types) -> xmlValue

    # Typecast array elements and unwrap single-element arrays
    def self?.typecast_array: (Array[xmlValue] array, Array[String] disallowed_types) -> xmlValue

    # Typecast a hash based on its type attribute
    def self?.typecast_hash: (xmlHash hash, Array[String] disallowed_types) -> xmlValue

    # Check if a type is in the disallowed list
    # Uses is_a?(Hash) guard then include? - Steep can't narrow xmlValue to String
    def self?.disallowed_type?: (untyped type, Array[String] disallowed_types) -> boolish

    # Convert a hash based on its type and content
    def self?.convert_hash: (xmlHash hash, xmlValue type, Array[String] disallowed_types) -> xmlValue

    # Typecast all child values in a hash
    def self?.typecast_children: (xmlHash hash, Array[String] disallowed_types) -> (xmlHash | StringIO)

    # Extract array entries from element with type="array"
    def self?.extract_array_entries: (xmlHash hash, Array[String] disallowed_types) -> Array[xmlValue]

    # Find array or hash entries in a hash, excluding the type key
    # Returns xmlValue subset (Array or Hash) - uses is_a? that Steep can't narrow
    def self?.find_array_entries: (xmlHash hash) -> untyped

    # Wrap hash in array if needed and typecast all entries
    def self?.wrap_and_typecast: (Array[xmlValue] | xmlHash entries, Array[String] disallowed_types) -> Array[xmlValue]

    # Convert text content using type converters
    # hash["type"] is xmlValue, used as Hash key - Steep requires String
    def self?.convert_text_content: (xmlHash hash) -> xmlValue

    # Unwrap value if hash has no other significant keys
    def self?.unwrap_if_simple: (xmlHash hash, xmlValue value) -> (xmlValue | xmlHash)

    # Check if a hash represents an empty value
    def self?.empty_value?: (xmlHash hash, xmlValue type) -> bool

    private

    # Recursively transform hash keys using a block
    # Block receives key (String) and returns transformed key
    # Uses untyped because &:to_sym Proc type narrowing not supported by Steep
    def self?.transform_keys: (untyped data) { (untyped) -> untyped } -> untyped

    # Unwrap a file object from the result hash if present
    def self?.unwrap_file_if_present: (xmlHash result) -> (xmlHash | StringIO)

    # Apply a type converter to content
    # Content is xmlValue (from hash.fetch) but typically String in practice
    def self?.apply_converter: (xmlHash hash, untyped content, Proc | Method converter) -> xmlValue
  end

  module FileLike
    DEFAULT_FILENAME: String

    DEFAULT_CONTENT_TYPE: String

    @original_filename: String?

    @content_type: String?

    attr_writer original_filename: String?

    attr_writer content_type: String?

    def original_filename: () -> String

    def content_type: () -> String
  end

  # Represents a StringIO that has been extended with FileLike
  # Used for file type conversions in XML parsing
  class FileIO < StringIO
    include FileLike
  end

  class ParseError < StandardError
    @xml: String?

    @cause: Exception?

    attr_reader xml: String?

    attr_reader cause: Exception?

    # Message can be String (normal) or Exception (from parser errors), or nil for default
    def initialize: (?(String | Exception | nil) message, ?xml: String?, ?cause: Exception?) -> void
  end

  class NoParserError < StandardError
  end

  class DisallowedTypeError < StandardError
    @type: String

    attr_reader type: String

    def initialize: (String type) -> void
  end

  # Parsers module - parser implementations depend on optional external gems
  module Parsers
  end
end

# Stub for Psych::SyntaxError which is part of the yaml library
module Psych
  class SyntaxError < ::StandardError
  end
end