sec_api 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (75) hide show
  1. checksums.yaml +7 -0
  2. data/.devcontainer/Dockerfile +54 -0
  3. data/.devcontainer/README.md +178 -0
  4. data/.devcontainer/devcontainer.json +46 -0
  5. data/.devcontainer/docker-compose.yml +28 -0
  6. data/.devcontainer/post-create.sh +51 -0
  7. data/.devcontainer/post-start.sh +44 -0
  8. data/.rspec +3 -0
  9. data/.standard.yml +3 -0
  10. data/CHANGELOG.md +5 -0
  11. data/CLAUDE.md +0 -0
  12. data/LICENSE.txt +21 -0
  13. data/MIGRATION.md +274 -0
  14. data/README.md +370 -0
  15. data/Rakefile +10 -0
  16. data/config/secapi.yml.example +57 -0
  17. data/docs/development-guide.md +291 -0
  18. data/docs/enumerator_pattern_design.md +483 -0
  19. data/docs/examples/README.md +58 -0
  20. data/docs/examples/backfill_filings.rb +419 -0
  21. data/docs/examples/instrumentation.rb +583 -0
  22. data/docs/examples/query_builder.rb +308 -0
  23. data/docs/examples/streaming_notifications.rb +491 -0
  24. data/docs/index.md +244 -0
  25. data/docs/migration-guide-v1.md +1091 -0
  26. data/docs/pre-review-checklist.md +145 -0
  27. data/docs/project-overview.md +90 -0
  28. data/docs/project-scan-report.json +60 -0
  29. data/docs/source-tree-analysis.md +190 -0
  30. data/lib/sec_api/callback_helper.rb +49 -0
  31. data/lib/sec_api/client.rb +606 -0
  32. data/lib/sec_api/collections/filings.rb +267 -0
  33. data/lib/sec_api/collections/fulltext_results.rb +86 -0
  34. data/lib/sec_api/config.rb +590 -0
  35. data/lib/sec_api/deep_freezable.rb +42 -0
  36. data/lib/sec_api/errors/authentication_error.rb +24 -0
  37. data/lib/sec_api/errors/configuration_error.rb +5 -0
  38. data/lib/sec_api/errors/error.rb +75 -0
  39. data/lib/sec_api/errors/network_error.rb +26 -0
  40. data/lib/sec_api/errors/not_found_error.rb +23 -0
  41. data/lib/sec_api/errors/pagination_error.rb +28 -0
  42. data/lib/sec_api/errors/permanent_error.rb +29 -0
  43. data/lib/sec_api/errors/rate_limit_error.rb +57 -0
  44. data/lib/sec_api/errors/reconnection_error.rb +34 -0
  45. data/lib/sec_api/errors/server_error.rb +25 -0
  46. data/lib/sec_api/errors/transient_error.rb +28 -0
  47. data/lib/sec_api/errors/validation_error.rb +23 -0
  48. data/lib/sec_api/extractor.rb +122 -0
  49. data/lib/sec_api/filing_journey.rb +477 -0
  50. data/lib/sec_api/mapping.rb +125 -0
  51. data/lib/sec_api/metrics_collector.rb +411 -0
  52. data/lib/sec_api/middleware/error_handler.rb +250 -0
  53. data/lib/sec_api/middleware/instrumentation.rb +186 -0
  54. data/lib/sec_api/middleware/rate_limiter.rb +541 -0
  55. data/lib/sec_api/objects/data_file.rb +34 -0
  56. data/lib/sec_api/objects/document_format_file.rb +45 -0
  57. data/lib/sec_api/objects/entity.rb +92 -0
  58. data/lib/sec_api/objects/extracted_data.rb +118 -0
  59. data/lib/sec_api/objects/fact.rb +147 -0
  60. data/lib/sec_api/objects/filing.rb +197 -0
  61. data/lib/sec_api/objects/fulltext_result.rb +66 -0
  62. data/lib/sec_api/objects/period.rb +96 -0
  63. data/lib/sec_api/objects/stream_filing.rb +194 -0
  64. data/lib/sec_api/objects/xbrl_data.rb +356 -0
  65. data/lib/sec_api/query.rb +423 -0
  66. data/lib/sec_api/rate_limit_state.rb +130 -0
  67. data/lib/sec_api/rate_limit_tracker.rb +154 -0
  68. data/lib/sec_api/stream.rb +841 -0
  69. data/lib/sec_api/structured_logger.rb +199 -0
  70. data/lib/sec_api/types.rb +32 -0
  71. data/lib/sec_api/version.rb +42 -0
  72. data/lib/sec_api/xbrl.rb +220 -0
  73. data/lib/sec_api.rb +137 -0
  74. data/sig/sec_api.rbs +4 -0
  75. metadata +217 -0
