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,790 @@
1
+ # Complex Types Reference
2
+
3
+ Arrays, objects, references, cross-schema linking, and advanced data generation.
4
+
5
+ ## Table of Contents
6
+
7
+ - [Array Type](#array-type)
8
+ - [Object Type](#object-type)
9
+ - [Reference Type](#reference-type)
10
+ - [Copy Type](#copy-type)
11
+ - [Shared Type](#shared-type)
12
+ - [Map By Field Type](#map-by-field-type)
13
+ - [One Of Type](#one-of-type)
14
+ - [Const Type](#const-type)
15
+ - [Custom Type](#custom-type)
16
+ - [Sequence Types](#sequence-types)
17
+
18
+ ---
19
+
20
+ ## Array Type
21
+
22
+ ### Basic Arrays
23
+
24
+ ```dsl
25
+ Order:
26
+ # Array of strings
27
+ tags: array(text, 1..5)
28
+
29
+ # Array of numbers
30
+ scores: array(number(1..100), 5)
31
+
32
+ # Array of schema references
33
+ items: array(OrderItem, 1..10)
34
+ ```
35
+
36
+ ### Syntax Options
37
+
38
+ ```dsl
39
+ # Positional arguments: element type, size
40
+ tags: array(text, 3) # Exactly 3 elements
41
+ tags: array(text, 1..5) # 1 to 5 elements
42
+
43
+ # Named arguments
44
+ tags: array(element: text, size: 3)
45
+ tags: array(element: text, size: 1..5)
46
+
47
+ # Complex element types
48
+ addresses: array(Address, 1..3)
49
+ emails: array(email, 0..5)
50
+ prices: array(number(0.99..99.99), 1..10)
51
+ ```
52
+
53
+ ### Array Arguments
54
+
55
+ | Argument | Type | Description |
56
+ |----------|------|-------------|
57
+ | `element` | Type | Element type or schema reference |
58
+ | `size` | Integer or Range | Number of elements |
59
+ | `unique` | Boolean | Ensure unique elements |
60
+ | `min` | Integer | Minimum size (alternative to range) |
61
+ | `max` | Integer | Maximum size (alternative to range) |
62
+
63
+ ### Array Examples
64
+
65
+ ```dsl
66
+ Product:
67
+ # 1-5 tags, each 3-20 characters
68
+ tags: array(text(3..20), 1..5)
69
+
70
+ # Exactly 3 images
71
+ images: array(url, 3)
72
+
73
+ # 0 or more reviews (optional array)
74
+ reviews: array(Review, 0..10)
75
+
76
+ # Unique categories
77
+ categories: array(enum(tech, food, fashion), size: 1..3, unique: true)
78
+
79
+ Review:
80
+ rating: number(1..5)
81
+ comment: text(10..500)
82
+ author: name
83
+ ```
84
+
85
+ ### Empty Arrays
86
+
87
+ ```dsl
88
+ # May be empty (0 elements)
89
+ optional_items: array(Item, 0..5)
90
+
91
+ # Always has at least one
92
+ required_items: array(Item, 1..5)
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Object Type
98
+
99
+ ### Inline Objects
100
+
101
+ For simple, one-off nested structures:
102
+
103
+ ```dsl
104
+ User:
105
+ metadata: object
106
+ ```
107
+
108
+ Generates a generic object with random keys/values.
109
+
110
+ ### Schema References
111
+
112
+ For structured nested objects, reference another schema:
113
+
114
+ ```dsl
115
+ User:
116
+ id: uuid
117
+ name: name
118
+ address: Address # Embeds full Address schema
119
+
120
+ Address:
121
+ street: text(10..100)
122
+ city: name
123
+ country: country_code
124
+ postal_code: postal_code
125
+ ```
126
+
127
+ ### Nested Schema Example
128
+
129
+ ```ruby
130
+ schema = FakeDataDSL.load('user.dsl')
131
+ user = schema.generate
132
+
133
+ # Result:
134
+ # {
135
+ # "id" => "550e8400-...",
136
+ # "name" => "John Smith",
137
+ # "address" => {
138
+ # "street" => "123 Main Street",
139
+ # "city" => "Springfield",
140
+ # "country" => "US",
141
+ # "postal_code" => "12345"
142
+ # }
143
+ # }
144
+ ```
145
+
146
+ ---
147
+
148
+ ## Reference Type
149
+
150
+ ### What is a Reference?
151
+
152
+ References let you link schemas together without embedding the full object.
153
+
154
+ ### Ref() Syntax
155
+
156
+ ```dsl
157
+ # Reference a specific field from another schema
158
+ Order:
159
+ id: uuid
160
+ user_id: Ref(User.id) # Gets a User id
161
+ shipping_address_id: Ref(Address.id) # Gets an Address id
162
+
163
+ User:
164
+ id: uuid
165
+ name: name
166
+
167
+ Address:
168
+ id: uuid
169
+ street: text
170
+ ```
171
+
172
+ ### How References Work
173
+
174
+ 1. **Same Batch**: References resolve to values generated in the same batch
175
+ 2. **Registry Required**: Cross-schema references need a Registry
176
+
177
+ ```ruby
178
+ registry = FakeDataDSL::Registry.new
179
+ registry.load_file('schemas/user.dsl')
180
+ registry.load_file('schemas/order.dsl')
181
+
182
+ # Generate with registry for reference resolution
183
+ order = registry.schema('Order').generate(registry: registry)
184
+ # order["user_id"] will be a valid UUID
185
+ ```
186
+
187
+ ### Reference Examples
188
+
189
+ ```dsl
190
+ # E-commerce system
191
+ Customer:
192
+ id: uuid
193
+ email: email
194
+
195
+ Order:
196
+ id: uuid
197
+ customer_id: Ref(Customer.id)
198
+ items: array(OrderItem, 1..10)
199
+
200
+ OrderItem:
201
+ id: uuid
202
+ order_id: Ref(Order.id)
203
+ product_id: Ref(Product.id)
204
+ quantity: number(1..5)
205
+
206
+ Product:
207
+ id: uuid
208
+ name: text(10..50)
209
+ price: money
210
+ ```
211
+
212
+ ### Reference vs Embed
213
+
214
+ | Approach | Use Case | Example |
215
+ |----------|----------|---------|
216
+ | **Embed** | Full object needed | `address: Address` |
217
+ | **Ref** | Just ID/foreign key | `user_id: Ref(User.id)` |
218
+
219
+ ```dsl
220
+ # Embed - includes full address
221
+ User:
222
+ address: Address
223
+
224
+ # Generates:
225
+ # { "address": { "street": "...", "city": "...", ... } }
226
+
227
+ # Reference - just the ID
228
+ Order:
229
+ shipping_address_id: Ref(Address.id)
230
+
231
+ # Generates:
232
+ # { "shipping_address_id": "550e8400-..." }
233
+ ```
234
+
235
+ ### ⚠️ Important: Ref() vs copy() - Know the Difference!
236
+
237
+ `Ref()` and `copy()` look similar but behave **very differently**:
238
+
239
+ | Feature | `Ref(Schema.field)` | `copy("path.to.field")` |
240
+ |---------|---------------------|-------------------------|
241
+ | **What it does** | Generates a NEW value | Copies an EXISTING value |
242
+ | **Source** | Generates from another schema's field type | References already-generated field in current context |
243
+ | **Use case** | Foreign key that doesn't need to match existing data | Link IDs across nested objects in same record |
244
+ | **Requires** | Registry for cross-schema resolution | Field must be defined BEFORE the copy |
245
+
246
+ #### Example: The Wrong Way vs The Right Way
247
+
248
+ ```dsl
249
+ # ❌ WRONG - Using Ref() when you need copy()
250
+ TwitterDM:
251
+ sender: TwitterUser
252
+ sender_id: Ref(TwitterUser.id) # Generates a NEW ID, different from sender.id!
253
+
254
+ # ✅ CORRECT - Using copy() to link IDs
255
+ TwitterDM:
256
+ sender: TwitterUser
257
+ sender_id: copy("sender.id") # Uses the SAME ID as sender.id
258
+ ```
259
+
260
+ #### When to Use Each
261
+
262
+ | Scenario | Use | Example |
263
+ |----------|-----|---------|
264
+ | Foreign key to another table | `Ref()` | `customer_id: Ref(Customer.id)` |
265
+ | Copying ID from nested object | `copy()` | `sender_id: copy("sender.id")` |
266
+ | Dynamic key linking with map_by_field | `copy()` | `for_user_id: copy("users.recipient.id")` |
267
+ | Independent ID generation | `Ref()` | `correlation_id: Ref(Request.id)` |
268
+
269
+ ---
270
+
271
+ ## Copy Type
272
+
273
+ ### What is Copy?
274
+
275
+ `copy()` references a value from another field in the same generation context. This enables linking related IDs across nested schemas.
276
+
277
+ ### Basic Usage
278
+
279
+ ```dsl
280
+ Order:
281
+ user: User
282
+ order_user_id: copy("user.id") # Copies the generated user's ID
283
+
284
+ User:
285
+ id: uuid
286
+ name: full_name
287
+ ```
288
+
289
+ ### Path Syntax
290
+
291
+ Paths use dot notation to traverse nested structures:
292
+
293
+ ```dsl
294
+ TwitterDM:
295
+ users: map_by_field("id", sender: TwitterUser, recipient: TwitterUser)
296
+ sender_id: copy("users.sender.id") # Gets sender's ID
297
+ recipient_id: copy("users.recipient.id") # Gets recipient's ID
298
+ ```
299
+
300
+ ### Important: Field Order
301
+
302
+ `copy()` can only reference fields that are **defined before** the copy field:
303
+
304
+ ```dsl
305
+ # ✅ CORRECT - user defined before copy
306
+ Order:
307
+ user: User
308
+ user_id: copy("user.id")
309
+
310
+ # ❌ WRONG - user_id defined before user
311
+ Order:
312
+ user_id: copy("user.id") # Will return nil!
313
+ user: User
314
+ ```
315
+
316
+ ### Path Validation
317
+
318
+ FakeDataDSL validates `copy()` paths at load time by default:
319
+
320
+ ```ruby
321
+ registry = FakeDataDSL::Registry.new
322
+ registry.load_string(<<~DSL)
323
+ Order:
324
+ user: User
325
+ user_id: copy("users.id") # Typo: "users" instead of "user"
326
+ DSL
327
+ # Raises: FakeDataDSL::Errors::PathError
328
+ # Path 'users.id' not found in schema
329
+ # Did you mean: user.id?
330
+ ```
331
+
332
+ To disable validation (not recommended):
333
+
334
+ ```ruby
335
+ registry.load_string(dsl, validate: false)
336
+ # OR globally:
337
+ FakeDataDSL.configure { |c| c.validate_paths_on_load = false }
338
+ ```
339
+
340
+ ---
341
+
342
+ ## Shared Type
343
+
344
+ ### What is Shared?
345
+
346
+ `shared()` generates a value once and reuses it across all records in a batch when using `generate_many()`. This is essential for simulating realistic data where certain values should be consistent.
347
+
348
+ ### ⚠️ Important: Only Works with `generate_many()`
349
+
350
+ **`shared()` requires `generate_many()` to function correctly.**
351
+
352
+ ```ruby
353
+ # ✅ CORRECT - shared values work with generate_many()
354
+ webhooks = schema.generate_many(10, registry: registry)
355
+ # All 10 webhooks have the SAME recipient
356
+
357
+ # ⚠️ WARNING - shared values don't work with generate()
358
+ webhook1 = schema.generate(registry: registry)
359
+ webhook2 = schema.generate(registry: registry)
360
+ # webhook1 and webhook2 have DIFFERENT recipients!
361
+ ```
362
+
363
+ When `shared()` is used with `generate()`, FakeDataDSL emits a warning:
364
+
365
+ ```
366
+ WARNING: shared() type used outside generate_many() - value will not be shared across calls.
367
+ Use generate_many() for batch generation with shared values.
368
+ ```
369
+
370
+ ### Basic Usage
371
+
372
+ ```dsl
373
+ # All webhooks in a batch share the same recipient
374
+ TwitterDMWebhook:
375
+ users: map_by_field("id", sender: TwitterUser, recipient: shared(TwitterUser))
376
+ for_user_id: copy("users.recipient.id") # Same for all records
377
+ ```
378
+
379
+ ### How Shared Works
380
+
381
+ 1. First generation: Creates the value and caches it in `shared_context`
382
+ 2. Subsequent generations: Returns the cached value
383
+
384
+ ```ruby
385
+ schema = registry.schema("TwitterDMWebhook")
386
+ webhooks = schema.generate_many(10, registry: registry)
387
+
388
+ # Verify sharing
389
+ recipient_ids = webhooks.map { |w| w.dig("users", "recipient", "id") }.uniq
390
+ # => 1 (all have same recipient)
391
+
392
+ sender_ids = webhooks.map { |w| w.dig("users", "sender", "id") }.uniq
393
+ # => 10 (all have different senders)
394
+ ```
395
+
396
+ ### Shared with Custom Keys
397
+
398
+ Use the `key:` argument for multiple shared values of the same type:
399
+
400
+ ```dsl
401
+ Conversation:
402
+ user1: shared(User, key: "participant_1")
403
+ user2: shared(User, key: "participant_2")
404
+ messages: array(Message, 5..10)
405
+
406
+ Message:
407
+ sender: one_of(shared(User, key: "participant_1"), shared(User, key: "participant_2"))
408
+ text: message_text
409
+ ```
410
+
411
+ ### When to Use Shared
412
+
413
+ | Scenario | Use Shared? |
414
+ |----------|-------------|
415
+ | Inbox webhooks (same recipient) | ✅ Yes |
416
+ | Random users per record | ❌ No |
417
+ | Consistent config across batch | ✅ Yes |
418
+ | Unique IDs per record | ❌ No |
419
+
420
+ ---
421
+
422
+ ## Map By Field Type
423
+
424
+ ### What is Map By Field?
425
+
426
+ `map_by_field()` creates dynamic object keys from generated field values. This is essential for APIs that return objects keyed by ID.
427
+
428
+ ### Basic Usage
429
+
430
+ ```dsl
431
+ EventData:
432
+ users: map_by_field("id", sender: TwitterUser, recipient: TwitterUser)
433
+
434
+ TwitterUser:
435
+ id: snowflake_id
436
+ name: full_name
437
+ screen_name: social_handle
438
+ ```
439
+
440
+ Generates:
441
+
442
+ ```json
443
+ {
444
+ "users": {
445
+ "1234567890123456789": {
446
+ "id": "1234567890123456789",
447
+ "name": "John Smith",
448
+ "screen_name": "johnsmith"
449
+ },
450
+ "9876543210987654321": {
451
+ "id": "9876543210987654321",
452
+ "name": "Jane Doe",
453
+ "screen_name": "janedoe"
454
+ }
455
+ }
456
+ }
457
+ ```
458
+
459
+ ### Syntax
460
+
461
+ ```dsl
462
+ field_name: map_by_field("key_field", alias1: Schema1, alias2: Schema2, ...)
463
+ ```
464
+
465
+ - `"key_field"` - The field to use as object keys (must exist in schemas)
466
+ - `alias: Schema` - Named entries in the map
467
+
468
+ ### Accessing Map Values with Copy
469
+
470
+ Use the alias names to reference values:
471
+
472
+ ```dsl
473
+ EventData:
474
+ users: map_by_field("id", sender: TwitterUser, recipient: shared(TwitterUser))
475
+ sender_id: copy("users.sender.id") # Access sender's ID
476
+ recipient_id: copy("users.recipient.id") # Access recipient's ID
477
+ ```
478
+
479
+ ### Combining with Shared
480
+
481
+ ```dsl
482
+ # Recipient is shared across all records in generate_many()
483
+ EventData:
484
+ users: map_by_field("id", sender: TwitterUser, recipient: shared(TwitterUser))
485
+ ```
486
+
487
+ ---
488
+
489
+ ## One Of Type
490
+
491
+ ### What is One Of?
492
+
493
+ `one_of()` randomly selects and generates one of multiple schema options. This enables polymorphic data structures.
494
+
495
+ ### Basic Usage
496
+
497
+ ```dsl
498
+ WebhookEvent:
499
+ event_id: uuid
500
+ payload: one_of(MessagePayload, TypingPayload, ReadReceiptPayload)
501
+
502
+ MessagePayload:
503
+ type: const("message")
504
+ text: message_text
505
+
506
+ TypingPayload:
507
+ type: const("typing")
508
+ is_typing: boolean
509
+
510
+ ReadReceiptPayload:
511
+ type: const("read_receipt")
512
+ message_id: snowflake_id
513
+ ```
514
+
515
+ ### Weighted Selection
516
+
517
+ Use weights to control selection probability:
518
+
519
+ ```dsl
520
+ # Messages 80%, typing 15%, read receipts 5%
521
+ payload: one_of(MessagePayload:80%, TypingPayload:15%, ReadReceiptPayload:5%)
522
+ ```
523
+
524
+ ### Use Cases
525
+
526
+ - Polymorphic API responses
527
+ - Mixed event types in streams
528
+ - Testing multiple payload formats
529
+
530
+ ---
531
+
532
+ ## Const Type
533
+
534
+ ### What is Const?
535
+
536
+ `const()` generates a fixed, literal value. Use this for constant fields like type discriminators.
537
+
538
+ ### Why Not Enum?
539
+
540
+ `enum()` is for selecting from multiple values. For single literal values, `const()` is more semantically correct:
541
+
542
+ ```dsl
543
+ # ❌ Enum smell - single value enum
544
+ type: enum(message_create)
545
+
546
+ # ✅ Semantic - constant value
547
+ type: const("message_create")
548
+ ```
549
+
550
+ ### Basic Usage
551
+
552
+ ```dsl
553
+ DirectMessageEvent:
554
+ type: const("message_create") # String literal
555
+ version: const(1) # Integer literal
556
+ enabled: const(true) # Boolean literal
557
+ ```
558
+
559
+ ### String vs Unquoted Values
560
+
561
+ ```dsl
562
+ # Quoted strings (recommended)
563
+ type: const("message_create")
564
+
565
+ # Integer
566
+ version: const(42)
567
+
568
+ # Boolean
569
+ active: const(true)
570
+ ```
571
+
572
+ ---
573
+
574
+ ## Custom Type
575
+
576
+ ### What is Custom?
577
+
578
+ `custom` lets you compute field values dynamically using Ruby functions.
579
+
580
+ ### Basic Usage
581
+
582
+ ```dsl
583
+ Invoice:
584
+ items: array(LineItem, 1..5)
585
+ subtotal: custom(:calculate_subtotal)
586
+ tax: custom(:calculate_tax)
587
+ total: custom(:calculate_total)
588
+
589
+ LineItem:
590
+ quantity: number(1..5)
591
+ unit_price: number(9.99..99.99)
592
+ ```
593
+
594
+ ### Registering Functions
595
+
596
+ ```ruby
597
+ FakeDataDSL.register_function(:calculate_subtotal) do |context|
598
+ items = context[:items] || []
599
+ items.sum { |item| item["quantity"] * item["unit_price"] }
600
+ end
601
+
602
+ FakeDataDSL.register_function(:calculate_tax) do |context|
603
+ subtotal = context[:subtotal] || 0
604
+ (subtotal * 0.08).round(2)
605
+ end
606
+
607
+ FakeDataDSL.register_function(:calculate_total) do |context|
608
+ subtotal = context[:subtotal] || 0
609
+ tax = context[:tax] || 0
610
+ (subtotal + tax).round(2)
611
+ end
612
+ ```
613
+
614
+ ### Context Object
615
+
616
+ The context contains:
617
+ - All previously generated fields
618
+ - Schema metadata
619
+ - Registry (if available)
620
+
621
+ ```ruby
622
+ FakeDataDSL.register_function(:full_name) do |context|
623
+ "#{context['first_name']} #{context['last_name']}"
624
+ end
625
+
626
+ FakeDataDSL.register_function(:email_from_name) do |context|
627
+ name = context['name'].downcase.gsub(' ', '.')
628
+ "#{name}@company.com"
629
+ end
630
+ ```
631
+
632
+ ### Custom Function Examples
633
+
634
+ ```ruby
635
+ # Conditional logic
636
+ FakeDataDSL.register_function(:discount_amount) do |context|
637
+ total = context[:total] || 0
638
+ tier = context[:customer_tier] || 'standard'
639
+
640
+ case tier
641
+ when 'gold' then total * 0.15
642
+ when 'silver' then total * 0.10
643
+ else total * 0.05
644
+ end
645
+ end
646
+
647
+ # External data
648
+ FakeDataDSL.register_function(:sku) do |context|
649
+ category = context[:category]
650
+ id = context[:id][0..7]
651
+ "#{category.upcase}-#{id}"
652
+ end
653
+
654
+ # Date calculations
655
+ FakeDataDSL.register_function(:expiration_date) do |context|
656
+ created = Date.parse(context[:created_at])
657
+ (created + 365).to_s
658
+ end
659
+ ```
660
+
661
+ ---
662
+
663
+ ## Sequence Types
664
+
665
+ ### id_sequence
666
+
667
+ Auto-incrementing integers:
668
+
669
+ ```dsl
670
+ User:
671
+ id: id_sequence
672
+ order_number: id_sequence(start: 1000)
673
+ ```
674
+
675
+ ### character_sequence
676
+
677
+ Sequential letters:
678
+
679
+ ```dsl
680
+ Item:
681
+ code: character_sequence
682
+ # Generates: A, B, C, ... Z, AA, AB, ...
683
+ ```
684
+
685
+ ### digit_sequence
686
+
687
+ Sequential numbers as strings:
688
+
689
+ ```dsl
690
+ Invoice:
691
+ number: digit_sequence(start: "001", pad: 5)
692
+ # Generates: "00001", "00002", "00003", ...
693
+ ```
694
+
695
+ ---
696
+
697
+ ## Formula Type
698
+
699
+ ### Mathematical Expressions
700
+
701
+ ```dsl
702
+ Product:
703
+ base_price: number(10..100)
704
+ tax_rate: number(0.05..0.15)
705
+ final_price: formula("base_price * (1 + tax_rate)")
706
+ ```
707
+
708
+ ### Supported Operations
709
+
710
+ - Arithmetic: `+`, `-`, `*`, `/`, `%`
711
+ - References to other fields
712
+ - Parentheses for grouping
713
+
714
+ ```dsl
715
+ Order:
716
+ quantity: number(1..10)
717
+ unit_price: number(9.99..99.99)
718
+ discount: number(0..20)
719
+
720
+ subtotal: formula("quantity * unit_price")
721
+ discount_amount: formula("subtotal * discount / 100")
722
+ total: formula("subtotal - discount_amount")
723
+ ```
724
+
725
+ ---
726
+
727
+ ## Template Type
728
+
729
+ ### String Templates
730
+
731
+ ```dsl
732
+ User:
733
+ first_name: first_name
734
+ last_name: last_name
735
+ display_name: template("${first_name} ${last_name}")
736
+ email: template("${first_name}.${last_name}@company.com")
737
+ ```
738
+
739
+ ### Template Syntax
740
+
741
+ - `${field_name}` - Insert field value
742
+ - Literal text around placeholders
743
+
744
+ ```dsl
745
+ Product:
746
+ brand: text(5..20)
747
+ model: text(3..10)
748
+ sku: template("${brand}-${model}-001")
749
+ url: template("https://shop.com/products/${sku}")
750
+ ```
751
+
752
+ ---
753
+
754
+ ## Type Composition
755
+
756
+ ### Combining Types
757
+
758
+ ```dsl
759
+ Order:
760
+ id: uuid(unique: true)
761
+
762
+ # Array of referenced items
763
+ item_ids: array(Ref(Product.id), 1..5)
764
+
765
+ # Nested objects
766
+ shipping: ShippingInfo
767
+ billing: BillingInfo
768
+
769
+ # Computed from nested data
770
+ total: custom(:sum_items)
771
+
772
+ ShippingInfo:
773
+ address: Address
774
+ method: enum(standard, express, overnight)
775
+ cost: money
776
+
777
+ BillingInfo:
778
+ address: Address
779
+ card_type: enum(visa, mastercard, amex)
780
+ last_four: text(4)
781
+ ```
782
+
783
+ ---
784
+
785
+ ## Next Steps
786
+
787
+ - [Field Modifiers](field_modifiers.md) - Optional, nullable, conditional
788
+ - [Custom Types Guide](../api/custom_types.md) - Creating your own types
789
+ - [Custom Functions](../api/custom_functions.md) - Computed field functions
790
+