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,63 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "synthra"
6
+ require "benchmark"
7
+
8
+ puts "Synthra Batch Generation Benchmark"
9
+ puts "=" * 50
10
+ puts
11
+
12
+ dsl = <<~DSL
13
+ User:
14
+ id: uuid
15
+ name: name
16
+ email: email
17
+ age: number(18..65)
18
+ active: boolean(true:80%)
19
+ role: enum(user, admin:10%, moderator:20%)
20
+ DSL
21
+
22
+ schema = Synthra.parse(dsl)
23
+
24
+ batch_sizes = [100, 1_000, 10_000]
25
+
26
+ # Add 100_000 only if running full benchmark
27
+ if ARGV.include?("--full")
28
+ batch_sizes << 100_000
29
+ end
30
+
31
+ puts "Batch sizes: #{batch_sizes.join(", ")}"
32
+ puts
33
+
34
+ results = []
35
+
36
+ batch_sizes.each do |count|
37
+ GC.start
38
+
39
+ time = Benchmark.measure do
40
+ schema.generate_many(count, seed: 42)
41
+ end
42
+
43
+ throughput = (count / time.real).round
44
+ time_ms = (time.real * 1000).round
45
+
46
+ results << {
47
+ count: count,
48
+ time_ms: time_ms,
49
+ throughput: throughput
50
+ }
51
+
52
+ puts "#{count.to_s.rjust(7)} records: #{time_ms}ms (#{throughput} records/sec)"
53
+ end
54
+
55
+ puts
56
+ puts "Summary Table:"
57
+ puts "-" * 50
58
+ puts "| Records | Time (ms) | Throughput (rec/sec) |"
59
+ puts "|---------|-----------|----------------------|"
60
+ results.each do |r|
61
+ puts "| #{r[:count].to_s.rjust(7)} | #{r[:time_ms].to_s.rjust(9)} | #{r[:throughput].to_s.rjust(20)} |"
62
+ end
63
+
@@ -0,0 +1,295 @@
1
+ # frozen_string_literal: true
2
+
3
+ # Synthra Engine Comparison Benchmark
4
+ #
5
+ # This script compares all three generation engines:
6
+ # 1. Standard (Ruby) Engine - Full Faker, growing memory
7
+ # 2. Streaming Engine - Full Faker, O(1) memory
8
+ # 3. Native (Rust) Engine - fake-rs, 2 threads default, fastest
9
+ #
10
+ # Usage:
11
+ # ruby benchmark/engine_comparison.rb
12
+ # ruby benchmark/engine_comparison.rb --full # Include larger tests
13
+
14
+ require "bundler/setup"
15
+ require "synthra"
16
+ require "benchmark"
17
+ require "tempfile"
18
+
19
+ FULL_MODE = ARGV.include?("--full")
20
+
21
+ # Schema for benchmarking
22
+ SCHEMA_DSL = <<~DSL
23
+ User:
24
+ id: uuid
25
+ name: name
26
+ email: email
27
+ age: number(18..80)
28
+ active: boolean
29
+ DSL
30
+
31
+ def format_rate(rate)
32
+ rate.to_s.reverse.gsub(/(\d{3})(?=\d)/, '\1,').reverse
33
+ end
34
+
35
+ def format_time(time)
36
+ if time < 1
37
+ "#{(time * 1000).round(1)}ms"
38
+ else
39
+ "#{time.round(2)}s"
40
+ end
41
+ end
42
+
43
+ def memory_usage
44
+ `ps -o rss= -p #{Process.pid}`.to_i / 1024 # MB
45
+ end
46
+
47
+ puts "=" * 70
48
+ puts "Synthra Engine Comparison Benchmark"
49
+ puts "=" * 70
50
+ puts
51
+ puts "Schema: User (uuid, name, email, number, boolean)"
52
+ puts "Date: #{Time.now}"
53
+ puts "Ruby: #{RUBY_VERSION}"
54
+ puts "Mode: #{FULL_MODE ? 'Full' : 'Quick'}"
55
+ puts
56
+
57
+ # Parse schema once
58
+ schema = Synthra.parse(SCHEMA_DSL)
59
+
60
+ # Warmup - generate a few records to load Faker
61
+ puts "Warming up..."
62
+ schema.generate_many(10, seed: 1)
63
+ puts
64
+
65
+ # Check native availability
66
+ native_available = Synthra::NativeEngine.available?
67
+ puts "Native Engine: #{native_available ? '✅ Available' : '❌ Not available'}"
68
+ if native_available
69
+ puts " #{Synthra::NativeEngine.performance_info}"
70
+ end
71
+ puts
72
+
73
+ # ============================================================================
74
+ # BENCHMARK 1: Standard Engine (Ruby)
75
+ # ============================================================================
76
+ puts "-" * 70
77
+ puts "1. STANDARD ENGINE (Ruby + Faker)"
78
+ puts "-" * 70
79
+ puts " - Full Faker library calls"
80
+ puts " - Memory grows with record count O(n)"
81
+ puts " - Best for: Small batches, development"
82
+ puts
83
+
84
+ standard_counts = FULL_MODE ? [100, 1_000, 10_000, 50_000] : [100, 1_000, 5_000]
85
+
86
+ puts " Count Time Rate Memory"
87
+ puts " " + "-" * 55
88
+
89
+ standard_counts.each do |count|
90
+ GC.start
91
+ mem_before = memory_usage
92
+
93
+ time = Benchmark.realtime do
94
+ schema.generate_many(count, seed: 42)
95
+ end
96
+
97
+ mem_after = memory_usage
98
+ rate = (count / time).round
99
+
100
+ puts " #{count.to_s.rjust(8)} #{format_time(time).rjust(8)} #{format_rate(rate).rjust(12)}/sec #{mem_after}MB (+#{mem_after - mem_before}MB)"
101
+ end
102
+ puts
103
+
104
+ # ============================================================================
105
+ # BENCHMARK 2: Streaming Engine (Ruby)
106
+ # ============================================================================
107
+ puts "-" * 70
108
+ puts "2. STREAMING ENGINE (Ruby + Faker)"
109
+ puts "-" * 70
110
+ puts " - Full Faker library calls"
111
+ puts " - Constant memory O(1)"
112
+ puts " - Best for: Large batches, memory-constrained"
113
+ puts
114
+
115
+ stream_counts = FULL_MODE ? [1_000, 10_000, 100_000, 500_000] : [1_000, 10_000, 50_000]
116
+
117
+ puts " Count Time Rate Memory"
118
+ puts " " + "-" * 55
119
+
120
+ stream_counts.each do |count|
121
+ GC.start
122
+ mem_before = memory_usage
123
+ processed = 0
124
+ last_record = nil
125
+
126
+ time = Benchmark.realtime do
127
+ schema.generate_many_stream(count, seed: 42) do |record|
128
+ processed += 1
129
+ last_record = record # Force record to be processed
130
+ end
131
+ end
132
+
133
+ # If no records were processed, the method might have returned an enumerator
134
+ if processed == 0
135
+ time = Benchmark.realtime do
136
+ schema.generate_many_stream(count, seed: 42).each do |record|
137
+ processed += 1
138
+ last_record = record
139
+ end
140
+ end
141
+ end
142
+
143
+ mem_after = memory_usage
144
+ rate = processed > 0 ? (processed / time).round : 0
145
+
146
+ puts " #{processed.to_s.rjust(8)} #{format_time(time).rjust(8)} #{format_rate(rate).rjust(12)}/sec #{mem_after}MB (+#{mem_after - mem_before}MB)"
147
+ end
148
+ puts
149
+
150
+ # ============================================================================
151
+ # BENCHMARK 3: Native Engine (Rust + fake-rs)
152
+ # ============================================================================
153
+ if native_available
154
+ puts "-" * 70
155
+ puts "3. NATIVE ENGINE (Rust + fake-rs)"
156
+ puts "-" * 70
157
+ puts " - Native Rust fake-rs library"
158
+ puts " - Default: 2 threads (configurable)"
159
+ puts " - Best for: High-volume, production"
160
+ puts
161
+
162
+ # 3a: In-memory generation
163
+ puts " 3a. In-Memory Generation (returns Ruby Array)"
164
+ puts " " + "-" * 55
165
+
166
+ native_counts = FULL_MODE ? [10_000, 100_000, 1_000_000, 5_000_000] : [10_000, 100_000, 500_000]
167
+
168
+ puts " Count Time Rate Memory"
169
+ puts " " + "-" * 55
170
+
171
+ native_counts.each do |count|
172
+ GC.start
173
+ mem_before = memory_usage
174
+
175
+ time = Benchmark.realtime do
176
+ schema.generate_many(count, engine: :native, seed: 42)
177
+ end
178
+
179
+ mem_after = memory_usage
180
+ rate = (count / time).round
181
+
182
+ puts " #{count.to_s.rjust(8)} #{format_time(time).rjust(8)} #{format_rate(rate).rjust(12)}/sec #{mem_after}MB (+#{mem_after - mem_before}MB)"
183
+ end
184
+ puts
185
+
186
+ # 3b: File generation (fastest)
187
+ puts " 3b. Direct File Generation (bypasses Ruby)"
188
+ puts " " + "-" * 55
189
+
190
+ file_counts = FULL_MODE ? [100_000, 1_000_000, 5_000_000, 10_000_000] : [100_000, 1_000_000, 3_000_000]
191
+
192
+ puts " Count Time Rate File Size"
193
+ puts " " + "-" * 55
194
+
195
+ file_counts.each do |count|
196
+ tmpfile = Tempfile.new(["benchmark", ".jsonl"])
197
+
198
+ time = Benchmark.realtime do
199
+ schema.generate_to_file(count, tmpfile.path, seed: 42)
200
+ end
201
+
202
+ size = File.size(tmpfile.path)
203
+ rate = (count / time).round
204
+ size_mb = (size / 1024.0 / 1024.0).round(1)
205
+
206
+ puts " #{count.to_s.rjust(8)} #{format_time(time).rjust(8)} #{format_rate(rate).rjust(12)}/sec #{size_mb}MB"
207
+
208
+ tmpfile.unlink
209
+ end
210
+ puts
211
+
212
+ # 3c: Thread comparison
213
+ puts " 3c. Thread Comparison (1M records to file)"
214
+ puts " " + "-" * 55
215
+
216
+ puts " Threads Time Rate"
217
+ puts " " + "-" * 40
218
+
219
+ [1, 2, 4, 0].each do |threads|
220
+ thread_label = threads == 0 ? "All (#{Synthra::NativeEngine.thread_count})" : threads.to_s
221
+ tmpfile = Tempfile.new(["benchmark", ".jsonl"])
222
+
223
+ # Note: Thread count only affects first call (Rayon limitation)
224
+ # Using env var workaround for accurate test
225
+ time = Benchmark.realtime do
226
+ schema.generate_to_file(1_000_000, tmpfile.path, seed: 42, threads: threads)
227
+ end
228
+
229
+ rate = (1_000_000 / time).round
230
+
231
+ puts " #{thread_label.to_s.rjust(8)} #{format_time(time).rjust(8)} #{format_rate(rate).rjust(12)}/sec"
232
+
233
+ tmpfile.unlink
234
+ end
235
+ puts
236
+
237
+ # 3d: Locale comparison
238
+ puts " 3d. Locale Comparison (100K records)"
239
+ puts " " + "-" * 55
240
+
241
+ puts " Locale Time Rate Sample Name"
242
+ puts " " + "-" * 60
243
+
244
+ ["en", "fr_fr", "de_de", "ja_jp", "zh_cn"].each do |locale|
245
+ tmpfile = Tempfile.new(["benchmark", ".jsonl"])
246
+
247
+ time = Benchmark.realtime do
248
+ schema.generate_to_file(100_000, tmpfile.path, seed: 42, locale: locale)
249
+ end
250
+
251
+ rate = (100_000 / time).round
252
+
253
+ # Get sample name
254
+ sample = schema.generate_many(1, engine: :native, seed: 42, locale: locale).first
255
+ sample_name = sample[:name][0..20]
256
+
257
+ puts " #{locale.ljust(8)} #{format_time(time).rjust(8)} #{format_rate(rate).rjust(12)}/sec #{sample_name}"
258
+
259
+ tmpfile.unlink
260
+ end
261
+ puts
262
+ else
263
+ puts "-" * 70
264
+ puts "3. NATIVE ENGINE - Not Available"
265
+ puts "-" * 70
266
+ puts " To enable:"
267
+ puts " 1. Install Rust: curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh"
268
+ puts " 2. gem install rb_sys"
269
+ puts " 3. bundle exec rake compile"
270
+ puts
271
+ end
272
+
273
+ # ============================================================================
274
+ # SUMMARY
275
+ # ============================================================================
276
+ puts "=" * 70
277
+ puts "SUMMARY"
278
+ puts "=" * 70
279
+ puts
280
+ puts "Engine | Speed | Memory | Best For"
281
+ puts "-" * 70
282
+ puts "Standard | ~7K/sec | O(n) | Development, small batches"
283
+ puts "Streaming | ~7K/sec | O(1) | Large batches, memory-limited"
284
+ if native_available
285
+ puts "Native (in-memory) | ~1M/sec | O(n) | Medium batches, need Ruby objects"
286
+ puts "Native (to-file) | ~4-5M/sec | O(1) | Production, high-volume"
287
+ end
288
+ puts
289
+ puts "Recommendations:"
290
+ puts " - Development: Standard engine (simpler debugging)"
291
+ puts " - Testing (>10K): Streaming engine (memory safe)"
292
+ puts " - Production: Native engine with threads: 2 (default)"
293
+ puts " - Maximum speed: Native engine with threads: 0"
294
+ puts
295
+
@@ -0,0 +1,108 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "synthra"
6
+ require "benchmark"
7
+
8
+ puts "Synthra Single Record Benchmark"
9
+ puts "=" * 50
10
+ puts
11
+
12
+ # Simple schema
13
+ simple_dsl = <<~DSL
14
+ User:
15
+ id: uuid
16
+ name: name
17
+ email: email
18
+ age: number(18..65)
19
+ active: boolean
20
+ DSL
21
+
22
+ # Medium schema
23
+ medium_dsl = <<~DSL
24
+ Product:
25
+ id: uuid
26
+ sku: text(10..20)
27
+ name: text(20..100)
28
+ description: text(50..200)
29
+ price: number(1..1000)
30
+ category: enum(electronics, clothing, home, food)
31
+ in_stock: boolean
32
+ stock_quantity: number(0..1000)
33
+ rating: number(1..5)
34
+ review_count: number(0..500)
35
+ created_at: past_date(1y)
36
+ updated_at: past_date(30d)
37
+ DSL
38
+
39
+ # Complex schema with nesting
40
+ complex_dsl = <<~DSL
41
+ Order:
42
+ id: uuid
43
+ customer: Customer
44
+ customer_id: copy("customer.id")
45
+ items: array(OrderItem, 1..3)
46
+ status: enum(pending, shipped, delivered)
47
+ created_at: timestamp
48
+
49
+ Customer:
50
+ id: uuid
51
+ email: email
52
+ name: name
53
+
54
+ OrderItem:
55
+ id: uuid
56
+ product_id: uuid
57
+ quantity: number(1..10)
58
+ price: number(10..100)
59
+ DSL
60
+
61
+ simple_registry = Synthra::Registry.new
62
+ simple_registry.load_string(simple_dsl)
63
+ simple = simple_registry.schema("User")
64
+
65
+ medium_registry = Synthra::Registry.new
66
+ medium_registry.load_string(medium_dsl)
67
+ medium = medium_registry.schema("Product")
68
+
69
+ complex_registry = Synthra::Registry.new
70
+ complex_registry.load_string(complex_dsl)
71
+ complex = complex_registry.schema("Order")
72
+
73
+ iterations = 1000
74
+
75
+ puts "Running #{iterations} iterations for each schema type..."
76
+ puts
77
+
78
+ Benchmark.bm(30) do |x|
79
+ x.report("Simple (5 fields):") do
80
+ iterations.times { simple.generate(seed: 42) }
81
+ end
82
+
83
+ x.report("Medium (12 fields):") do
84
+ iterations.times { medium.generate(seed: 42) }
85
+ end
86
+
87
+ x.report("Complex (nested, arrays):") do
88
+ iterations.times { complex.generate(seed: 42, registry: complex_registry) }
89
+ end
90
+ end
91
+
92
+ puts
93
+ puts "Throughput Summary:"
94
+ puts "-" * 50
95
+
96
+ [
97
+ ["Simple", simple, nil],
98
+ ["Medium", medium, nil],
99
+ ["Complex", complex, complex_registry]
100
+ ].each do |name, schema, registry|
101
+ time = Benchmark.measure do
102
+ iterations.times { schema.generate(seed: 42, registry: registry) }
103
+ end
104
+ throughput = (iterations / time.real).round
105
+ avg_ms = ((time.real / iterations) * 1000).round(2)
106
+ puts "#{name.ljust(20)} #{avg_ms}ms avg (#{throughput} ops/sec)"
107
+ end
108
+
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "synthra"
6
+ require "benchmark"
7
+
8
+ puts "Synthra Streaming Benchmark"
9
+ puts "=" * 50
10
+ puts
11
+
12
+ dsl = <<~DSL
13
+ Event:
14
+ id: uuid
15
+ type: enum(click, view, purchase)
16
+ user_id: uuid
17
+ timestamp: now
18
+ DSL
19
+
20
+ schema = Synthra.parse(dsl)
21
+
22
+ def memory_mb
23
+ `ps -o rss= -p #{Process.pid}`.to_i / 1024.0
24
+ rescue
25
+ 0
26
+ end
27
+
28
+ stream_sizes = [10_000, 100_000]
29
+
30
+ # Add 1M only if running full benchmark
31
+ if ARGV.include?("--full")
32
+ stream_sizes << 1_000_000
33
+ end
34
+
35
+ puts "Testing streaming with constant memory usage..."
36
+ puts "Stream sizes: #{stream_sizes.map { |s| s >= 1_000_000 ? "#{s / 1_000_000}M" : "#{s / 1000}K" }.join(", ")}"
37
+ puts
38
+
39
+ results = []
40
+
41
+ stream_sizes.each do |count|
42
+ GC.start
43
+ sleep 0.1
44
+ initial_memory = memory_mb
45
+ peak_memory = initial_memory
46
+
47
+ time = Benchmark.measure do
48
+ schema.generate_stream(count: count, seed: 42).each do |_record|
49
+ current = memory_mb
50
+ peak_memory = current if current > peak_memory
51
+ end
52
+ end
53
+
54
+ memory_growth = peak_memory - initial_memory
55
+ throughput = (count / time.real).round
56
+
57
+ results << {
58
+ count: count,
59
+ time_s: time.real.round(2),
60
+ memory_mb: memory_growth.round(1),
61
+ throughput: throughput
62
+ }
63
+
64
+ label = count >= 1_000_000 ? "#{count / 1_000_000}M" : "#{count / 1000}K"
65
+ puts "#{label.rjust(5)} records:"
66
+ puts " Time: #{(time.real * 1000).round}ms"
67
+ puts " Memory growth: #{memory_growth.round(1)}MB"
68
+ puts " Throughput: #{throughput} records/sec"
69
+ puts
70
+ end
71
+
72
+ puts "Key Insight: Memory stays constant regardless of record count!"
73
+ puts "This is because streaming processes one record at a time."
74
+