@@ -0,0 +1,145 @@
1
+ # Pre-Review Checklist
2
+
3
+ Use this checklist before submitting code for review. These items were identified through Epic 3 and Epic 4 retrospectives as common issues caught in code review.
4
+
5
+ ## Thread Safety
6
+
7
+ - [ ] **Immutable objects are frozen** - All Dry::Struct value objects should be deeply frozen
8
+ - [ ] **String attributes frozen** - Use `attribute&.freeze` for string attributes in value objects
9
+ - [ ] **Shared state protected** - Any state shared across requests uses Mutex or is immutable
10
+ - [ ] **No instance variable mutation** - Value objects don't mutate after construction
11
+
12
+ ```ruby
13
+ # Good: Deep freeze in from_api
14
+ def self.from_api(data)
15
+ new(
16
+ statements_of_income: parse_statement(data["StatementsOfIncome"]).freeze,
17
+ # ...
18
+ ).freeze
19
+ end
20
+
21
+ # Good: String attribute frozen
22
+ attribute? :text, Types::String.optional
23
+ # In from_api:
24
+ text: data["text"]&.freeze
25
+ ```
26
+
27
+ ## Test Coverage
28
+
29
+ - [ ] **Unit tests for new value objects** - Every new Dry::Struct class needs dedicated tests
30
+ - [ ] **Edge case tests included**:
31
+ - [ ] `nil` input handling
32
+ - [ ] Empty string/hash/array input
33
+ - [ ] Whitespace-only strings
34
+ - [ ] Invalid type input
35
+ - [ ] **Error message verification** - Exception tests verify message content, not just exception type
36
+ - [ ] **`stubs.verify_stubbed_calls`** - All HTTP stub tests verify stubs were called
37
+
38
+ ```ruby
39
+ # Good: Verify error message content
40
+ expect { method_call }.to raise_error(SecApi::ValidationError) do |error|
41
+ expect(error.message).to include("missing required field")
42
+ expect(error.message).to include("period")
43
+ end
44
+
45
+ # Good: Verify stubs in after block or inline
46
+ after { stubs.verify_stubbed_calls }
47
+ ```
48
+
49
+ ## Input Validation
50
+
51
+ - [ ] **Validate required parameters** - Raise ArgumentError or ValidationError for missing required input
52
+ - [ ] **URL format validation** - SEC URLs follow predictable patterns
53
+ - [ ] **CIK format handling** - Normalize to 10-digit with leading zeros
54
+ - [ ] **Accession number format** - Support both dashed and undashed formats
55
+
56
+ ```ruby
57
+ # Good: Input validation with helpful error
58
+ def ticker(symbol)
59
+ raise ArgumentError, "ticker symbol required" if symbol.nil? || symbol.empty?
60
+ # ...
61
+ end
62
+ ```
63
+
64
+ ## Documentation
65
+
66
+ - [ ] **YARD docs on public methods** - `@param`, `@return`, `@raise`, `@example` tags
67
+ - [ ] **Examples match implementation** - YARD examples actually work with current code
68
+ - [ ] **@note for non-obvious behavior** - Document gotchas, limitations, or surprising behavior
69
+
70
+ ```ruby
71
+ # Good: Complete YARD documentation
72
+ # Extracts XBRL data from a filing
73
+ #
74
+ # @param filing_url [String] URL to the SEC filing
75
+ # @return [XbrlData] Typed XBRL data object
76
+ # @raise [NotFoundError] when filing URL is invalid
77
+ # @raise [ValidationError] when response structure is invalid
78
+ #
79
+ # @example Extract from URL
80
+ # data = client.xbrl.to_json("https://sec.gov/...")
81
+ # data.statements_of_income["Revenue"]
82
+ #
83
+ # @note Element names follow US-GAAP taxonomy exactly
84
+ #
85
+ def to_json(filing_url)
86
+ ```
87
+
88
+ ## Code Quality
89
+
90
+ - [ ] **DRY - No duplicate code** - Extract shared logic to modules or helpers
91
+ - [ ] **Consistent patterns** - Follow established `from_api` factory method pattern
92
+ - [ ] **No dead code** - Remove commented-out code, unused variables, unreachable branches
93
+ - [ ] **StandardRB passes** - Run `bundle exec standardrb` before review
94
+
95
+ ## Error Handling
96
+
97
+ - [ ] **Correct exception types**:
98
+ - `ValidationError` - Malformed data, failed validation
99
+ - `NotFoundError` - Resource not found (404), invalid URL
100
+ - `AuthenticationError` - Invalid API key (401, 403)
101
+ - `NetworkError` - Connection failures, timeouts
102
+ - `ServerError` - API server errors (500-504)
103
+ - [ ] **Actionable error messages** - Include what failed, why, and received data context
104
+ - [ ] **No silent failures** - All errors raised or logged, never swallowed
105
+
106
+ ```ruby
107
+ # Good: Actionable error message
108
+ raise ValidationError, "XBRL fact missing required 'period' field. " \
109
+ "Received: #{data.inspect}"
110
+ ```
111
+
112
+ ## HTTP Testing Patterns
113
+
114
+ - [ ] **Use stub_connection/build_connection helpers** - See `spec/support/test_helpers.rb`
115
+ - [ ] **Include ErrorHandler middleware** - Required for error scenario tests
116
+ - [ ] **JSON content type in responses** - `{"Content-Type" => "application/json"}`
117
+ - [ ] **Response body as JSON string** - Use `.to_json` on response hashes
118
+
119
+ ```ruby
120
+ # Good: Complete stub setup
121
+ stubs = Faraday::Adapter::Test::Stubs.new
122
+ stubs.get("/endpoint") do
123
+ [200, {"Content-Type" => "application/json"}, {data: "value"}.to_json]
124
+ end
125
+ stub_connection(stubs)
126
+
127
+ # ... test code ...
128
+
129
+ stubs.verify_stubbed_calls
130
+ ```
131
+
132
+ ## Quick Self-Review
133
+
134
+ Before requesting review, ask yourself:
135
+
136
+ 1. **Would this break in a multi-threaded environment?** (Sidekiq, concurrent requests)
137
+ 2. **What happens if the API returns unexpected data?** (nil, wrong type, missing fields)
138
+ 3. **Can someone understand this code without context?** (clear names, comments where needed)
139
+ 4. **Did I test the failure paths, not just success?**
140
+ 5. **Does the documentation match what the code actually does?**
141
+
142
+ ---
143
+
144
+ *Checklist created from Epic 3 & 4 retrospective learnings*
145
+ *Last updated: 2026-01-07*
@@ -0,0 +1,90 @@
1
+ # sec_api - Project Overview
2
+
3
+ **Generated:** 2026-01-05
4
+ **Scan Level:** Quick
5
+ **Project Type:** Ruby Library (Gem)
6
+
7
+ ## Executive Summary
8
+
9
+ sec_api is a production-grade Ruby client library for accessing SEC EDGAR filings through the sec-api.io API. The project is evolving from a basic v0.1.0 API wrapper into enterprise-level financial data infrastructure for commercial financial analysis platforms.
10
+
11
+ **Current State:** Active development, transitioning from v0.1.0 to v1.0.0
12
+ **Purpose:** Provide Ruby developers with reliable, resilient access to SEC filing data for financial analysis applications
13
+
14
+ ## Project Classification
15
+
16
+ | Attribute | Value |
17
+ |-----------|-------|
18
+ | **Repository Type** | Monolith |
19
+ | **Project Type** | Library (Ruby Gem) |
20
+ | **Primary Language** | Ruby 3.2.3 |
21
+ | **Minimum Ruby** | 3.1.0+ |
22
+ | **Distribution** | RubyGems (rubygems.org) |
23
+ | **Architecture Pattern** | Client → Proxy pattern with middleware stack |
24
+
25
+ ## Technology Stack
26
+
27
+ ### Core Dependencies
28
+ - **faraday** - HTTP client with middleware capabilities
29
+ - **faraday-retry** - Automatic retry middleware
30
+ - **anyway_config** - Configuration management (YAML + environment variables)
31
+ - **dry-struct** - Immutable value objects for type-safe responses
32
+
33
+ ### Development Dependencies
34
+ - **rspec** ~> 3.0 - Testing framework
35
+ - **standard** ~> 1.3 - Ruby linter (Standard Ruby style guide)
36
+ - **async-http-faraday** - Async HTTP support for concurrent requests
37
+
38
+ ### Key Features (v1.0.0 Target)
39
+ - ✅ Configuration management with validation
40
+ - ✅ Exception hierarchy (TransientError/PermanentError)
41
+ - 🚧 Query builder DSL with fluent interface
42
+ - 🚧 Automatic pagination for large result sets
43
+ - 🚧 XBRL data extraction with validation
44
+ - 🚧 Real-time filing notifications (WebSocket streaming)
45
+ - 🚧 Rate limiting intelligence with proactive throttling
46
+ - 🚧 Production observability hooks
47
+
48
+ ## Project Structure
49
+
50
+ ```
51
+ sec_api/
52
+ ├── lib/sec_api/ # Main library code
53
+ │ ├── client.rb # Client entry point
54
+ │ ├── config.rb # Configuration (anyway_config)
55
+ │ ├── errors/ # Exception hierarchy
56
+ │ ├── middleware/ # Faraday middleware (retry, rate limiting)
57
+ │ ├── collections/ # Collection objects (Filings, etc.)
58
+ │ └── objects/ # Value objects (Filing, Entity, etc.)
59
+ ├── spec/ # RSpec tests
60
+ ├── config/ # Configuration files
61
+ └── _bmad-output/ # Planning artifacts (PRD, Architecture, Epics)
62
+ ```
63
+
64
+ ## Development Status
65
+
66
+ ### Completed (v0.1.0)
67
+ - Basic query, search, mapping, and extractor endpoints
68
+ - Configuration via anyway_config
69
+ - Immutable value objects with Dry::Struct
70
+
71
+ ### In Progress (v1.0.0)
72
+ - Production-grade error handling and retry logic
73
+ - Query builder DSL
74
+ - XBRL extraction with validation
75
+ - Real-time streaming API
76
+ - Rate limiting middleware
77
+ - Observability and instrumentation
78
+
79
+ ## Links to Planning Documents
80
+
81
+ - **[PRD](../_bmad-output/planning-artifacts/prd.md)** - Complete product requirements
82
+ - **[Architecture](../_bmad-output/planning-artifacts/architecture.md)** - Architectural decisions and patterns
83
+ - **[Epics & Stories](../_bmad-output/planning-artifacts/epics.md)** - Implementation breakdown
84
+
85
+ ## Links to Documentation
86
+
87
+ - [README](../README.md) - Project overview and usage
88
+ - [CHANGELOG](../CHANGELOG.md) - Version history
89
+ - [Source Tree Analysis](./source-tree-analysis.md) - Code organization
90
+ - [Development Guide](./development-guide.md) - Setup and development workflow
@@ -0,0 +1,60 @@
1
+ {
2
+ "workflow_version": "1.2.0",
3
+ "timestamps": {
4
+ "started": "2026-01-05T03:28:45Z",
5
+ "last_updated": "2026-01-05T03:42:00Z",
6
+ "completed": "2026-01-05T03:42:00Z"
7
+ },
8
+ "mode": "initial_scan",
9
+ "scan_level": "quick",
10
+ "project_root": "/Users/ljuti/Code/projects/metalsmoney/ruby/sec_api",
11
+ "output_folder": "/Users/ljuti/Code/projects/metalsmoney/ruby/sec_api/docs",
12
+ "completed_steps": [
13
+ {
14
+ "step": "step_1",
15
+ "status": "completed",
16
+ "timestamp": "2026-01-05T03:36:30Z",
17
+ "summary": "Classified as monolith with 1 part"
18
+ },
19
+ {
20
+ "step": "step_2",
21
+ "status": "completed",
22
+ "timestamp": "2026-01-05T03:38:00Z",
23
+ "summary": "Found 5 existing docs (README, CHANGELOG, PRD, Architecture, Epics)"
24
+ },
25
+ {
26
+ "step": "step_3",
27
+ "status": "completed",
28
+ "timestamp": "2026-01-05T03:40:00Z",
29
+ "summary": "Tech stack: Ruby 3.2.3, Faraday, anyway_config, dry-struct"
30
+ },
31
+ {
32
+ "step": "step_complete",
33
+ "status": "completed",
34
+ "timestamp": "2026-01-05T03:42:00Z",
35
+ "summary": "Documentation generation complete, 4 files written"
36
+ }
37
+ ],
38
+ "current_step": "completed",
39
+ "findings": {
40
+ "project_classification": "Monolith, 1 part, Ruby library",
41
+ "existing_docs_count": 5,
42
+ "technology_stack": "Ruby 3.2.3, Faraday, anyway_config, dry-struct, RSpec",
43
+ "architecture_pattern": "Client → Proxy pattern with Faraday middleware"
44
+ },
45
+ "project_types": [
46
+ {
47
+ "part_id": "main",
48
+ "project_type_id": "library",
49
+ "display_name": "sec_api (Ruby Gem)"
50
+ }
51
+ ],
52
+ "outputs_generated": [
53
+ "project-scan-report.json",
54
+ "index.md",
55
+ "project-overview.md",
56
+ "source-tree-analysis.md",
57
+ "development-guide.md"
58
+ ],
59
+ "resume_instructions": "Workflow complete"
60
+ }
@@ -0,0 +1,190 @@
1
+ # sec_api - Source Tree Analysis
2
+
3
+ **Generated:** 2026-01-05
4
+ **Project Root:** `/Users/ljuti/Code/projects/metalsmoney/ruby/sec_api`
5
+
6
+ ## Directory Structure with Annotations
7
+
8
+ ```
9
+ sec_api/
10
+ ├── .github/
11
+ │ └── workflows/ # CI/CD workflows (planned for v1.0)
12
+ ├── .git/ # Git repository
13
+ ├── .ruby-lsp/ # Ruby LSP cache
14
+ ├── _bmad/ # BMAD framework installation
15
+ │ ├── core/ # Core BMAD modules
16
+ │ └── bmm/ # BMM (BMAD Methodology Manager) modules
17
+ ├── _bmad-output/ # BMAD planning artifacts
18
+ │ ├── planning-artifacts/ # PRD, Architecture, Epics documents
19
+ │ └── implementation-artifacts/ # (future: sprint status, stories)
20
+ ├── bin/
21
+ │ ├── console # Interactive IRB console for development
22
+ │ └── setup # Setup script for dependencies
23
+ ├── config/
24
+ │ └── secapi.yml # Configuration file (API key, settings)
25
+ ├── docs/ # PROJECT DOCUMENTATION (this directory)
26
+ │ ├── project-overview.md # Project overview (generated)
27
+ │ ├── source-tree-analysis.md # This file
28
+ │ └── project-scan-report.json # Workflow state tracking
29
+ ├── lib/ # MAIN LIBRARY CODE
30
+ │ ├── sec_api.rb # Main entry point (requires all files)
31
+ │ └── sec_api/ # Library modules
32
+ │ ├── client.rb # 🔑 Client entry point (delegates to proxies)
33
+ │ ├── config.rb # Configuration management (anyway_config)
34
+ │ ├── version.rb # Gem version constant
35
+ │ ├── collections/ # Collection objects (Enumerable wrappers)
36
+ │ │ ├── filings.rb # Filings collection
37
+ │ │ └── fulltext_results.rb # Full-text search results
38
+ │ ├── errors/ # 🔑 Exception hierarchy
39
+ │ │ ├── error.rb # Base SecApi::Error
40
+ │ │ ├── transient_error.rb # Retryable errors
41
+ │ │ ├── permanent_error.rb # Non-retryable errors
42
+ │ │ ├── rate_limit_error.rb
43
+ │ │ ├── server_error.rb
44
+ │ │ ├── network_error.rb
45
+ │ │ ├── authentication_error.rb
46
+ │ │ ├── not_found_error.rb
47
+ │ │ ├── validation_error.rb
48
+ │ │ └── configuration_error.rb
49
+ │ ├── middleware/ # 🔑 Faraday middleware stack
50
+ │ │ └── error_handler.rb # HTTP status → exception mapping
51
+ │ ├── objects/ # Value objects (Dry::Struct)
52
+ │ │ ├── filing.rb # Filing metadata
53
+ │ │ ├── entity.rb # Company/entity information
54
+ │ │ ├── fulltext_result.rb
55
+ │ │ ├── data_file.rb
56
+ │ │ └── document_format_file.rb
57
+ │ ├── query.rb # Query API proxy
58
+ │ ├── mapping.rb # Mapping API proxy (ticker/CIK resolution)
59
+ │ ├── extractor.rb # Extractor API proxy
60
+ │ └── xbrl.rb # XBRL API proxy
61
+ ├── sig/ # RBS type signatures (optional)
62
+ ├── spec/ # 🔑 RSPEC TESTS
63
+ │ ├── spec_helper.rb # Test configuration
64
+ │ └── sec_api/ # Test files mirroring lib/ structure
65
+ ├── .gitignore
66
+ ├── .node-version # Node version (for tooling)
67
+ ├── .rspec # RSpec configuration
68
+ ├── .rspec_status # Test run status
69
+ ├── .standard.yml # Standard Ruby linter configuration
70
+ ├── CHANGELOG.md # Version history
71
+ ├── CLAUDE.md # Claude session notes
72
+ ├── Gemfile # Gem dependencies
73
+ ├── Gemfile.lock # Locked dependency versions
74
+ ├── LICENSE.txt # MIT License
75
+ ├── README.md # Project README
76
+ ├── Rakefile # Rake tasks
77
+ └── sec_api.gemspec # Gem specification
78
+ ```
79
+
80
+ ## Critical Directories Explained
81
+
82
+ ### `/lib/sec_api/` - Main Library Code
83
+
84
+ **Purpose:** Core gem functionality organized by technical layer
85
+
86
+ **Architecture Pattern:** Client → Proxy pattern with middleware stack
87
+
88
+ **Key Components:**
89
+ - **client.rb** - Main entry point, delegates to proxies
90
+ - **errors/** - Complete exception hierarchy (TransientError/PermanentError)
91
+ - **middleware/** - Faraday middleware (retry, rate limiting, error handling)
92
+ - **objects/** - Immutable value objects (Dry::Struct)
93
+ - **collections/** - Collection wrappers with Enumerable interface
94
+ - **Proxies:** query.rb, mapping.rb, extractor.rb, xbrl.rb
95
+
96
+ ### `/spec/` - Test Suite
97
+
98
+ **Purpose:** RSpec tests mirroring lib/ structure
99
+
100
+ **Coverage:** >90% target for v1.0.0
101
+
102
+ **Testing Strategy:**
103
+ - VCR/WebMock cassettes for API integration tests
104
+ - Shared examples for cross-cutting behavior (retry, pagination, rate limiting)
105
+ - Unit tests for pure logic
106
+
107
+ ### `/_bmad-output/planning-artifacts/` - Planning Documents
108
+
109
+ **Purpose:** Product requirements, architecture decisions, and epic breakdown
110
+
111
+ **Key Files:**
112
+ - **prd.md** - Product Requirements Document
113
+ - **architecture.md** - Architectural decisions and patterns
114
+ - **epics.md** - Epic and story breakdown for implementation
115
+
116
+ ### `/config/` - Configuration Files
117
+
118
+ **Purpose:** YAML configuration and local overrides
119
+
120
+ **Files:**
121
+ - **secapi.yml** - Default configuration (API key, retry settings, etc.)
122
+ - **secapi.local.yml** (gitignored) - Local environment overrides
123
+
124
+ ## Entry Points
125
+
126
+ ### Main Entry Point
127
+ **File:** `lib/sec_api.rb`
128
+ **Purpose:** Requires all library files, provides top-level namespace
129
+
130
+ ### Client Initialization
131
+ **File:** `lib/sec_api/client.rb`
132
+ **Usage:**
133
+ ```ruby
134
+ client = SecApi::Client.new # Auto-loads config from YAML
135
+ client.query # Query API proxy
136
+ client.mapping # Mapping API proxy
137
+ client.extractor # Extractor API proxy
138
+ client.xbrl # XBRL API proxy
139
+ ```
140
+
141
+ ### Development Console
142
+ **File:** `bin/console`
143
+ **Purpose:** Interactive IRB session with gem loaded for manual testing
144
+
145
+ ## Code Organization Patterns
146
+
147
+ ### Naming Conventions
148
+ - **Modules/Classes:** `SecApi::` namespace, PascalCase
149
+ - **Files:** snake_case matching class names
150
+ - **Methods:** snake_case (Ruby standard)
151
+ - **Constants:** SCREAMING_SNAKE_CASE
152
+
153
+ ### File-to-Class Mapping
154
+ - `lib/sec_api/client.rb` → `SecApi::Client`
155
+ - `lib/sec_api/errors/rate_limit_error.rb` → `SecApi::RateLimitError`
156
+ - `lib/sec_api/objects/filing.rb` → `SecApi::Filing`
157
+
158
+ ### Dependencies
159
+ - **External:** Faraday, anyway_config, dry-struct
160
+ - **Internal:** Client → Proxies → Middleware → HTTP API
161
+
162
+ ## Integration Points
163
+
164
+ ### External API
165
+ - **sec-api.io REST API** - All HTTP requests via Faraday
166
+ - **Base URL:** `https://api.sec-api.io` (configurable)
167
+ - **Authentication:** API key in headers
168
+
169
+ ### Configuration
170
+ - **YAML files:** `config/secapi.yml`
171
+ - **Environment variables:** `SECAPI_*` prefix
172
+ - **Managed by:** anyway_config gem
173
+
174
+ ### Testing
175
+ - **Framework:** RSpec
176
+ - **Mocking:** VCR/WebMock for HTTP requests
177
+ - **Linting:** Standard Ruby (standardrb)
178
+
179
+ ## Future Directories (Planned for v1.0.0)
180
+
181
+ Based on the Architecture document, these directories will be added:
182
+
183
+ - `lib/sec_api/proxies/` - Organized proxy objects
184
+ - `lib/sec_api/middleware/retry_config.rb` - Enhanced retry configuration
185
+ - `lib/sec_api/middleware/rate_limiter.rb` - Rate limiting middleware
186
+ - `lib/sec_api/middleware/instrumentation.rb` - Observability hooks
187
+ - `spec/support/shared_examples/` - Shared test behavior
188
+ - `spec/fixtures/vcr_cassettes/` - VCR cassettes organized by proxy
189
+ - `docs/examples/` - Usage examples (query, backfill, streaming)
190
+ - `docs/migration-guide-v1.md` - v0.1.0 → v1.0.0 migration guide
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+
5
+ module SecApi
6
+ # Shared helper methods for callback invocation and error handling.
7
+ #
8
+ # This module provides consistent callback error logging across all middleware
9
+ # and client code. All callback invocations should use this module's helpers
10
+ # to ensure consistent structured logging when callbacks fail.
11
+ #
12
+ # @example Including in a middleware class
13
+ # class MyMiddleware < Faraday::Middleware
14
+ # include SecApi::CallbackHelper
15
+ #
16
+ # def call(env)
17
+ # invoke_callback_safely("my_callback") do
18
+ # @config.my_callback&.call(data: "value")
19
+ # end
20
+ # end
21
+ # end
22
+ #
23
+ module CallbackHelper
24
+ # Logs callback errors to the configured logger with structured JSON format.
25
+ #
26
+ # @param callback_name [String] Name of the callback that failed
27
+ # @param error [Exception] The exception that was raised
28
+ # @param config [SecApi::Config, nil] Config object with logger (optional)
29
+ # @return [void]
30
+ def log_callback_error(callback_name, error, config = nil)
31
+ # Use instance variable @config if config not passed
32
+ cfg = config || (defined?(@config) ? @config : nil) || (defined?(@_config) ? @_config : nil)
33
+ return unless cfg&.logger
34
+
35
+ begin
36
+ cfg.logger.error do
37
+ {
38
+ event: "secapi.callback_error",
39
+ callback: callback_name,
40
+ error_class: error.class.name,
41
+ error_message: error.message
42
+ }.to_json
43
+ end
44
+ rescue
45
+ # Don't let logging errors break anything
46
+ end
47
+ end
48
+ end
49
+ end