Class: SecApi::ExtractedData

Inherits:
Dry::Struct
  • Object
show all
Includes:
DeepFreezable
Defined in:
lib/sec_api/objects/extracted_data.rb

Overview

Represents extracted data from SEC filings

This immutable value object wraps extraction results from the sec-api.io Extractor endpoint. All attributes are optional to handle varying API response structures across different form types.

Examples:

Creating from API response

api_response = {
  "text" => "Full extracted text...",
  "sections" => { "risk_factors" => "Risk content..." },
  "metadata" => { "source_url" => "https://..." }
}
data = SecApi::ExtractedData.from_api(api_response)
data.text       # => "Full extracted text..."
data.sections   # => { risk_factors: "Risk content..." }

Thread-safe concurrent access

threads = 10.times.map do
  Thread.new { data.text; data.sections }
end
threads.each(&:join) # No race conditions

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(attributes) ⇒ ExtractedData

Explicit freeze for immutability and thread safety Deep freeze all nested hashes and strings to ensure thread safety

Parameters:

  • attributes (Hash)

    The attributes hash



50
51
52
53
54
55
56
# File 'lib/sec_api/objects/extracted_data.rb', line 50

def initialize(attributes)
  super
  text&.freeze
  deep_freeze(sections) if sections
  deep_freeze() if 
  freeze
end

Dynamic Method Handling

This class handles dynamic methods through the method_missing method

#method_missing(name, *args) ⇒ String?

Access a specific section by name using dynamic method dispatch

Allows convenient access to sections via method calls instead of hash access. Returns nil for missing sections (no NoMethodError raised for section names). Methods with special suffixes (!, ?, =) still raise NoMethodError.

Examples:

Access risk factors section

extracted.risk_factors  # => "Risk factor text..."

Access missing section

extracted.nonexistent   # => nil (no error)

Parameters:

  • name (Symbol)

    The section name to access

  • args (Array)

    Additional arguments (ignored)

Returns:

  • (String, nil)

    The section content or nil if not present



85
86
87
88
89
90
91
92
93
94
95
# File 'lib/sec_api/objects/extracted_data.rb', line 85

def method_missing(name, *args)
  # Only handle zero-argument calls (getter-style)
  return super if args.any?

  # Don't intercept bang, predicate, or setter methods
  name_str = name.to_s
  return super if name_str.end_with?("!", "?", "=")

  # Return section content if sections exist, nil otherwise
  sections&.[](name)
end

Class Method Details

.from_api(data) ⇒ ExtractedData

Normalize API response (handle string vs symbol keys)

Parameters:

  • data (Hash)

    The raw API response

Returns:



62
63
64
65
66
67
68
# File 'lib/sec_api/objects/extracted_data.rb', line 62

def self.from_api(data)
  new(
    text: data["text"] || data[:text],
    sections: normalize_sections(data["sections"] || data[:sections]),
    metadata: data["metadata"] || data[:metadata] || {}
  )
end

Instance Method Details

#metadataHash?

Metadata about extraction Flexible hash to handle varying API response structures

Returns:

  • (Hash, nil)


45
# File 'lib/sec_api/objects/extracted_data.rb', line 45

attribute? :metadata, Types::Hash.optional

#respond_to_missing?(name, include_private = false) ⇒ Boolean

Support respond_to? for sections that exist in the hash

Only responds true for section names that are actually present in the sections hash. This allows proper Ruby introspection.

Parameters:

  • name (Symbol)

    The method name to check

  • include_private (Boolean) (defaults to: false)

    Whether to include private methods

Returns:

  • (Boolean)

    true if section exists in hash



105
106
107
# File 'lib/sec_api/objects/extracted_data.rb', line 105

def respond_to_missing?(name, include_private = false)
  sections&.key?(name) || super
end

#sectionsHash{Symbol => String}?

Structured sections (risk_factors, financials, etc.) API returns hash like { “risk_factors”: “…”, “financials”: “…” }

Returns:

  • (Hash{Symbol => String}, nil)


40
# File 'lib/sec_api/objects/extracted_data.rb', line 40

attribute? :sections, Types::Hash.map(Types::Symbol, Types::String).optional

#textString?

Full extracted text (if extracting entire filing)

Returns:

  • (String, nil)


35
# File 'lib/sec_api/objects/extracted_data.rb', line 35

attribute? :text, Types::String.optional