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.
Files changed (302) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +40 -0
  4. data/.yardopts +18 -0
  5. data/ACCESS_DOCS.md +116 -0
  6. data/ADVANCED_FEATURES_SUMMARY.md +245 -0
  7. data/CHANGELOG.md +498 -0
  8. data/CODE_OF_CONDUCT.md +122 -0
  9. data/CONTRIBUTING.md +307 -0
  10. data/DOCUMENTATION.md +204 -0
  11. data/DOCUMENTATION_GUIDE.md +194 -0
  12. data/LICENSE +22 -0
  13. data/README.md +1458 -0
  14. data/Rakefile +128 -0
  15. data/benchmark/README.md +591 -0
  16. data/benchmark/batch_generation.rb +63 -0
  17. data/benchmark/engine_comparison.rb +295 -0
  18. data/benchmark/single_record.rb +108 -0
  19. data/benchmark/streaming.rb +74 -0
  20. data/docs/ACTIVERECORD_INFERENCE.md +524 -0
  21. data/docs/API_SERVER.md +278 -0
  22. data/docs/CONFIG_FILE.md +315 -0
  23. data/docs/DATA_CONTRACTS.md +312 -0
  24. data/docs/ENHANCED_REPL.md +304 -0
  25. data/docs/FACTORY_BOT.md +271 -0
  26. data/docs/GITHUB_ACTION.md +420 -0
  27. data/docs/GRAPHQL_EXPORT.md +303 -0
  28. data/docs/GRAPHQL_FEDERATION.md +651 -0
  29. data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
  30. data/docs/LSP.md +189 -0
  31. data/docs/MIGRATION_GENERATOR.md +547 -0
  32. data/docs/MOCK_SERVER.md +280 -0
  33. data/docs/NATIVE_ENGINE.md +298 -0
  34. data/docs/OPENAPI_EXPORT.md +252 -0
  35. data/docs/PERFORMANCE_MODE.md +228 -0
  36. data/docs/PERSONAS.md +547 -0
  37. data/docs/PROPERTY_TESTING.md +284 -0
  38. data/docs/PROTOBUF_EXPORT.md +276 -0
  39. data/docs/QUALITY_METRICS.md +555 -0
  40. data/docs/QUICK_REFERENCE.md +88 -0
  41. data/docs/RAILS_ENGINE.md +404 -0
  42. data/docs/RAILS_INTEGRATION.md +454 -0
  43. data/docs/README.md +175 -0
  44. data/docs/REPL_QUICK_REFERENCE.md +149 -0
  45. data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
  46. data/docs/SCENARIOS.md +576 -0
  47. data/docs/SECURITY_TESTING.md +318 -0
  48. data/docs/SNAPSHOT_TESTING.md +308 -0
  49. data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
  50. data/docs/TERRAFORM_EXPORT.md +447 -0
  51. data/docs/TIME_TRAVEL.md +507 -0
  52. data/docs/USAGE_GUIDE.md +980 -0
  53. data/docs/WEBHOOK_SIMULATOR.md +579 -0
  54. data/docs/advanced_features.md +183 -0
  55. data/docs/api_reference.md +153 -0
  56. data/docs/behaviors.md +138 -0
  57. data/docs/best_practices.md +191 -0
  58. data/docs/dsl_reference.md +172 -0
  59. data/docs/examples.md +162 -0
  60. data/docs/generation_modes.md +125 -0
  61. data/docs/getting_started.md +92 -0
  62. data/docs/index.html +177 -0
  63. data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
  64. data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
  65. data/docs/troubleshooting.md +215 -0
  66. data/docs/type_reference.md +224 -0
  67. data/examples/hostile_mode_demo.rb +64 -0
  68. data/exe/synthra +6 -0
  69. data/ext/synthra_native/Cargo.lock +972 -0
  70. data/ext/synthra_native/Cargo.toml +46 -0
  71. data/ext/synthra_native/extconf.rb +41 -0
  72. data/ext/synthra_native/src/generator.rs +397 -0
  73. data/ext/synthra_native/src/lib.rs +229 -0
  74. data/ext/synthra_native/src/types.rs +1485 -0
  75. data/lib/generators/synthra/install_generator.rb +86 -0
  76. data/lib/generators/synthra/templates/api_response.dsl +15 -0
  77. data/lib/generators/synthra/templates/fake_data.rake +116 -0
  78. data/lib/generators/synthra/templates/synthra.yml +31 -0
  79. data/lib/generators/synthra/templates/synthra_support.rb +19 -0
  80. data/lib/generators/synthra/templates/user.dsl +11 -0
  81. data/lib/synthra/activerecord_inference.rb +394 -0
  82. data/lib/synthra/api.rb +381 -0
  83. data/lib/synthra/api_server.rb +718 -0
  84. data/lib/synthra/behaviors/applicator.rb +165 -0
  85. data/lib/synthra/behaviors/base.rb +155 -0
  86. data/lib/synthra/behaviors/close_connection.rb +60 -0
  87. data/lib/synthra/behaviors/deprecated.rb +100 -0
  88. data/lib/synthra/behaviors/failure.rb +69 -0
  89. data/lib/synthra/behaviors/latency.rb +103 -0
  90. data/lib/synthra/behaviors/partial_data.rb +83 -0
  91. data/lib/synthra/behaviors/randomize_order.rb +74 -0
  92. data/lib/synthra/behaviors/registry.rb +238 -0
  93. data/lib/synthra/behaviors/simulate_error.rb +79 -0
  94. data/lib/synthra/cli/commands/base.rb +105 -0
  95. data/lib/synthra/cli/commands/diff.rb +227 -0
  96. data/lib/synthra/cli/commands/docs.rb +67 -0
  97. data/lib/synthra/cli/commands/export.rb +150 -0
  98. data/lib/synthra/cli/commands/generate.rb +72 -0
  99. data/lib/synthra/cli/commands/import.rb +80 -0
  100. data/lib/synthra/cli/commands/lint.rb +53 -0
  101. data/lib/synthra/cli/commands/live.rb +57 -0
  102. data/lib/synthra/cli/commands/seed.rb +99 -0
  103. data/lib/synthra/cli/commands/validate.rb +28 -0
  104. data/lib/synthra/cli.rb +2471 -0
  105. data/lib/synthra/config_file.rb +129 -0
  106. data/lib/synthra/configuration.rb +281 -0
  107. data/lib/synthra/contracts_registry.rb +408 -0
  108. data/lib/synthra/database_seeder.rb +268 -0
  109. data/lib/synthra/deterministic_ids.rb +218 -0
  110. data/lib/synthra/documentation_generator.rb +414 -0
  111. data/lib/synthra/engine.rb +251 -0
  112. data/lib/synthra/errors.rb +1169 -0
  113. data/lib/synthra/export/base.rb +85 -0
  114. data/lib/synthra/export/csv.rb +101 -0
  115. data/lib/synthra/export/graphql.rb +266 -0
  116. data/lib/synthra/export/graphql_federation.rb +377 -0
  117. data/lib/synthra/export/graphviz.rb +258 -0
  118. data/lib/synthra/export/javascript.rb +327 -0
  119. data/lib/synthra/export/json_data.rb +61 -0
  120. data/lib/synthra/export/json_schema.rb +290 -0
  121. data/lib/synthra/export/openapi.rb +514 -0
  122. data/lib/synthra/export/protobuf.rb +483 -0
  123. data/lib/synthra/export/python.rb +560 -0
  124. data/lib/synthra/export/sql.rb +381 -0
  125. data/lib/synthra/export/sql_insert.rb +152 -0
  126. data/lib/synthra/export/terraform.rb +501 -0
  127. data/lib/synthra/export/type_mapping.rb +389 -0
  128. data/lib/synthra/export/typescript.rb +300 -0
  129. data/lib/synthra/export/xml_data.rb +104 -0
  130. data/lib/synthra/export/yaml_data.rb +56 -0
  131. data/lib/synthra/export.rb +404 -0
  132. data/lib/synthra/factory_bot_integration.rb +157 -0
  133. data/lib/synthra/field.rb +440 -0
  134. data/lib/synthra/functions/registry.rb +104 -0
  135. data/lib/synthra/generator/context.rb +372 -0
  136. data/lib/synthra/generator/engine.rb +336 -0
  137. data/lib/synthra/generator/faker_adapter.rb +425 -0
  138. data/lib/synthra/generator/modes.rb +444 -0
  139. data/lib/synthra/generator/resolver.rb +256 -0
  140. data/lib/synthra/generator/rng.rb +273 -0
  141. data/lib/synthra/generator/streamer.rb +63 -0
  142. data/lib/synthra/generator/uniqueness.rb +118 -0
  143. data/lib/synthra/initializer.rb +97 -0
  144. data/lib/synthra/limits.rb +279 -0
  145. data/lib/synthra/live_preview.rb +518 -0
  146. data/lib/synthra/loader_config.rb +78 -0
  147. data/lib/synthra/lsp/server.rb +689 -0
  148. data/lib/synthra/migration_generator.rb +301 -0
  149. data/lib/synthra/mixin.rb +174 -0
  150. data/lib/synthra/mock_server.rb +458 -0
  151. data/lib/synthra/native_engine.rb +304 -0
  152. data/lib/synthra/openapi_importer.rb +228 -0
  153. data/lib/synthra/output/json_formatter.rb +40 -0
  154. data/lib/synthra/output/ndjson_formatter.rb +55 -0
  155. data/lib/synthra/parser/ast.rb +1287 -0
  156. data/lib/synthra/parser/lexer.rb +1152 -0
  157. data/lib/synthra/parser/parser.rb +1664 -0
  158. data/lib/synthra/parser/tokens.rb +461 -0
  159. data/lib/synthra/performance_mode.rb +364 -0
  160. data/lib/synthra/personas.rb +397 -0
  161. data/lib/synthra/property_testing.rb +172 -0
  162. data/lib/synthra/quality_metrics.rb +405 -0
  163. data/lib/synthra/rails_test_helper.rb +248 -0
  164. data/lib/synthra/registry.rb +533 -0
  165. data/lib/synthra/relationships.rb +193 -0
  166. data/lib/synthra/repl/enhanced_repl.rb +607 -0
  167. data/lib/synthra/repl/formatter.rb +191 -0
  168. data/lib/synthra/scenarios.rb +423 -0
  169. data/lib/synthra/schema.rb +605 -0
  170. data/lib/synthra/schema_inheritance.rb +104 -0
  171. data/lib/synthra/schema_versioning.rb +212 -0
  172. data/lib/synthra/snapshot_testing.rb +199 -0
  173. data/lib/synthra/time_travel.rb +338 -0
  174. data/lib/synthra/type_definitions.rb +274 -0
  175. data/lib/synthra/types/address_location/addresses.rb +125 -0
  176. data/lib/synthra/types/address_location/airports.rb +200 -0
  177. data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
  178. data/lib/synthra/types/address_location/locations.rb +406 -0
  179. data/lib/synthra/types/base.rb +48 -0
  180. data/lib/synthra/types/commerce_products/commerce.rb +119 -0
  181. data/lib/synthra/types/commerce_products/companies.rb +71 -0
  182. data/lib/synthra/types/commerce_products/construction.rb +126 -0
  183. data/lib/synthra/types/commerce_products/products.rb +205 -0
  184. data/lib/synthra/types/core/collections.rb +513 -0
  185. data/lib/synthra/types/core/defaults.rb +76 -0
  186. data/lib/synthra/types/core/enums.rb +102 -0
  187. data/lib/synthra/types/core/identifiers.rb +445 -0
  188. data/lib/synthra/types/core/primitives.rb +586 -0
  189. data/lib/synthra/types/core/references.rb +466 -0
  190. data/lib/synthra/types/core/sequences.rb +304 -0
  191. data/lib/synthra/types/crypto/crypto.rb +162 -0
  192. data/lib/synthra/types/date_time/dates.rb +387 -0
  193. data/lib/synthra/types/finance_banking/banking.rb +424 -0
  194. data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
  195. data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
  196. data/lib/synthra/types/formula.rb +184 -0
  197. data/lib/synthra/types/health_medical/medical.rb +150 -0
  198. data/lib/synthra/types/hostile_payloads.rb +122 -0
  199. data/lib/synthra/types/json_array.rb +51 -0
  200. data/lib/synthra/types/media_entertainment/media.rb +62 -0
  201. data/lib/synthra/types/naughty_string.rb +30 -0
  202. data/lib/synthra/types/personal_names/chinese.rb +41 -0
  203. data/lib/synthra/types/personal_names/identifiers.rb +187 -0
  204. data/lib/synthra/types/personal_names/names.rb +246 -0
  205. data/lib/synthra/types/personal_names/national_id.rb +89 -0
  206. data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
  207. data/lib/synthra/types/regex.rb +248 -0
  208. data/lib/synthra/types/registry.rb +182 -0
  209. data/lib/synthra/types/repeating_element.rb +50 -0
  210. data/lib/synthra/types/scenario.rb +29 -0
  211. data/lib/synthra/types/technology_internet/apps.rb +67 -0
  212. data/lib/synthra/types/technology_internet/communication.rb +162 -0
  213. data/lib/synthra/types/technology_internet/devices.rb +80 -0
  214. data/lib/synthra/types/technology_internet/formats.rb +139 -0
  215. data/lib/synthra/types/technology_internet/networking.rb +143 -0
  216. data/lib/synthra/types/template.rb +92 -0
  217. data/lib/synthra/types/text_content/business.rb +128 -0
  218. data/lib/synthra/types/text_content/colors.rb +70 -0
  219. data/lib/synthra/types/text_content/misc.rb +237 -0
  220. data/lib/synthra/types/text_content/security.rb +130 -0
  221. data/lib/synthra/types/text_content/text_generation.rb +532 -0
  222. data/lib/synthra/types/travel/travel.rb +150 -0
  223. data/lib/synthra/utils/string_distance.rb +133 -0
  224. data/lib/synthra/validator/dsl_validator.rb +339 -0
  225. data/lib/synthra/validator/path_validator.rb +615 -0
  226. data/lib/synthra/version.rb +35 -0
  227. data/lib/synthra/webhook_simulator.rb +341 -0
  228. data/lib/synthra.rb +259 -0
  229. data/schemas/address.dsl +16 -0
  230. data/schemas/api_response.dsl +8 -0
  231. data/schemas/error_payload.dsl +9 -0
  232. data/schemas/order.dsl +10 -0
  233. data/schemas/order_item.dsl +8 -0
  234. data/schemas/payment.dsl +11 -0
  235. data/schemas/social_post.dsl +13 -0
  236. data/schemas/user.dsl +10 -0
  237. data/scripts/batch_fix_all.rb +96 -0
  238. data/scripts/delete_old_files.rb +32 -0
  239. data/scripts/fix_all_grouped_files.rb +137 -0
  240. data/scripts/fix_all_indentation.rb +48 -0
  241. data/scripts/fix_all_syntax.rb +124 -0
  242. data/scripts/fix_grouped_files.rb +184 -0
  243. data/scripts/fix_syntax_errors.rb +108 -0
  244. data/scripts/group_domain_types.rb +112 -0
  245. data/scripts/group_domain_types_fixed.rb +150 -0
  246. data/scripts/merge_domains_to_one_file.rb +68 -0
  247. data/scripts/move_existing_types.rb +130 -0
  248. data/scripts/split_grouped_types.rb +142 -0
  249. data/tech_docs/README.md +134 -0
  250. data/tech_docs/advanced/streaming.md +405 -0
  251. data/tech_docs/advanced/thread_safety.md +253 -0
  252. data/tech_docs/api/overview.md +485 -0
  253. data/tech_docs/appendices/type_chart.md +193 -0
  254. data/tech_docs/basic_concepts.md +383 -0
  255. data/tech_docs/behaviors/overview.md +346 -0
  256. data/tech_docs/dsl/complex_types.md +790 -0
  257. data/tech_docs/dsl/core_types.md +464 -0
  258. data/tech_docs/dsl/datetime_types.md +325 -0
  259. data/tech_docs/dsl/field_modifiers.md +414 -0
  260. data/tech_docs/dsl/grammar.md +431 -0
  261. data/tech_docs/dsl/schema_definition.md +399 -0
  262. data/tech_docs/export/README.md +276 -0
  263. data/tech_docs/installation.md +273 -0
  264. data/tech_docs/integration/ci_cd.md +707 -0
  265. data/tech_docs/integration/ci_cd_guide.md +579 -0
  266. data/tech_docs/integration/factory_bot.md +485 -0
  267. data/tech_docs/integration/rails.md +630 -0
  268. data/tech_docs/integration/rspec.md +449 -0
  269. data/tech_docs/modes/overview.md +350 -0
  270. data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
  271. data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
  272. data/tech_docs/quick_start.md +256 -0
  273. data/tech_docs/templates/README.md +805 -0
  274. data/tech_docs/tutorials/advanced.md +371 -0
  275. data/tech_docs/tutorials/getting_started.md +189 -0
  276. data/tech_docs/tutorials/intermediate.md +231 -0
  277. data/tech_docs/tutorials/template_gallery.md +569 -0
  278. data/tech_example/01_basic_usage.rb +238 -0
  279. data/tech_example/02_types_demo.rb +336 -0
  280. data/tech_example/04_cli_usage.md +429 -0
  281. data/tech_example/05_database_seeding.rb +283 -0
  282. data/tech_example/07_rspec_integration.rb +359 -0
  283. data/tech_example/10_custom_types.rb +387 -0
  284. data/tech_example/12_behaviors.rb +372 -0
  285. data/tech_example/13_twitter_dm_example.rb +120 -0
  286. data/tech_example/14_exact_json_structure.rb +253 -0
  287. data/tech_example/16_lsp_server.rb +56 -0
  288. data/tech_example/17_property_testing.rb +92 -0
  289. data/tech_example/18_enhanced_repl.rb +120 -0
  290. data/tech_example/19_security_fuzzing.rb +145 -0
  291. data/tech_example/NEW_FEATURES.md +169 -0
  292. data/tech_example/README.md +118 -0
  293. data/tech_example/schemas/api_response.dsl +123 -0
  294. data/tech_example/schemas/ecommerce.dsl +121 -0
  295. data/tech_example/schemas/twitter_dm.dsl +109 -0
  296. data/tech_example/schemas/user.dsl +36 -0
  297. data/vscode-extension/README.md +246 -0
  298. data/vscode-extension/language-configuration.json +31 -0
  299. data/vscode-extension/package.json +55 -0
  300. data/vscode-extension/snippets/fakedatadsl.json +198 -0
  301. data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
  302. 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
+