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,387 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # ============================================================================
5
+ # FakeDataDSL - Custom Types Examples
6
+ # ============================================================================
7
+ # This file demonstrates how to create and register custom types.
8
+ # Run with: cd fake_data_dsl && ruby tech_example/10_custom_types.rb
9
+ # ============================================================================
10
+
11
+ # Load the local gem
12
+ $LOAD_PATH.unshift(File.expand_path('../lib', __dir__))
13
+ require 'fake_data_dsl'
14
+ require 'json'
15
+
16
+ puts "=" * 70
17
+ puts "FakeDataDSL Custom Types Examples"
18
+ puts "=" * 70
19
+ puts
20
+
21
+ # ============================================================================
22
+ # Example 1: Simple Block-Based Custom Type
23
+ # ============================================================================
24
+ puts "1. SIMPLE BLOCK-BASED CUSTOM TYPE"
25
+ puts "-" * 40
26
+
27
+ # Register a custom SSN type
28
+ FakeDataDSL.register_type(:ssn) do |rng, _context, _args, mode|
29
+ case mode
30
+ when :random
31
+ "#{rng.int(100, 999)}-#{rng.int(10, 99)}-#{rng.int(1000, 9999)}"
32
+ when :edge
33
+ ["000-00-0000", "999-99-9999", "123-45-6789"].sample
34
+ when :invalid
35
+ [nil, "invalid-ssn", "12345", 12345].sample
36
+ else
37
+ "#{rng.int(100, 999)}-#{rng.int(10, 99)}-#{rng.int(1000, 9999)}"
38
+ end
39
+ end
40
+
41
+ schema = FakeDataDSL.parse(<<~DSL)
42
+ Person:
43
+ name: name
44
+ ssn: ssn
45
+ DSL
46
+
47
+ puts "Generated Person with SSN:"
48
+ person = schema.generate
49
+ puts JSON.pretty_generate(person)
50
+ puts
51
+ puts "Edge case SSN:"
52
+ edge_person = schema.generate(mode: :edge)
53
+ puts " SSN: #{edge_person['ssn']}"
54
+ puts
55
+
56
+ # ============================================================================
57
+ # Example 2: Custom Type with Arguments
58
+ # ============================================================================
59
+ puts "2. CUSTOM TYPE WITH ARGUMENTS"
60
+ puts "-" * 40
61
+
62
+ # Product code with configurable prefix
63
+ FakeDataDSL.register_type(:product_code) do |rng, _context, args, _mode|
64
+ prefix = args[:prefix] || "PRD"
65
+ length = args[:length] || 8
66
+
67
+ suffix = (1..length).map { rng.int(0, 9) }.join
68
+ "#{prefix}-#{suffix}"
69
+ end
70
+
71
+ schema2 = FakeDataDSL.parse(<<~DSL)
72
+ Product:
73
+ id: uuid
74
+ code_default: product_code
75
+ code_custom: product_code(prefix: SKU, length: 6)
76
+ code_long: product_code(prefix: ITEM, length: 12)
77
+ DSL
78
+
79
+ puts "Generated Product Codes:"
80
+ product = schema2.generate
81
+ puts JSON.pretty_generate(product)
82
+ puts
83
+
84
+ # ============================================================================
85
+ # Example 3: Class-Based Custom Type
86
+ # ============================================================================
87
+ puts "3. CLASS-BASED CUSTOM TYPE"
88
+ puts "-" * 40
89
+
90
+ # Define a custom type class
91
+ class VinType < FakeDataDSL::Types::Base
92
+ VIN_CHARS = "ABCDEFGHJKLMNPRSTUVWXYZ0123456789".chars.freeze
93
+
94
+ def generate_random(rng, _args)
95
+ # VIN is 17 characters
96
+ 17.times.map { VIN_CHARS[rng.int(0, VIN_CHARS.length - 1)] }.join
97
+ end
98
+
99
+ def generate_edge(rng, _args)
100
+ # Edge cases
101
+ [
102
+ "11111111111111111", # All 1s
103
+ "00000000000000000", # All 0s
104
+ "WVWZZZ3CZWE123456", # Real-ish format
105
+ "A" * 17, # All same letter
106
+ ].sample
107
+ end
108
+
109
+ def generate_invalid(rng, _args)
110
+ [
111
+ nil,
112
+ "INVALID",
113
+ "TOO-SHORT",
114
+ "THIS-IS-WAY-TOO-LONG-FOR-A-VIN",
115
+ 12345,
116
+ ""
117
+ ].sample
118
+ end
119
+ end
120
+
121
+ FakeDataDSL.register_type(:vin, VinType)
122
+
123
+ schema3 = FakeDataDSL.parse(<<~DSL)
124
+ Vehicle:
125
+ id: uuid
126
+ make: enum(Toyota, Honda, Ford, BMW, Tesla)
127
+ model: text(5..15)
128
+ year: number(1990..2024)
129
+ vin: vin
130
+ DSL
131
+
132
+ puts "Generated Vehicle with VIN:"
133
+ vehicle = schema3.generate
134
+ puts JSON.pretty_generate(vehicle)
135
+ puts
136
+ puts "Edge case VIN: #{schema3.generate(mode: :edge)['vin']}"
137
+ puts
138
+
139
+ # ============================================================================
140
+ # Example 4: Custom Type Using Faker
141
+ # ============================================================================
142
+ puts "4. CUSTOM TYPE USING FAKER"
143
+ puts "-" * 40
144
+
145
+ require 'faker'
146
+
147
+ FakeDataDSL.register_type(:pokemon_name) do |_rng, _context, _args, mode|
148
+ case mode
149
+ when :random
150
+ Faker::Games::Pokemon.name
151
+ when :edge
152
+ ["Pikachu", "MissingNo", "", "A" * 100].sample
153
+ when :invalid
154
+ [nil, 123, [], {}].sample
155
+ else
156
+ Faker::Games::Pokemon.name
157
+ end
158
+ end
159
+
160
+ FakeDataDSL.register_type(:lorem_ipsum) do |_rng, _context, args, _mode|
161
+ sentences = args[:sentences] || 3
162
+ Faker::Lorem.sentences(number: sentences).join(" ")
163
+ end
164
+
165
+ schema4 = FakeDataDSL.parse(<<~DSL)
166
+ GameCharacter:
167
+ id: uuid
168
+ name: pokemon_name
169
+ bio: lorem_ipsum(sentences: 5)
170
+ level: number(1..100)
171
+ DSL
172
+
173
+ puts "Generated Game Character:"
174
+ character = schema4.generate
175
+ puts JSON.pretty_generate(character)
176
+ puts
177
+
178
+ # ============================================================================
179
+ # Example 5: Complex Custom Type
180
+ # ============================================================================
181
+ puts "5. COMPLEX CUSTOM TYPE (Credit Card)"
182
+ puts "-" * 40
183
+
184
+ class CreditCardType < FakeDataDSL::Types::Base
185
+ PREFIXES = {
186
+ visa: ["4"],
187
+ mastercard: ["51", "52", "53", "54", "55"],
188
+ amex: ["34", "37"],
189
+ discover: ["6011"]
190
+ }.freeze
191
+
192
+ def generate_random(rng, args)
193
+ card_type = args[:type]&.to_sym || PREFIXES.keys.sample
194
+ generate_card(rng, card_type)
195
+ end
196
+
197
+ def generate_edge(rng, args)
198
+ # Famous test card numbers
199
+ [
200
+ "4111111111111111", # Visa test
201
+ "5500000000000004", # Mastercard test
202
+ "340000000000009", # Amex test
203
+ "0000000000000000", # All zeros
204
+ ].sample
205
+ end
206
+
207
+ def generate_invalid(rng, args)
208
+ [
209
+ nil,
210
+ "invalid",
211
+ "1234",
212
+ "1234-5678-9012-3456-7890", # Too long
213
+ 12345678901234, # Number not string
214
+ ].sample
215
+ end
216
+
217
+ private
218
+
219
+ def generate_card(rng, card_type)
220
+ prefix = PREFIXES[card_type]&.sample || "4"
221
+ length = card_type == :amex ? 15 : 16
222
+
223
+ # Generate number with prefix
224
+ remaining = length - prefix.length - 1
225
+ number = prefix + remaining.times.map { rng.int(0, 9) }.join
226
+
227
+ # Add Luhn check digit
228
+ number + luhn_check_digit(number)
229
+ end
230
+
231
+ def luhn_check_digit(number)
232
+ digits = number.chars.map(&:to_i).reverse
233
+ sum = digits.each_with_index.sum do |digit, i|
234
+ if i.odd?
235
+ doubled = digit * 2
236
+ doubled > 9 ? doubled - 9 : doubled
237
+ else
238
+ digit
239
+ end
240
+ end
241
+ ((10 - (sum % 10)) % 10).to_s
242
+ end
243
+ end
244
+
245
+ FakeDataDSL.register_type(:credit_card_number, CreditCardType)
246
+
247
+ schema5 = FakeDataDSL.parse(<<~DSL)
248
+ PaymentMethod:
249
+ id: uuid
250
+ card_number: credit_card_number
251
+ visa_card: credit_card_number(type: visa)
252
+ amex_card: credit_card_number(type: amex)
253
+ expiry_month: number(1..12)
254
+ expiry_year: number(2024..2030)
255
+ cvv: text(3)
256
+ DSL
257
+
258
+ puts "Generated Payment Method:"
259
+ payment = schema5.generate
260
+ puts JSON.pretty_generate(payment)
261
+ puts
262
+
263
+ # ============================================================================
264
+ # Example 6: Custom Type with Context
265
+ # ============================================================================
266
+ puts "6. CUSTOM TYPE WITH CONTEXT"
267
+ puts "-" * 40
268
+
269
+ FakeDataDSL.register_type(:username_from_email) do |rng, context, _args, _mode|
270
+ email = context['email']
271
+ if email
272
+ # Extract username from email
273
+ base = email.split('@').first.gsub('.', '_')
274
+ "#{base}#{rng.int(1, 999)}"
275
+ else
276
+ "user#{rng.int(10000, 99999)}"
277
+ end
278
+ end
279
+
280
+ schema6 = FakeDataDSL.parse(<<~DSL)
281
+ UserAccount:
282
+ email: email
283
+ username: username_from_email
284
+ DSL
285
+
286
+ puts "Generated User (username derived from email):"
287
+ 3.times do
288
+ account = schema6.generate
289
+ puts " #{account['email']} -> #{account['username']}"
290
+ end
291
+ puts
292
+
293
+ # ============================================================================
294
+ # Example 7: Locale-Aware Custom Type
295
+ # ============================================================================
296
+ puts "7. LOCALE-AWARE CUSTOM TYPE"
297
+ puts "-" * 40
298
+
299
+ FakeDataDSL.register_type(:local_phone) do |rng, _context, args, _mode|
300
+ country = args[:country] || "US"
301
+
302
+ case country.to_s.upcase
303
+ when "US"
304
+ area = rng.int(200, 999)
305
+ prefix = rng.int(200, 999)
306
+ line = rng.int(1000, 9999)
307
+ "+1-#{area}-#{prefix}-#{line}"
308
+ when "UK"
309
+ "+44-#{rng.int(1000, 9999)}-#{rng.int(100000, 999999)}"
310
+ when "DE"
311
+ "+49-#{rng.int(100, 999)}-#{rng.int(1000000, 9999999)}"
312
+ when "JP"
313
+ "+81-#{rng.int(10, 99)}-#{rng.int(1000, 9999)}-#{rng.int(1000, 9999)}"
314
+ else
315
+ "+#{rng.int(1, 999)}-#{rng.int(100000000, 999999999)}"
316
+ end
317
+ end
318
+
319
+ schema7 = FakeDataDSL.parse(<<~DSL)
320
+ Contact:
321
+ name: name
322
+ phone_us: local_phone(country: US)
323
+ phone_uk: local_phone(country: UK)
324
+ phone_de: local_phone(country: DE)
325
+ phone_jp: local_phone(country: JP)
326
+ DSL
327
+
328
+ puts "Generated Contact with Localized Phones:"
329
+ contact = schema7.generate
330
+ puts JSON.pretty_generate(contact)
331
+ puts
332
+
333
+ # ============================================================================
334
+ # Example 8: Stateful Custom Type
335
+ # ============================================================================
336
+ puts "8. STATEFUL CUSTOM TYPE (Auto-increment)"
337
+ puts "-" * 40
338
+
339
+ class TicketNumberType < FakeDataDSL::Types::Base
340
+ def initialize
341
+ super
342
+ @mutex = Mutex.new
343
+ @counter = 0
344
+ end
345
+
346
+ def generate_random(_rng, args)
347
+ prefix = args[:prefix] || "TKT"
348
+ year = Time.now.year
349
+
350
+ number = @mutex.synchronize do
351
+ @counter += 1
352
+ @counter
353
+ end
354
+
355
+ "#{prefix}-#{year}-#{number.to_s.rjust(6, '0')}"
356
+ end
357
+
358
+ def generate_edge(_rng, _args)
359
+ generate_random(nil, {}) # Use same logic for consistency
360
+ end
361
+
362
+ def generate_invalid(_rng, _args)
363
+ [nil, "", "INVALID", 12345].sample
364
+ end
365
+ end
366
+
367
+ FakeDataDSL.register_type(:ticket_number, TicketNumberType.new)
368
+
369
+ schema8 = FakeDataDSL.parse(<<~DSL)
370
+ SupportTicket:
371
+ ticket_number: ticket_number
372
+ subject: text(20..100)
373
+ priority: enum(low, medium, high, critical)
374
+ status: enum(open, in_progress, resolved, closed)
375
+ DSL
376
+
377
+ puts "Generated Support Tickets with Auto-Increment:"
378
+ 5.times do
379
+ ticket = schema8.generate
380
+ puts " #{ticket['ticket_number']}: #{ticket['subject'][0..30]}..."
381
+ end
382
+ puts
383
+
384
+ puts "=" * 70
385
+ puts "Custom types examples completed!"
386
+ puts "=" * 70
387
+