synthra 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/.rspec +4 -0
- data/.rubocop.yml +40 -0
- data/.yardopts +18 -0
- data/ACCESS_DOCS.md +116 -0
- data/ADVANCED_FEATURES_SUMMARY.md +245 -0
- data/CHANGELOG.md +498 -0
- data/CODE_OF_CONDUCT.md +122 -0
- data/CONTRIBUTING.md +307 -0
- data/DOCUMENTATION.md +204 -0
- data/DOCUMENTATION_GUIDE.md +194 -0
- data/LICENSE +22 -0
- data/README.md +1458 -0
- data/Rakefile +128 -0
- data/benchmark/README.md +591 -0
- data/benchmark/batch_generation.rb +63 -0
- data/benchmark/engine_comparison.rb +295 -0
- data/benchmark/single_record.rb +108 -0
- data/benchmark/streaming.rb +74 -0
- data/docs/ACTIVERECORD_INFERENCE.md +524 -0
- data/docs/API_SERVER.md +278 -0
- data/docs/CONFIG_FILE.md +315 -0
- data/docs/DATA_CONTRACTS.md +312 -0
- data/docs/ENHANCED_REPL.md +304 -0
- data/docs/FACTORY_BOT.md +271 -0
- data/docs/GITHUB_ACTION.md +420 -0
- data/docs/GRAPHQL_EXPORT.md +303 -0
- data/docs/GRAPHQL_FEDERATION.md +651 -0
- data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
- data/docs/LSP.md +189 -0
- data/docs/MIGRATION_GENERATOR.md +547 -0
- data/docs/MOCK_SERVER.md +280 -0
- data/docs/NATIVE_ENGINE.md +298 -0
- data/docs/OPENAPI_EXPORT.md +252 -0
- data/docs/PERFORMANCE_MODE.md +228 -0
- data/docs/PERSONAS.md +547 -0
- data/docs/PROPERTY_TESTING.md +284 -0
- data/docs/PROTOBUF_EXPORT.md +276 -0
- data/docs/QUALITY_METRICS.md +555 -0
- data/docs/QUICK_REFERENCE.md +88 -0
- data/docs/RAILS_ENGINE.md +404 -0
- data/docs/RAILS_INTEGRATION.md +454 -0
- data/docs/README.md +175 -0
- data/docs/REPL_QUICK_REFERENCE.md +149 -0
- data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
- data/docs/SCENARIOS.md +576 -0
- data/docs/SECURITY_TESTING.md +318 -0
- data/docs/SNAPSHOT_TESTING.md +308 -0
- data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
- data/docs/TERRAFORM_EXPORT.md +447 -0
- data/docs/TIME_TRAVEL.md +507 -0
- data/docs/USAGE_GUIDE.md +980 -0
- data/docs/WEBHOOK_SIMULATOR.md +579 -0
- data/docs/advanced_features.md +183 -0
- data/docs/api_reference.md +153 -0
- data/docs/behaviors.md +138 -0
- data/docs/best_practices.md +191 -0
- data/docs/dsl_reference.md +172 -0
- data/docs/examples.md +162 -0
- data/docs/generation_modes.md +125 -0
- data/docs/getting_started.md +92 -0
- data/docs/index.html +177 -0
- data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
- data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
- data/docs/troubleshooting.md +215 -0
- data/docs/type_reference.md +224 -0
- data/examples/hostile_mode_demo.rb +64 -0
- data/exe/synthra +6 -0
- data/ext/synthra_native/Cargo.lock +972 -0
- data/ext/synthra_native/Cargo.toml +46 -0
- data/ext/synthra_native/extconf.rb +41 -0
- data/ext/synthra_native/src/generator.rs +397 -0
- data/ext/synthra_native/src/lib.rs +229 -0
- data/ext/synthra_native/src/types.rs +1485 -0
- data/lib/generators/synthra/install_generator.rb +86 -0
- data/lib/generators/synthra/templates/api_response.dsl +15 -0
- data/lib/generators/synthra/templates/fake_data.rake +116 -0
- data/lib/generators/synthra/templates/synthra.yml +31 -0
- data/lib/generators/synthra/templates/synthra_support.rb +19 -0
- data/lib/generators/synthra/templates/user.dsl +11 -0
- data/lib/synthra/activerecord_inference.rb +394 -0
- data/lib/synthra/api.rb +381 -0
- data/lib/synthra/api_server.rb +718 -0
- data/lib/synthra/behaviors/applicator.rb +165 -0
- data/lib/synthra/behaviors/base.rb +155 -0
- data/lib/synthra/behaviors/close_connection.rb +60 -0
- data/lib/synthra/behaviors/deprecated.rb +100 -0
- data/lib/synthra/behaviors/failure.rb +69 -0
- data/lib/synthra/behaviors/latency.rb +103 -0
- data/lib/synthra/behaviors/partial_data.rb +83 -0
- data/lib/synthra/behaviors/randomize_order.rb +74 -0
- data/lib/synthra/behaviors/registry.rb +238 -0
- data/lib/synthra/behaviors/simulate_error.rb +79 -0
- data/lib/synthra/cli/commands/base.rb +105 -0
- data/lib/synthra/cli/commands/diff.rb +227 -0
- data/lib/synthra/cli/commands/docs.rb +67 -0
- data/lib/synthra/cli/commands/export.rb +150 -0
- data/lib/synthra/cli/commands/generate.rb +72 -0
- data/lib/synthra/cli/commands/import.rb +80 -0
- data/lib/synthra/cli/commands/lint.rb +53 -0
- data/lib/synthra/cli/commands/live.rb +57 -0
- data/lib/synthra/cli/commands/seed.rb +99 -0
- data/lib/synthra/cli/commands/validate.rb +28 -0
- data/lib/synthra/cli.rb +2471 -0
- data/lib/synthra/config_file.rb +129 -0
- data/lib/synthra/configuration.rb +281 -0
- data/lib/synthra/contracts_registry.rb +408 -0
- data/lib/synthra/database_seeder.rb +268 -0
- data/lib/synthra/deterministic_ids.rb +218 -0
- data/lib/synthra/documentation_generator.rb +414 -0
- data/lib/synthra/engine.rb +251 -0
- data/lib/synthra/errors.rb +1169 -0
- data/lib/synthra/export/base.rb +85 -0
- data/lib/synthra/export/csv.rb +101 -0
- data/lib/synthra/export/graphql.rb +266 -0
- data/lib/synthra/export/graphql_federation.rb +377 -0
- data/lib/synthra/export/graphviz.rb +258 -0
- data/lib/synthra/export/javascript.rb +327 -0
- data/lib/synthra/export/json_data.rb +61 -0
- data/lib/synthra/export/json_schema.rb +290 -0
- data/lib/synthra/export/openapi.rb +514 -0
- data/lib/synthra/export/protobuf.rb +483 -0
- data/lib/synthra/export/python.rb +560 -0
- data/lib/synthra/export/sql.rb +381 -0
- data/lib/synthra/export/sql_insert.rb +152 -0
- data/lib/synthra/export/terraform.rb +501 -0
- data/lib/synthra/export/type_mapping.rb +389 -0
- data/lib/synthra/export/typescript.rb +300 -0
- data/lib/synthra/export/xml_data.rb +104 -0
- data/lib/synthra/export/yaml_data.rb +56 -0
- data/lib/synthra/export.rb +404 -0
- data/lib/synthra/factory_bot_integration.rb +157 -0
- data/lib/synthra/field.rb +440 -0
- data/lib/synthra/functions/registry.rb +104 -0
- data/lib/synthra/generator/context.rb +372 -0
- data/lib/synthra/generator/engine.rb +336 -0
- data/lib/synthra/generator/faker_adapter.rb +425 -0
- data/lib/synthra/generator/modes.rb +444 -0
- data/lib/synthra/generator/resolver.rb +256 -0
- data/lib/synthra/generator/rng.rb +273 -0
- data/lib/synthra/generator/streamer.rb +63 -0
- data/lib/synthra/generator/uniqueness.rb +118 -0
- data/lib/synthra/initializer.rb +97 -0
- data/lib/synthra/limits.rb +279 -0
- data/lib/synthra/live_preview.rb +518 -0
- data/lib/synthra/loader_config.rb +78 -0
- data/lib/synthra/lsp/server.rb +689 -0
- data/lib/synthra/migration_generator.rb +301 -0
- data/lib/synthra/mixin.rb +174 -0
- data/lib/synthra/mock_server.rb +458 -0
- data/lib/synthra/native_engine.rb +304 -0
- data/lib/synthra/openapi_importer.rb +228 -0
- data/lib/synthra/output/json_formatter.rb +40 -0
- data/lib/synthra/output/ndjson_formatter.rb +55 -0
- data/lib/synthra/parser/ast.rb +1287 -0
- data/lib/synthra/parser/lexer.rb +1152 -0
- data/lib/synthra/parser/parser.rb +1664 -0
- data/lib/synthra/parser/tokens.rb +461 -0
- data/lib/synthra/performance_mode.rb +364 -0
- data/lib/synthra/personas.rb +397 -0
- data/lib/synthra/property_testing.rb +172 -0
- data/lib/synthra/quality_metrics.rb +405 -0
- data/lib/synthra/rails_test_helper.rb +248 -0
- data/lib/synthra/registry.rb +533 -0
- data/lib/synthra/relationships.rb +193 -0
- data/lib/synthra/repl/enhanced_repl.rb +607 -0
- data/lib/synthra/repl/formatter.rb +191 -0
- data/lib/synthra/scenarios.rb +423 -0
- data/lib/synthra/schema.rb +605 -0
- data/lib/synthra/schema_inheritance.rb +104 -0
- data/lib/synthra/schema_versioning.rb +212 -0
- data/lib/synthra/snapshot_testing.rb +199 -0
- data/lib/synthra/time_travel.rb +338 -0
- data/lib/synthra/type_definitions.rb +274 -0
- data/lib/synthra/types/address_location/addresses.rb +125 -0
- data/lib/synthra/types/address_location/airports.rb +200 -0
- data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
- data/lib/synthra/types/address_location/locations.rb +406 -0
- data/lib/synthra/types/base.rb +48 -0
- data/lib/synthra/types/commerce_products/commerce.rb +119 -0
- data/lib/synthra/types/commerce_products/companies.rb +71 -0
- data/lib/synthra/types/commerce_products/construction.rb +126 -0
- data/lib/synthra/types/commerce_products/products.rb +205 -0
- data/lib/synthra/types/core/collections.rb +513 -0
- data/lib/synthra/types/core/defaults.rb +76 -0
- data/lib/synthra/types/core/enums.rb +102 -0
- data/lib/synthra/types/core/identifiers.rb +445 -0
- data/lib/synthra/types/core/primitives.rb +586 -0
- data/lib/synthra/types/core/references.rb +466 -0
- data/lib/synthra/types/core/sequences.rb +304 -0
- data/lib/synthra/types/crypto/crypto.rb +162 -0
- data/lib/synthra/types/date_time/dates.rb +387 -0
- data/lib/synthra/types/finance_banking/banking.rb +424 -0
- data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
- data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
- data/lib/synthra/types/formula.rb +184 -0
- data/lib/synthra/types/health_medical/medical.rb +150 -0
- data/lib/synthra/types/hostile_payloads.rb +122 -0
- data/lib/synthra/types/json_array.rb +51 -0
- data/lib/synthra/types/media_entertainment/media.rb +62 -0
- data/lib/synthra/types/naughty_string.rb +30 -0
- data/lib/synthra/types/personal_names/chinese.rb +41 -0
- data/lib/synthra/types/personal_names/identifiers.rb +187 -0
- data/lib/synthra/types/personal_names/names.rb +246 -0
- data/lib/synthra/types/personal_names/national_id.rb +89 -0
- data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
- data/lib/synthra/types/regex.rb +248 -0
- data/lib/synthra/types/registry.rb +182 -0
- data/lib/synthra/types/repeating_element.rb +50 -0
- data/lib/synthra/types/scenario.rb +29 -0
- data/lib/synthra/types/technology_internet/apps.rb +67 -0
- data/lib/synthra/types/technology_internet/communication.rb +162 -0
- data/lib/synthra/types/technology_internet/devices.rb +80 -0
- data/lib/synthra/types/technology_internet/formats.rb +139 -0
- data/lib/synthra/types/technology_internet/networking.rb +143 -0
- data/lib/synthra/types/template.rb +92 -0
- data/lib/synthra/types/text_content/business.rb +128 -0
- data/lib/synthra/types/text_content/colors.rb +70 -0
- data/lib/synthra/types/text_content/misc.rb +237 -0
- data/lib/synthra/types/text_content/security.rb +130 -0
- data/lib/synthra/types/text_content/text_generation.rb +532 -0
- data/lib/synthra/types/travel/travel.rb +150 -0
- data/lib/synthra/utils/string_distance.rb +133 -0
- data/lib/synthra/validator/dsl_validator.rb +339 -0
- data/lib/synthra/validator/path_validator.rb +615 -0
- data/lib/synthra/version.rb +35 -0
- data/lib/synthra/webhook_simulator.rb +341 -0
- data/lib/synthra.rb +259 -0
- data/schemas/address.dsl +16 -0
- data/schemas/api_response.dsl +8 -0
- data/schemas/error_payload.dsl +9 -0
- data/schemas/order.dsl +10 -0
- data/schemas/order_item.dsl +8 -0
- data/schemas/payment.dsl +11 -0
- data/schemas/social_post.dsl +13 -0
- data/schemas/user.dsl +10 -0
- data/scripts/batch_fix_all.rb +96 -0
- data/scripts/delete_old_files.rb +32 -0
- data/scripts/fix_all_grouped_files.rb +137 -0
- data/scripts/fix_all_indentation.rb +48 -0
- data/scripts/fix_all_syntax.rb +124 -0
- data/scripts/fix_grouped_files.rb +184 -0
- data/scripts/fix_syntax_errors.rb +108 -0
- data/scripts/group_domain_types.rb +112 -0
- data/scripts/group_domain_types_fixed.rb +150 -0
- data/scripts/merge_domains_to_one_file.rb +68 -0
- data/scripts/move_existing_types.rb +130 -0
- data/scripts/split_grouped_types.rb +142 -0
- data/tech_docs/README.md +134 -0
- data/tech_docs/advanced/streaming.md +405 -0
- data/tech_docs/advanced/thread_safety.md +253 -0
- data/tech_docs/api/overview.md +485 -0
- data/tech_docs/appendices/type_chart.md +193 -0
- data/tech_docs/basic_concepts.md +383 -0
- data/tech_docs/behaviors/overview.md +346 -0
- data/tech_docs/dsl/complex_types.md +790 -0
- data/tech_docs/dsl/core_types.md +464 -0
- data/tech_docs/dsl/datetime_types.md +325 -0
- data/tech_docs/dsl/field_modifiers.md +414 -0
- data/tech_docs/dsl/grammar.md +431 -0
- data/tech_docs/dsl/schema_definition.md +399 -0
- data/tech_docs/export/README.md +276 -0
- data/tech_docs/installation.md +273 -0
- data/tech_docs/integration/ci_cd.md +707 -0
- data/tech_docs/integration/ci_cd_guide.md +579 -0
- data/tech_docs/integration/factory_bot.md +485 -0
- data/tech_docs/integration/rails.md +630 -0
- data/tech_docs/integration/rspec.md +449 -0
- data/tech_docs/modes/overview.md +350 -0
- data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
- data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
- data/tech_docs/quick_start.md +256 -0
- data/tech_docs/templates/README.md +805 -0
- data/tech_docs/tutorials/advanced.md +371 -0
- data/tech_docs/tutorials/getting_started.md +189 -0
- data/tech_docs/tutorials/intermediate.md +231 -0
- data/tech_docs/tutorials/template_gallery.md +569 -0
- data/tech_example/01_basic_usage.rb +238 -0
- data/tech_example/02_types_demo.rb +336 -0
- data/tech_example/04_cli_usage.md +429 -0
- data/tech_example/05_database_seeding.rb +283 -0
- data/tech_example/07_rspec_integration.rb +359 -0
- data/tech_example/10_custom_types.rb +387 -0
- data/tech_example/12_behaviors.rb +372 -0
- data/tech_example/13_twitter_dm_example.rb +120 -0
- data/tech_example/14_exact_json_structure.rb +253 -0
- data/tech_example/16_lsp_server.rb +56 -0
- data/tech_example/17_property_testing.rb +92 -0
- data/tech_example/18_enhanced_repl.rb +120 -0
- data/tech_example/19_security_fuzzing.rb +145 -0
- data/tech_example/NEW_FEATURES.md +169 -0
- data/tech_example/README.md +118 -0
- data/tech_example/schemas/api_response.dsl +123 -0
- data/tech_example/schemas/ecommerce.dsl +121 -0
- data/tech_example/schemas/twitter_dm.dsl +109 -0
- data/tech_example/schemas/user.dsl +36 -0
- data/vscode-extension/README.md +246 -0
- data/vscode-extension/language-configuration.json +31 -0
- data/vscode-extension/package.json +55 -0
- data/vscode-extension/snippets/fakedatadsl.json +198 -0
- data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
- metadata +478 -0
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
# Thread Safety Guide
|
|
2
|
+
|
|
3
|
+
FakeDataDSL is designed for safe use in multi-threaded applications. This guide explains what is and isn't thread-safe.
|
|
4
|
+
|
|
5
|
+
## Quick Reference
|
|
6
|
+
|
|
7
|
+
| Operation | Thread-Safe? | Notes |
|
|
8
|
+
|-----------|--------------|-------|
|
|
9
|
+
| `schema.generate()` | ✅ Yes | Each call is independent |
|
|
10
|
+
| `schema.generate_many()` | ✅ Yes | Batch is atomic |
|
|
11
|
+
| `schema.generate_stream()` | ✅ Yes | Iterator is independent |
|
|
12
|
+
| `registry.load_file()` | ✅ Yes | Mutex-protected |
|
|
13
|
+
| `registry.schema()` | ✅ Yes | Mutex-protected |
|
|
14
|
+
| `shared()` within batch | ✅ Yes | Context is per-batch |
|
|
15
|
+
| `shared()` across batches | ⚠️ N/A | Each batch has own context |
|
|
16
|
+
| Custom functions | ⚠️ Depends | Your code must be thread-safe |
|
|
17
|
+
| `FakeDataDSL.configure` | ⚠️ No | Configure before multi-threading |
|
|
18
|
+
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
## Safe Patterns
|
|
22
|
+
|
|
23
|
+
### Pattern 1: Concurrent Generation with Same Schema
|
|
24
|
+
|
|
25
|
+
```ruby
|
|
26
|
+
schema = registry.schema("User")
|
|
27
|
+
|
|
28
|
+
# Safe: Each thread generates independently
|
|
29
|
+
threads = 10.times.map do
|
|
30
|
+
Thread.new do
|
|
31
|
+
100.times { schema.generate(registry: registry) }
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
threads.each(&:join)
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
### Pattern 2: Different Seeds per Thread
|
|
39
|
+
|
|
40
|
+
```ruby
|
|
41
|
+
schema = registry.schema("User")
|
|
42
|
+
|
|
43
|
+
# Safe: Different seeds ensure different outputs
|
|
44
|
+
threads = 10.times.map do |i|
|
|
45
|
+
Thread.new do
|
|
46
|
+
seed = i * 1000
|
|
47
|
+
schema.generate_many(100, seed: seed, registry: registry)
|
|
48
|
+
end
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
results = threads.map(&:value)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### Pattern 3: Shared Registry
|
|
55
|
+
|
|
56
|
+
```ruby
|
|
57
|
+
# Load schemas once
|
|
58
|
+
registry = FakeDataDSL::Registry.new
|
|
59
|
+
registry.load_dir("schemas/")
|
|
60
|
+
|
|
61
|
+
# Safe: Registry is thread-safe for reads
|
|
62
|
+
threads = 10.times.map do
|
|
63
|
+
Thread.new do
|
|
64
|
+
user_schema = registry.schema("User")
|
|
65
|
+
order_schema = registry.schema("Order")
|
|
66
|
+
|
|
67
|
+
user_schema.generate(registry: registry)
|
|
68
|
+
order_schema.generate(registry: registry)
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
75
|
+
## What About `shared()`?
|
|
76
|
+
|
|
77
|
+
The `shared()` type is designed for use within a **single batch** generated by `generate_many()`:
|
|
78
|
+
|
|
79
|
+
```ruby
|
|
80
|
+
# Safe: shared() context is isolated to this batch
|
|
81
|
+
webhooks = schema.generate_many(10, registry: registry)
|
|
82
|
+
# All 10 webhooks have the same shared recipient
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### Important: `shared()` Does NOT Share Across Batches
|
|
86
|
+
|
|
87
|
+
```ruby
|
|
88
|
+
# Each batch has its OWN shared context
|
|
89
|
+
batch1 = schema.generate_many(10, registry: registry) # Recipient A
|
|
90
|
+
batch2 = schema.generate_many(10, registry: registry) # Recipient B (different!)
|
|
91
|
+
|
|
92
|
+
# This is correct behavior - batches are independent
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
### Concurrent Batches with `shared()`
|
|
96
|
+
|
|
97
|
+
```ruby
|
|
98
|
+
schema = registry.schema("TwitterDMWebhook")
|
|
99
|
+
|
|
100
|
+
# Safe: Each batch has independent shared context
|
|
101
|
+
threads = 5.times.map do
|
|
102
|
+
Thread.new do
|
|
103
|
+
schema.generate_many(100, registry: registry)
|
|
104
|
+
end
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
batches = threads.map(&:value)
|
|
108
|
+
# Each batch has consistent shared values within itself
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
---
|
|
112
|
+
|
|
113
|
+
## Unsafe Patterns
|
|
114
|
+
|
|
115
|
+
### Anti-Pattern 1: Modifying Configuration During Generation
|
|
116
|
+
|
|
117
|
+
```ruby
|
|
118
|
+
# ❌ UNSAFE: Don't modify config while generating
|
|
119
|
+
Thread.new do
|
|
120
|
+
1000.times { schema.generate }
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
Thread.new do
|
|
124
|
+
FakeDataDSL.configure { |c| c.default_mode = :edge } # Race condition!
|
|
125
|
+
end
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
**Fix**: Configure before spawning threads.
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
# ✅ Safe: Configure first
|
|
132
|
+
FakeDataDSL.configure { |c| c.default_mode = :edge }
|
|
133
|
+
|
|
134
|
+
threads = 10.times.map { Thread.new { schema.generate } }
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
### Anti-Pattern 2: Sharing Mutable State in Custom Functions
|
|
138
|
+
|
|
139
|
+
```ruby
|
|
140
|
+
# ❌ UNSAFE: Mutable shared state
|
|
141
|
+
counter = 0
|
|
142
|
+
|
|
143
|
+
FakeDataDSL.register_function(:next_id) do |context|
|
|
144
|
+
counter += 1 # Race condition!
|
|
145
|
+
end
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
**Fix**: Use thread-local storage or atomic operations.
|
|
149
|
+
|
|
150
|
+
```ruby
|
|
151
|
+
# ✅ Safe: Thread-local counter
|
|
152
|
+
FakeDataDSL.register_function(:next_id) do |context|
|
|
153
|
+
Thread.current[:counter] ||= 0
|
|
154
|
+
Thread.current[:counter] += 1
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
# ✅ Safe: Atomic counter
|
|
158
|
+
require 'concurrent'
|
|
159
|
+
counter = Concurrent::AtomicFixnum.new(0)
|
|
160
|
+
|
|
161
|
+
FakeDataDSL.register_function(:next_id) do |context|
|
|
162
|
+
counter.increment
|
|
163
|
+
end
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Anti-Pattern 3: Loading Schemas While Generating
|
|
167
|
+
|
|
168
|
+
```ruby
|
|
169
|
+
# ⚠️ Technically safe but not recommended
|
|
170
|
+
Thread.new { registry.load_file("new_schema.dsl") }
|
|
171
|
+
Thread.new { registry.schema("User").generate }
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
**Fix**: Load all schemas before starting generation.
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## Determinism in Concurrent Scenarios
|
|
179
|
+
|
|
180
|
+
### Same Seed = Same Output (Even in Threads)
|
|
181
|
+
|
|
182
|
+
```ruby
|
|
183
|
+
# Results are deterministic when using the same seed
|
|
184
|
+
result1 = schema.generate(seed: 12345)
|
|
185
|
+
result2 = schema.generate(seed: 12345)
|
|
186
|
+
|
|
187
|
+
result1 == result2 # true, regardless of threading
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
### Batch Determinism
|
|
191
|
+
|
|
192
|
+
```ruby
|
|
193
|
+
# Same seed produces same batch
|
|
194
|
+
batch1 = schema.generate_many(100, seed: 42)
|
|
195
|
+
batch2 = schema.generate_many(100, seed: 42)
|
|
196
|
+
|
|
197
|
+
batch1 == batch2 # true
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
---
|
|
201
|
+
|
|
202
|
+
## Performance Considerations
|
|
203
|
+
|
|
204
|
+
### RNG Isolation
|
|
205
|
+
|
|
206
|
+
Each generation creates its own Random Number Generator (RNG) instance, so there's no contention on shared RNG state.
|
|
207
|
+
|
|
208
|
+
### Registry Caching
|
|
209
|
+
|
|
210
|
+
The Registry uses mutex-protected caching for parsed schemas:
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
# First load: parses and caches
|
|
214
|
+
registry.load_file("user.dsl")
|
|
215
|
+
|
|
216
|
+
# Subsequent loads: uses cache (fast, thread-safe)
|
|
217
|
+
registry.load_file("user.dsl")
|
|
218
|
+
```
|
|
219
|
+
|
|
220
|
+
### Parallel Batch Generation
|
|
221
|
+
|
|
222
|
+
For maximum throughput, use parallel batches:
|
|
223
|
+
|
|
224
|
+
```ruby
|
|
225
|
+
require 'parallel'
|
|
226
|
+
|
|
227
|
+
# Generate 1 million users across all CPU cores
|
|
228
|
+
results = Parallel.map(1..100, in_processes: 8) do |batch_num|
|
|
229
|
+
seed = batch_num * 10000
|
|
230
|
+
schema.generate_many(10000, seed: seed, registry: registry)
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
all_users = results.flatten
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## Summary
|
|
239
|
+
|
|
240
|
+
1. **Generation is thread-safe** - Call `generate()`, `generate_many()`, `generate_stream()` from any thread
|
|
241
|
+
2. **Registry is thread-safe** - Share a single registry across threads
|
|
242
|
+
3. **Configure once** - Set configuration before multi-threaded use
|
|
243
|
+
4. **Custom functions must be thread-safe** - Avoid shared mutable state
|
|
244
|
+
5. **`shared()` is batch-scoped** - Each `generate_many()` call has independent shared context
|
|
245
|
+
|
|
246
|
+
---
|
|
247
|
+
|
|
248
|
+
## Next Steps
|
|
249
|
+
|
|
250
|
+
- [Streaming Generation](streaming.md) - Memory-efficient generation
|
|
251
|
+
- [Custom Functions](../api/custom_functions.md) - Writing thread-safe custom functions
|
|
252
|
+
- [Configuration](../api/configuration.md) - Global configuration options
|
|
253
|
+
|
|
@@ -0,0 +1,485 @@
|
|
|
1
|
+
# Ruby API Overview
|
|
2
|
+
|
|
3
|
+
Complete reference for the FakeDataDSL Ruby API.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Quick Reference](#quick-reference)
|
|
8
|
+
- [Main Module](#main-module)
|
|
9
|
+
- [Core Classes](#core-classes)
|
|
10
|
+
- [Loading Schemas](#loading-schemas)
|
|
11
|
+
- [Generating Data](#generating-data)
|
|
12
|
+
- [Configuration](#configuration)
|
|
13
|
+
- [Custom Extensions](#custom-extensions)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Quick Reference
|
|
18
|
+
|
|
19
|
+
```ruby
|
|
20
|
+
require 'fake_data_dsl'
|
|
21
|
+
|
|
22
|
+
# Load and generate
|
|
23
|
+
schema = FakeDataDSL.load('schema.dsl')
|
|
24
|
+
data = schema.generate
|
|
25
|
+
data = schema.generate(seed: 12345, mode: :edge)
|
|
26
|
+
many = schema.generate_many(100)
|
|
27
|
+
|
|
28
|
+
# Registry for multiple schemas
|
|
29
|
+
registry = FakeDataDSL::Registry.new
|
|
30
|
+
registry.load_dir('schemas/')
|
|
31
|
+
user = registry.schema('User').generate(registry: registry)
|
|
32
|
+
|
|
33
|
+
# Custom types and functions
|
|
34
|
+
FakeDataDSL.register_type(:custom_type) { |rng, ctx, args, mode| "value" }
|
|
35
|
+
FakeDataDSL.register_function(:compute) { |ctx| ctx['a'] + ctx['b'] }
|
|
36
|
+
|
|
37
|
+
# Configuration
|
|
38
|
+
FakeDataDSL.configure do |config|
|
|
39
|
+
config.default_mode = :random
|
|
40
|
+
config.limits.max_array_size = 1000
|
|
41
|
+
end
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Main Module
|
|
47
|
+
|
|
48
|
+
### FakeDataDSL
|
|
49
|
+
|
|
50
|
+
The main entry point for the gem.
|
|
51
|
+
|
|
52
|
+
```ruby
|
|
53
|
+
# Load schema from file
|
|
54
|
+
schema = FakeDataDSL.load(path)
|
|
55
|
+
|
|
56
|
+
# Parse schema from string
|
|
57
|
+
schema = FakeDataDSL.parse(dsl_string)
|
|
58
|
+
|
|
59
|
+
# Register custom type
|
|
60
|
+
FakeDataDSL.register_type(name, &block)
|
|
61
|
+
FakeDataDSL.register_type(name, class)
|
|
62
|
+
|
|
63
|
+
# Register custom function
|
|
64
|
+
FakeDataDSL.register_function(name, &block)
|
|
65
|
+
|
|
66
|
+
# Register custom behavior
|
|
67
|
+
FakeDataDSL.register_behavior(name, &block)
|
|
68
|
+
|
|
69
|
+
# Configure globally
|
|
70
|
+
FakeDataDSL.configure { |config| ... }
|
|
71
|
+
|
|
72
|
+
# Reset configuration
|
|
73
|
+
FakeDataDSL.reset_configuration!
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Core Classes
|
|
79
|
+
|
|
80
|
+
### Class Overview
|
|
81
|
+
|
|
82
|
+
| Class | Purpose |
|
|
83
|
+
|-------|---------|
|
|
84
|
+
| `Schema` | Represents a parsed schema |
|
|
85
|
+
| `Registry` | Manages multiple schemas |
|
|
86
|
+
| `Generator::Engine` | Core generation logic |
|
|
87
|
+
| `Generator::RNG` | Random number generation |
|
|
88
|
+
| `Generator::Context` | Field generation context |
|
|
89
|
+
| `Configuration` | Global settings |
|
|
90
|
+
| `Limits` | Resource limits |
|
|
91
|
+
|
|
92
|
+
### Schema
|
|
93
|
+
|
|
94
|
+
```ruby
|
|
95
|
+
# Load schema
|
|
96
|
+
schema = FakeDataDSL.load('user.dsl')
|
|
97
|
+
|
|
98
|
+
# Access properties
|
|
99
|
+
schema.name # => "User"
|
|
100
|
+
schema.fields # => [Field, Field, ...]
|
|
101
|
+
schema.behaviors # => [Behavior, ...]
|
|
102
|
+
schema.metadata # => { seed: nil, ... }
|
|
103
|
+
|
|
104
|
+
# Generate data
|
|
105
|
+
schema.generate
|
|
106
|
+
schema.generate(seed: 123)
|
|
107
|
+
schema.generate(mode: :edge)
|
|
108
|
+
schema.generate_many(100)
|
|
109
|
+
schema.generate_stream(count: 1000)
|
|
110
|
+
schema.generate_json
|
|
111
|
+
schema.generate_json(pretty: true)
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Registry
|
|
115
|
+
|
|
116
|
+
```ruby
|
|
117
|
+
# Create registry
|
|
118
|
+
registry = FakeDataDSL::Registry.new
|
|
119
|
+
|
|
120
|
+
# Load schemas
|
|
121
|
+
registry.load_file('user.dsl')
|
|
122
|
+
registry.load_dir('schemas/')
|
|
123
|
+
registry.load_string(dsl_content, name: 'inline')
|
|
124
|
+
|
|
125
|
+
# Access schemas
|
|
126
|
+
schema = registry.schema('User')
|
|
127
|
+
schemas = registry.schemas # => { 'User' => Schema, ... }
|
|
128
|
+
registry.schema?('User') # => true
|
|
129
|
+
|
|
130
|
+
# Generate with registry
|
|
131
|
+
registry.schema('Order').generate(registry: registry)
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Generator::Engine
|
|
135
|
+
|
|
136
|
+
```ruby
|
|
137
|
+
# Usually not used directly - Schema wraps this
|
|
138
|
+
|
|
139
|
+
engine = FakeDataDSL::Generator::Engine.new(
|
|
140
|
+
schema: schema,
|
|
141
|
+
registry: registry,
|
|
142
|
+
seed: 12345,
|
|
143
|
+
mode: :random
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
data = engine.generate
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
### Generator::RNG
|
|
150
|
+
|
|
151
|
+
```ruby
|
|
152
|
+
# Seedable random number generator
|
|
153
|
+
rng = FakeDataDSL::Generator::RNG.new(seed: 12345)
|
|
154
|
+
|
|
155
|
+
rng.int(1, 100) # => 42
|
|
156
|
+
rng.float(0.0, 1.0) # => 0.7234
|
|
157
|
+
rng.bool # => true
|
|
158
|
+
rng.sample([1, 2, 3]) # => 2
|
|
159
|
+
rng.shuffle([1, 2, 3]) # => [3, 1, 2]
|
|
160
|
+
rng.string(10) # => "aBcDeFgHiJ"
|
|
161
|
+
rng.hex(16) # => "a1b2c3d4..."
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
---
|
|
165
|
+
|
|
166
|
+
## Loading Schemas
|
|
167
|
+
|
|
168
|
+
### From File
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
# Load single file
|
|
172
|
+
schema = FakeDataDSL.load('schemas/user.dsl')
|
|
173
|
+
|
|
174
|
+
# File with multiple schemas returns first
|
|
175
|
+
schema = FakeDataDSL.load('schemas/all.dsl') # Returns first schema
|
|
176
|
+
|
|
177
|
+
# Use registry for multiple schemas
|
|
178
|
+
registry = FakeDataDSL::Registry.new
|
|
179
|
+
registry.load_file('schemas/all.dsl')
|
|
180
|
+
user = registry.schema('User')
|
|
181
|
+
order = registry.schema('Order')
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
### From String
|
|
185
|
+
|
|
186
|
+
```ruby
|
|
187
|
+
dsl = <<~DSL
|
|
188
|
+
User:
|
|
189
|
+
id: uuid
|
|
190
|
+
name: name
|
|
191
|
+
DSL
|
|
192
|
+
|
|
193
|
+
schema = FakeDataDSL.parse(dsl)
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### From Directory
|
|
197
|
+
|
|
198
|
+
```ruby
|
|
199
|
+
registry = FakeDataDSL::Registry.new
|
|
200
|
+
registry.load_dir('schemas/') # Loads all .dsl files
|
|
201
|
+
|
|
202
|
+
# Recursive loading
|
|
203
|
+
registry.load_dir('schemas/', recursive: true)
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
---
|
|
207
|
+
|
|
208
|
+
## Generating Data
|
|
209
|
+
|
|
210
|
+
### Single Record
|
|
211
|
+
|
|
212
|
+
```ruby
|
|
213
|
+
# Basic generation
|
|
214
|
+
user = schema.generate
|
|
215
|
+
|
|
216
|
+
# With options
|
|
217
|
+
user = schema.generate(
|
|
218
|
+
seed: 12345, # Reproducible output
|
|
219
|
+
mode: :random, # Generation mode
|
|
220
|
+
registry: registry, # For cross-references
|
|
221
|
+
overrides: { # Override specific fields
|
|
222
|
+
'status' => 'active'
|
|
223
|
+
}
|
|
224
|
+
)
|
|
225
|
+
```
|
|
226
|
+
|
|
227
|
+
### Multiple Records
|
|
228
|
+
|
|
229
|
+
```ruby
|
|
230
|
+
# Generate array
|
|
231
|
+
users = schema.generate_many(100)
|
|
232
|
+
|
|
233
|
+
# With options
|
|
234
|
+
users = schema.generate_many(100,
|
|
235
|
+
seed: 12345,
|
|
236
|
+
mode: :edge
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
# Each record has different data
|
|
240
|
+
users.map { |u| u['id'] }.uniq.size # => 100
|
|
241
|
+
```
|
|
242
|
+
|
|
243
|
+
### Streaming
|
|
244
|
+
|
|
245
|
+
```ruby
|
|
246
|
+
# Memory-efficient for large batches
|
|
247
|
+
schema.generate_stream(count: 1_000_000).each do |record|
|
|
248
|
+
database.insert(record)
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# With options
|
|
252
|
+
schema.generate_stream(
|
|
253
|
+
count: 1_000_000,
|
|
254
|
+
seed: 12345,
|
|
255
|
+
mode: :random
|
|
256
|
+
).each do |record|
|
|
257
|
+
process(record)
|
|
258
|
+
end
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
### JSON Output
|
|
262
|
+
|
|
263
|
+
```ruby
|
|
264
|
+
# Compact JSON
|
|
265
|
+
json = schema.generate_json
|
|
266
|
+
|
|
267
|
+
# Pretty printed
|
|
268
|
+
json = schema.generate_json(pretty: true)
|
|
269
|
+
|
|
270
|
+
# Multiple records
|
|
271
|
+
json = schema.generate_many_json(100)
|
|
272
|
+
json = schema.generate_many_json(100, pretty: true)
|
|
273
|
+
```
|
|
274
|
+
|
|
275
|
+
### Generation Modes
|
|
276
|
+
|
|
277
|
+
```ruby
|
|
278
|
+
# Random - typical realistic data (default)
|
|
279
|
+
schema.generate(mode: :random)
|
|
280
|
+
|
|
281
|
+
# Edge - boundary values
|
|
282
|
+
schema.generate(mode: :edge)
|
|
283
|
+
|
|
284
|
+
# Invalid - invalid data for validation testing
|
|
285
|
+
schema.generate(mode: :invalid)
|
|
286
|
+
|
|
287
|
+
# Mixed - combination of modes
|
|
288
|
+
schema.generate(mode: :mixed)
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
---
|
|
292
|
+
|
|
293
|
+
## Configuration
|
|
294
|
+
|
|
295
|
+
### Global Configuration
|
|
296
|
+
|
|
297
|
+
```ruby
|
|
298
|
+
FakeDataDSL.configure do |config|
|
|
299
|
+
# Default generation mode
|
|
300
|
+
config.default_mode = :random
|
|
301
|
+
|
|
302
|
+
# Maximum retries for unique values
|
|
303
|
+
config.max_unique_retries = 1000
|
|
304
|
+
|
|
305
|
+
# Enable/disable behaviors
|
|
306
|
+
config.behaviors_enabled = true
|
|
307
|
+
|
|
308
|
+
# Registry cache size
|
|
309
|
+
config.registry_cache_size = 100
|
|
310
|
+
end
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
### Resource Limits
|
|
314
|
+
|
|
315
|
+
```ruby
|
|
316
|
+
FakeDataDSL.configure do |config|
|
|
317
|
+
# Maximum latency from @latency behavior
|
|
318
|
+
config.limits.max_latency_ms = 10_000
|
|
319
|
+
|
|
320
|
+
# Maximum recursion depth
|
|
321
|
+
config.limits.max_recursion = 10
|
|
322
|
+
|
|
323
|
+
# Maximum array size
|
|
324
|
+
config.limits.max_array_size = 1000
|
|
325
|
+
|
|
326
|
+
# Maximum text length
|
|
327
|
+
config.limits.max_text_length = 10_000
|
|
328
|
+
end
|
|
329
|
+
```
|
|
330
|
+
|
|
331
|
+
### Accessing Configuration
|
|
332
|
+
|
|
333
|
+
```ruby
|
|
334
|
+
config = FakeDataDSL.configuration
|
|
335
|
+
|
|
336
|
+
config.default_mode # => :random
|
|
337
|
+
config.limits.max_array_size # => 1000
|
|
338
|
+
```
|
|
339
|
+
|
|
340
|
+
### Reset
|
|
341
|
+
|
|
342
|
+
```ruby
|
|
343
|
+
FakeDataDSL.reset_configuration!
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
---
|
|
347
|
+
|
|
348
|
+
## Custom Extensions
|
|
349
|
+
|
|
350
|
+
### Custom Types
|
|
351
|
+
|
|
352
|
+
```ruby
|
|
353
|
+
# Block-based
|
|
354
|
+
FakeDataDSL.register_type(:ssn) do |rng, context, args, mode|
|
|
355
|
+
case mode
|
|
356
|
+
when :random
|
|
357
|
+
"#{rng.int(100, 999)}-#{rng.int(10, 99)}-#{rng.int(1000, 9999)}"
|
|
358
|
+
when :edge
|
|
359
|
+
"000-00-0000"
|
|
360
|
+
when :invalid
|
|
361
|
+
"invalid-ssn"
|
|
362
|
+
end
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
# Class-based
|
|
366
|
+
class SSN < FakeDataDSL::Types::Base
|
|
367
|
+
def generate_random(rng, args)
|
|
368
|
+
"#{rng.int(100, 999)}-#{rng.int(10, 99)}-#{rng.int(1000, 9999)}"
|
|
369
|
+
end
|
|
370
|
+
|
|
371
|
+
def generate_edge(rng, args)
|
|
372
|
+
"000-00-0000"
|
|
373
|
+
end
|
|
374
|
+
|
|
375
|
+
def generate_invalid(rng, args)
|
|
376
|
+
"invalid-ssn"
|
|
377
|
+
end
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
FakeDataDSL.register_type(:ssn, SSN)
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Custom Functions
|
|
384
|
+
|
|
385
|
+
```ruby
|
|
386
|
+
# Simple computed field
|
|
387
|
+
FakeDataDSL.register_function(:full_name) do |context|
|
|
388
|
+
"#{context['first_name']} #{context['last_name']}"
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
# Complex logic
|
|
392
|
+
FakeDataDSL.register_function(:order_total) do |context|
|
|
393
|
+
items = context[:items] || []
|
|
394
|
+
subtotal = items.sum { |i| i['price'] * i['quantity'] }
|
|
395
|
+
tax = subtotal * 0.08
|
|
396
|
+
shipping = subtotal > 100 ? 0 : 9.99
|
|
397
|
+
(subtotal + tax + shipping).round(2)
|
|
398
|
+
end
|
|
399
|
+
```
|
|
400
|
+
|
|
401
|
+
### Custom Behaviors
|
|
402
|
+
|
|
403
|
+
```ruby
|
|
404
|
+
# Block-based
|
|
405
|
+
FakeDataDSL.register_behavior(:log) do |result, context, value|
|
|
406
|
+
puts "[DEBUG] Generated: #{result.inspect}"
|
|
407
|
+
result
|
|
408
|
+
end
|
|
409
|
+
|
|
410
|
+
# Class-based
|
|
411
|
+
class AuditBehavior < FakeDataDSL::Behaviors::Base
|
|
412
|
+
def apply(result, context, value)
|
|
413
|
+
log_generation(result, context)
|
|
414
|
+
result
|
|
415
|
+
end
|
|
416
|
+
|
|
417
|
+
private
|
|
418
|
+
|
|
419
|
+
def log_generation(result, context)
|
|
420
|
+
# Custom logging logic
|
|
421
|
+
end
|
|
422
|
+
end
|
|
423
|
+
|
|
424
|
+
FakeDataDSL.register_behavior(:audit, AuditBehavior)
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
---
|
|
428
|
+
|
|
429
|
+
## Error Handling
|
|
430
|
+
|
|
431
|
+
### Error Types
|
|
432
|
+
|
|
433
|
+
```ruby
|
|
434
|
+
begin
|
|
435
|
+
data = schema.generate
|
|
436
|
+
rescue FakeDataDSL::Errors::ParseError => e
|
|
437
|
+
# DSL syntax error
|
|
438
|
+
puts "Parse error: #{e.message}"
|
|
439
|
+
puts "Line #{e.line}, column #{e.column}"
|
|
440
|
+
|
|
441
|
+
rescue FakeDataDSL::Errors::UnknownTypeError => e
|
|
442
|
+
# Unknown type in schema
|
|
443
|
+
puts "Unknown type: #{e.type_name}"
|
|
444
|
+
|
|
445
|
+
rescue FakeDataDSL::Errors::UniquenessError => e
|
|
446
|
+
# Couldn't generate unique value
|
|
447
|
+
puts "Uniqueness failed for: #{e.field_name}"
|
|
448
|
+
|
|
449
|
+
rescue FakeDataDSL::Errors::CyclicReferenceError => e
|
|
450
|
+
# Circular schema reference
|
|
451
|
+
puts "Cycle detected: #{e.cycle_path}"
|
|
452
|
+
|
|
453
|
+
rescue FakeDataDSL::Errors::SimulatedFailure
|
|
454
|
+
# @failure behavior triggered
|
|
455
|
+
|
|
456
|
+
rescue FakeDataDSL::Errors::SimulatedError => e
|
|
457
|
+
# @simulate_error behavior triggered
|
|
458
|
+
puts "HTTP #{e.status_code}"
|
|
459
|
+
end
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
### Error Codes
|
|
463
|
+
|
|
464
|
+
```ruby
|
|
465
|
+
# Each error has a code for programmatic handling
|
|
466
|
+
rescue FakeDataDSL::Errors::ParseError => e
|
|
467
|
+
case e.class::ERROR_CODE
|
|
468
|
+
when "SYNTAX_ERROR"
|
|
469
|
+
# Handle syntax error
|
|
470
|
+
when "UNKNOWN_TYPE_ERROR"
|
|
471
|
+
# Handle unknown type
|
|
472
|
+
end
|
|
473
|
+
end
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
## Next Steps
|
|
479
|
+
|
|
480
|
+
- [Schema Class](schema.md) - Detailed Schema API
|
|
481
|
+
- [Registry Class](registry.md) - Multi-schema management
|
|
482
|
+
- [Custom Types](custom_types.md) - Creating type generators
|
|
483
|
+
- [Custom Functions](custom_functions.md) - Computed fields
|
|
484
|
+
- [Error Handling](error_handling.md) - Complete error reference
|
|
485
|
+
|