rails-flow-map 0.1.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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +52 -0
- data/LICENSE +21 -0
- data/README.md +314 -0
- data/README_ja.md +314 -0
- data/README_zh.md +314 -0
- data/lib/rails_flow_map/analyzers/controller_analyzer.rb +124 -0
- data/lib/rails_flow_map/analyzers/model_analyzer.rb +139 -0
- data/lib/rails_flow_map/configuration.rb +22 -0
- data/lib/rails_flow_map/engine.rb +16 -0
- data/lib/rails_flow_map/errors.rb +240 -0
- data/lib/rails_flow_map/formatters/d3js_formatter.rb +488 -0
- data/lib/rails_flow_map/formatters/erd_formatter.rb +64 -0
- data/lib/rails_flow_map/formatters/git_diff_formatter.rb +589 -0
- data/lib/rails_flow_map/formatters/graphviz_formatter.rb +111 -0
- data/lib/rails_flow_map/formatters/mermaid_formatter.rb +91 -0
- data/lib/rails_flow_map/formatters/metrics_formatter.rb +196 -0
- data/lib/rails_flow_map/formatters/openapi_formatter.rb +557 -0
- data/lib/rails_flow_map/formatters/plantuml_formatter.rb +92 -0
- data/lib/rails_flow_map/formatters/sequence_formatter.rb +288 -0
- data/lib/rails_flow_map/generators/install/templates/rails_flow_map.rb +34 -0
- data/lib/rails_flow_map/generators/install_generator.rb +32 -0
- data/lib/rails_flow_map/logging.rb +215 -0
- data/lib/rails_flow_map/models/flow_edge.rb +31 -0
- data/lib/rails_flow_map/models/flow_graph.rb +58 -0
- data/lib/rails_flow_map/models/flow_node.rb +37 -0
- data/lib/rails_flow_map/version.rb +3 -0
- data/lib/rails_flow_map.rb +310 -0
- data/lib/tasks/rails_flow_map.rake +70 -0
- metadata +156 -0
@@ -0,0 +1,240 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module RailsFlowMap
|
4
|
+
# Base error class for all RailsFlowMap errors
|
5
|
+
#
|
6
|
+
# This class provides a common interface for all errors raised by the
|
7
|
+
# RailsFlowMap gem, including error categorization, context preservation,
|
8
|
+
# and structured error reporting.
|
9
|
+
class Error < StandardError
|
10
|
+
attr_reader :context, :error_code, :category
|
11
|
+
|
12
|
+
# Initialize a new error with context and categorization
|
13
|
+
#
|
14
|
+
# @param message [String] The error message
|
15
|
+
# @param context [Hash] Additional context about the error
|
16
|
+
# @param error_code [String] Unique error code for programmatic handling
|
17
|
+
# @param category [Symbol] Error category for classification
|
18
|
+
def initialize(message, context: {}, error_code: nil, category: :general)
|
19
|
+
super(message)
|
20
|
+
@context = context
|
21
|
+
@error_code = error_code || self.class.name.split('::').last.downcase
|
22
|
+
@category = category
|
23
|
+
end
|
24
|
+
|
25
|
+
# @return [Hash] Full error information including context
|
26
|
+
def to_h
|
27
|
+
{
|
28
|
+
error_class: self.class.name,
|
29
|
+
message: message,
|
30
|
+
error_code: error_code,
|
31
|
+
category: category,
|
32
|
+
context: context,
|
33
|
+
backtrace: backtrace&.first(10)
|
34
|
+
}
|
35
|
+
end
|
36
|
+
|
37
|
+
# @return [String] JSON representation of the error
|
38
|
+
def to_json(*args)
|
39
|
+
require 'json'
|
40
|
+
to_h.to_json(*args)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
# Raised when graph parsing fails
|
45
|
+
class GraphParseError < Error
|
46
|
+
def initialize(message, context: {})
|
47
|
+
super(message, context: context, category: :parsing)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Raised when graph validation fails
|
52
|
+
class GraphValidationError < Error
|
53
|
+
def initialize(message, context: {})
|
54
|
+
super(message, context: context, category: :validation)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Raised when formatter encounters an error
|
59
|
+
class FormatterError < Error
|
60
|
+
def initialize(message, context: {})
|
61
|
+
super(message, context: context, category: :formatting)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Raised when file operations fail
|
66
|
+
class FileOperationError < Error
|
67
|
+
def initialize(message, context: {})
|
68
|
+
super(message, context: context, category: :file_operation)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Raised when security validation fails
|
73
|
+
class SecurityError < Error
|
74
|
+
def initialize(message, context: {})
|
75
|
+
super(message, context: context, category: :security)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
# Raised when configuration is invalid
|
80
|
+
class ConfigurationError < Error
|
81
|
+
def initialize(message, context: {})
|
82
|
+
super(message, context: context, category: :configuration)
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
# Raised when a feature is not implemented
|
87
|
+
class NotImplementedError < Error
|
88
|
+
def initialize(message = "Feature not yet implemented", context: {})
|
89
|
+
super(message, context: context, category: :not_implemented)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
# Raised when invalid input is provided
|
94
|
+
class InvalidInputError < Error
|
95
|
+
def initialize(message, context: {})
|
96
|
+
super(message, context: context, category: :invalid_input)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# Raised when resource limits are exceeded
|
101
|
+
class ResourceLimitError < Error
|
102
|
+
def initialize(message, context: {})
|
103
|
+
super(message, context: context, category: :resource_limit)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
# Error handling utilities
|
108
|
+
module ErrorHandler
|
109
|
+
extend self
|
110
|
+
|
111
|
+
# Maximum number of retry attempts for transient errors
|
112
|
+
MAX_RETRIES = 3
|
113
|
+
|
114
|
+
# Errors that can be retried
|
115
|
+
RETRYABLE_ERRORS = [
|
116
|
+
Errno::ENOENT,
|
117
|
+
Errno::EACCES,
|
118
|
+
Errno::EAGAIN,
|
119
|
+
Errno::EWOULDBLOCK
|
120
|
+
].freeze
|
121
|
+
|
122
|
+
# Execute a block with comprehensive error handling
|
123
|
+
#
|
124
|
+
# @param operation [String] Description of the operation
|
125
|
+
# @param context [Hash] Additional context for error reporting
|
126
|
+
# @param retries [Integer] Number of retry attempts for transient errors
|
127
|
+
# @yield The block to execute
|
128
|
+
# @return The result of the block
|
129
|
+
# @raise [RailsFlowMap::Error] Re-raised as appropriate RailsFlowMap error
|
130
|
+
def with_error_handling(operation, context: {}, retries: MAX_RETRIES)
|
131
|
+
attempt = 0
|
132
|
+
|
133
|
+
begin
|
134
|
+
Logging.with_context(operation: operation, **context) do
|
135
|
+
yield
|
136
|
+
end
|
137
|
+
rescue *RETRYABLE_ERRORS => e
|
138
|
+
attempt += 1
|
139
|
+
if attempt <= retries
|
140
|
+
Logging.logger.warn("Retrying #{operation} (attempt #{attempt}/#{retries}): #{e.message}")
|
141
|
+
sleep(0.1 * attempt) # Exponential backoff
|
142
|
+
retry
|
143
|
+
else
|
144
|
+
handle_error(e, operation, context.merge(max_retries_exceeded: true))
|
145
|
+
end
|
146
|
+
rescue RailsFlowMap::Error => e
|
147
|
+
# Already a RailsFlowMap error, just log and re-raise
|
148
|
+
Logging.log_error(e, context: context.merge(operation: operation))
|
149
|
+
raise
|
150
|
+
rescue => e
|
151
|
+
handle_error(e, operation, context)
|
152
|
+
end
|
153
|
+
end
|
154
|
+
|
155
|
+
# Convert standard errors to RailsFlowMap errors with context
|
156
|
+
#
|
157
|
+
# @param error [Exception] The original error
|
158
|
+
# @param operation [String] Description of the operation that failed
|
159
|
+
# @param context [Hash] Additional context
|
160
|
+
# @raise [RailsFlowMap::Error] Appropriate RailsFlowMap error type
|
161
|
+
def handle_error(error, operation, context = {})
|
162
|
+
error_context = context.merge(
|
163
|
+
operation: operation,
|
164
|
+
original_error: error.class.name
|
165
|
+
)
|
166
|
+
|
167
|
+
rails_flow_map_error = case error
|
168
|
+
when JSON::ParserError, YAML::SyntaxError
|
169
|
+
GraphParseError.new("Failed to parse data during #{operation}: #{error.message}", context: error_context)
|
170
|
+
when Errno::ENOENT, Errno::EACCES
|
171
|
+
FileOperationError.new("File operation failed during #{operation}: #{error.message}", context: error_context)
|
172
|
+
when ArgumentError
|
173
|
+
InvalidInputError.new("Invalid input during #{operation}: #{error.message}", context: error_context)
|
174
|
+
when SystemStackError
|
175
|
+
ResourceLimitError.new("Stack overflow during #{operation}, input too complex", context: error_context)
|
176
|
+
when NoMemoryError
|
177
|
+
ResourceLimitError.new("Out of memory during #{operation}", context: error_context)
|
178
|
+
else
|
179
|
+
Error.new("Unexpected error during #{operation}: #{error.message}", context: error_context)
|
180
|
+
end
|
181
|
+
|
182
|
+
Logging.log_error(rails_flow_map_error, context: error_context)
|
183
|
+
raise rails_flow_map_error
|
184
|
+
end
|
185
|
+
|
186
|
+
# Validate input parameters and raise errors if invalid
|
187
|
+
#
|
188
|
+
# @param validations [Hash] Hash of parameter_name => validation_proc pairs
|
189
|
+
# @param context [Hash] Additional context for error reporting
|
190
|
+
# @raise [InvalidInputError] If any validation fails
|
191
|
+
def validate_input!(validations, context: {})
|
192
|
+
validations.each do |param_name, validation|
|
193
|
+
begin
|
194
|
+
next if validation.call
|
195
|
+
rescue => e
|
196
|
+
raise InvalidInputError.new(
|
197
|
+
"Validation failed for parameter '#{param_name}': #{e.message}",
|
198
|
+
context: context.merge(parameter: param_name)
|
199
|
+
)
|
200
|
+
end
|
201
|
+
|
202
|
+
raise InvalidInputError.new(
|
203
|
+
"Invalid value for parameter '#{param_name}'",
|
204
|
+
context: context.merge(parameter: param_name)
|
205
|
+
)
|
206
|
+
end
|
207
|
+
end
|
208
|
+
|
209
|
+
# Check if an error is retryable
|
210
|
+
#
|
211
|
+
# @param error [Exception] The error to check
|
212
|
+
# @return [Boolean] True if the error can be retried
|
213
|
+
def retryable?(error)
|
214
|
+
RETRYABLE_ERRORS.any? { |retryable_class| error.is_a?(retryable_class) }
|
215
|
+
end
|
216
|
+
|
217
|
+
# Extract user-friendly error message from any error
|
218
|
+
#
|
219
|
+
# @param error [Exception] The error to extract message from
|
220
|
+
# @return [String] User-friendly error message
|
221
|
+
def user_friendly_message(error)
|
222
|
+
case error
|
223
|
+
when RailsFlowMap::GraphParseError
|
224
|
+
"Failed to parse the graph data. Please check your input format."
|
225
|
+
when RailsFlowMap::FileOperationError
|
226
|
+
"File operation failed. Please check file permissions and paths."
|
227
|
+
when RailsFlowMap::InvalidInputError
|
228
|
+
"Invalid input provided. #{error.message}"
|
229
|
+
when RailsFlowMap::SecurityError
|
230
|
+
"Security validation failed. Operation blocked for safety."
|
231
|
+
when RailsFlowMap::ResourceLimitError
|
232
|
+
"Operation exceeded resource limits. Please try with smaller input."
|
233
|
+
when RailsFlowMap::Error
|
234
|
+
error.message
|
235
|
+
else
|
236
|
+
"An unexpected error occurred. Please try again."
|
237
|
+
end
|
238
|
+
end
|
239
|
+
end
|
240
|
+
end
|