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,424 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ module FinanceBanking
6
+ # Money type
7
+ class Money < Base
8
+ # Generate a random money value
9
+ #
10
+ # Creates a hash with amount (2 decimal places) and currency code.
11
+ # Uses Faker::Currency.code if no currency specified.
12
+ #
13
+ # @param rng [Generator::RNG] random number generator
14
+ # @param args [Hash] type arguments
15
+ # @option args [Range, Hash] :range amount range (default: 0..10000)
16
+ # @option args [String] :currency currency code (default: random from Faker)
17
+ # @return [Hash] { "amount" => Float, "currency" => String }
18
+ #
19
+ # @example
20
+ # generate_random(rng, { range: 0..100, currency: "EUR" })
21
+ # # => { "amount" => 45.67, "currency" => "EUR" }
22
+ #
23
+ def generate_random(rng, context, args)
24
+ range = args[:range] || (0..10_000)
25
+ min = range.is_a?(Hash) ? (range[:min] || 0) : range.min
26
+ max = range.is_a?(Hash) ? (range[:max] || 10_000) : range.max
27
+
28
+ # Use provided currency or generate one using Faker
29
+ currency = args[:currency] || generate_currency(context)
30
+
31
+ # Generate amount and round to 2 decimal places (cents)
32
+ amount = (rng.rand * (max.to_f - min.to_f) + min.to_f).round(2)
33
+
34
+ {
35
+ "amount" => amount,
36
+ "currency" => currency
37
+ }
38
+ end
39
+
40
+ def generate_edge(rng, context, args)
41
+ {
42
+ "amount" => 0.0,
43
+ "currency" => "USD"
44
+ }
45
+ end
46
+
47
+ def generate_invalid(rng, context, args)
48
+ rng.sample([nil, "not money", [], {}, 123])
49
+ end
50
+
51
+ private
52
+
53
+ def generate_currency(context)
54
+ adapter = faker_adapter(context)
55
+ if adapter
56
+ adapter.currency_code
57
+
58
+ else
59
+ Faker::Currency.code
60
+ end
61
+
62
+ rescue StandardError
63
+ "USD"
64
+ end
65
+
66
+ end
67
+
68
+ # Currency type
69
+ class Currency < Base
70
+ # Fallback currency codes if Faker is unavailable
71
+ #
72
+ # ISO 4217 currency codes used when Faker cannot generate codes.
73
+ #
74
+ # @return [Array<String>] list of 3-letter currency codes
75
+ #
76
+ FALLBACK_CODES = %w[
77
+ USD EUR GBP JPY CNY INR BRL CAD AUD CHF
78
+ SEK NOK DKK NZD SGD HKD KRW MXN ZAR RUB
79
+ TRY PLN CZK HUF RON BGN HRK ILS AED SAR
80
+ THB MYR IDR PHP VND EGP NGN KES MAD PKR
81
+ ].freeze
82
+
83
+ # Generate a random currency code
84
+ #
85
+ # Uses Faker::Currency.code for realistic ISO 4217 codes.
86
+ #
87
+ # @param rng [Generator::RNG] random number generator
88
+ # @param args [Hash] type arguments (not used)
89
+ # @return [String] three-letter currency code
90
+ #
91
+ # @example
92
+ # generate_random(rng, {})
93
+ # # => "EUR"
94
+ #
95
+ def generate_random(rng, context, args)
96
+ adapter = faker_adapter(context)
97
+ if adapter
98
+ adapter.currency_code
99
+
100
+ else
101
+ Faker::Currency.code
102
+ end
103
+
104
+ rescue StandardError
105
+ rng.sample(FALLBACK_CODES)
106
+ end
107
+
108
+
109
+ def generate_edge(rng, context, args)
110
+ ["USD", "EUR", "GBP"].sample(random: rng.instance_variable_get(:@random))
111
+ end
112
+
113
+ def generate_invalid(rng, context, args)
114
+ [nil, "XX", "123", [], {}].sample(random: rng.instance_variable_get(:@random))
115
+ end
116
+ end
117
+
118
+ # CreditCard type
119
+ class CreditCard < Base
120
+ # Credit card type definitions
121
+ #
122
+ # Maps card type symbols to their specifications including
123
+ # valid prefixes and card number length.
124
+ #
125
+ # @return [Hash<Symbol, Hash>] card type to specification mapping
126
+ # @example
127
+ # CARD_TYPES[:visa] # => { prefix: [4], length: 16 }
128
+ # CARD_TYPES[:mastercard] # => { prefix: [51, 52, 53, 54, 55], length: 16 }
129
+ #
130
+ CARD_TYPES = {
131
+ visa: { prefix: [4], length: 16 },
132
+ mastercard: { prefix: [51, 52, 53, 54, 55], length: 16 },
133
+ amex: { prefix: [34, 37], length: 15 },
134
+ discover: { prefix: [6011, 65], length: 16 },
135
+ diners: { prefix: [30, 36, 38], length: 14 },
136
+ jcb: { prefix: [35], length: 16 }
137
+ }.freeze
138
+
139
+ # Generate random credit card number
140
+ #
141
+ # @param rng [Generator::RNG] random number generator
142
+ # @param context [Generator::Context] generation context
143
+ # @param args [Hash] type arguments
144
+ # @option args [Symbol, String] :type card type (:visa, :mastercard, :amex, etc.)
145
+ # @return [String] generated credit card number
146
+ #
147
+ def generate_random(rng, context, args)
148
+ type = (args[:type] || rng.sample(CARD_TYPES.keys)).to_sym
149
+ card_spec = CARD_TYPES[type] || CARD_TYPES[:visa]
150
+
151
+ # :nocov:
152
+ # The non-Array arm is unreachable: every CARD_TYPES entry (and the
153
+ # :visa default) declares an Array prefix, so card_spec[:prefix] is
154
+ # always an Array here.
155
+ prefix = card_spec[:prefix].is_a?(Array) ? rng.sample(card_spec[:prefix]) : card_spec[:prefix]
156
+ # :nocov:
157
+ length = card_spec[:length]
158
+
159
+ # Generate base number with prefix
160
+ base = prefix.to_s
161
+ remaining = length - base.length - 1 # -1 for check digit
162
+
163
+ # Fill with random digits
164
+ remaining.times { base += rng.int(0, 9).to_s }
165
+
166
+ # Calculate and append Luhn check digit
167
+ check_digit = calculate_luhn_check_digit(base)
168
+ "#{base}#{check_digit}"
169
+ end
170
+
171
+ def generate_edge(rng, context, args)
172
+ ["4111111111111111", "4000000000000002"].sample(random: rng.instance_variable_get(:@random))
173
+ end
174
+
175
+ def generate_invalid(rng, context, args)
176
+ rng.sample([nil, "1234", "invalid", [], {}])
177
+ end
178
+
179
+ private
180
+
181
+ def calculate_luhn_check_digit(number)
182
+ sum = 0
183
+ number.reverse.chars.each_with_index do |digit, index|
184
+ n = digit.to_i
185
+ n *= 2 if index.odd?
186
+ n -= 9 if n > 9
187
+ sum += n
188
+ end
189
+ (10 - (sum % 10)) % 10
190
+ end
191
+ end
192
+
193
+ # BankName type
194
+ class BankName < Base
195
+ # Generate random bank name
196
+ #
197
+ # @param rng [Generator::RNG] random number generator (not directly used)
198
+ # @param context [Generator::Context] generation context
199
+ # @param args [Hash] type arguments (not used)
200
+ # @return [String] generated bank name
201
+ #
202
+ def generate_random(rng, context, args)
203
+ adapter = faker_adapter(context)
204
+ if adapter
205
+ adapter.bank
206
+
207
+ else
208
+ Faker::Bank.name
209
+ end
210
+
211
+ rescue StandardError
212
+ rng.sample(["Chase Bank", "Bank of America", "Wells Fargo"])
213
+ end
214
+
215
+
216
+ def generate_edge(rng, context, args)
217
+ ["", "A"].sample(random: rng.instance_variable_get(:@random))
218
+ end
219
+
220
+ def generate_invalid(rng, context, args)
221
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
222
+ end
223
+ end
224
+
225
+ # AccountNumber type
226
+ class AccountNumber < Base
227
+ # Generate random account number
228
+ #
229
+ # @param rng [Generator::RNG] random number generator
230
+ # @param context [Generator::Context] generation context
231
+ # @param args [Hash] type arguments
232
+ # @option args [Integer] :length account number length (default: 8..12)
233
+ # @option args [String] :format format string (e.g., "%010d" for zero-padding)
234
+ # @return [String] generated account number
235
+ #
236
+ def generate_random(rng, context, args)
237
+ length = args[:length] || rng.int(8, 12)
238
+ format_str = args[:format]
239
+
240
+ # Generate number
241
+ max_value = 10**length - 1
242
+ value = rng.int(0, max_value)
243
+
244
+ # Format if specified
245
+ if format_str
246
+ format(format_str, value)
247
+ else
248
+ value.to_s.rjust(length, "0")
249
+ end
250
+ end
251
+
252
+ def generate_edge(rng, context, args)
253
+ ["00000000", "999999999999"].sample(random: rng.instance_variable_get(:@random))
254
+ end
255
+
256
+ def generate_invalid(rng, context, args)
257
+ [nil, "not a number", [], {}].sample(random: rng.instance_variable_get(:@random))
258
+ end
259
+ end
260
+
261
+ # Iban type
262
+ class Iban < Base
263
+ # IBAN format specifications by country
264
+ #
265
+ # Maps country codes to their IBAN format specifications including
266
+ # country code and total length.
267
+ #
268
+ # @return [Hash<Symbol, Hash>] country code to format mapping
269
+ # @example
270
+ # IBAN_FORMATS[:gb] # => { code: "GB", length: 22 }
271
+ # IBAN_FORMATS[:de] # => { code: "DE", length: 22 }
272
+ #
273
+ IBAN_FORMATS = {
274
+ gb: { code: "GB", length: 22 },
275
+ de: { code: "DE", length: 22 },
276
+ fr: { code: "FR", length: 27 },
277
+ it: { code: "IT", length: 27 },
278
+ es: { code: "ES", length: 24 },
279
+ nl: { code: "NL", length: 18 },
280
+ be: { code: "BE", length: 16 },
281
+ us: { code: "US", length: 22 } # Not standard, but sometimes used
282
+ }.freeze
283
+
284
+ # Generate random IBAN
285
+ #
286
+ # @param rng [Generator::RNG] random number generator
287
+ # @param context [Generator::Context] generation context
288
+ # @param args [Hash] type arguments
289
+ # @option args [Symbol, String] :country country code (:gb, :de, :fr, etc.)
290
+ # @return [String] generated IBAN
291
+ #
292
+ def generate_random(rng, context, args)
293
+ country = (args[:country] || :gb).to_sym
294
+ format = IBAN_FORMATS[country] || IBAN_FORMATS[:gb]
295
+
296
+ country_code = format[:code]
297
+ length = format[:length]
298
+
299
+ # Generate check digits (2 digits)
300
+ check_digits = format("%02d", rng.int(0, 99))
301
+
302
+ # Generate BBAN (Basic Bank Account Number) - alphanumeric
303
+ bban_length = length - 4 # -4 for country code and check digits
304
+ chars = ("A".."Z").to_a + ("0".."9").to_a
305
+ bban = bban_length.times.map { rng.sample(chars) }.join
306
+
307
+ "#{country_code}#{check_digits}#{bban}"
308
+ end
309
+
310
+ def generate_edge(rng, context, args)
311
+ ["GB00000000000000000000", "DE00000000000000000000"].sample(random: rng.instance_variable_get(:@random))
312
+ end
313
+
314
+ def generate_invalid(rng, context, args)
315
+ [nil, "invalid", "123", [], {}].sample(random: rng.instance_variable_get(:@random))
316
+ end
317
+ end
318
+
319
+ # BankRoutingNumber type
320
+ class BankRoutingNumber < Base
321
+ def generate_random(rng, context, args)
322
+ format("%09d", rng.int(100000000, 999999999))
323
+ end
324
+
325
+ def generate_edge(rng, context, args)
326
+ ["000000000", "999999999"].sample(random: rng.instance_variable_get(:@random))
327
+ end
328
+
329
+ def generate_invalid(rng, context, args)
330
+ [nil, "not a number", [], {}].sample(random: rng.instance_variable_get(:@random))
331
+ end
332
+ end
333
+
334
+ # BankSwiftBic type
335
+ class BankSwiftBic < Base
336
+ def generate_random(rng, context, args)
337
+ # Format: 4 letters (bank) + 2 letters (country) + 2 alphanumeric (location) + 3 alphanumeric (branch, optional)
338
+ bank = (0...4).map { rng.sample(('A'..'Z').to_a) }.join
339
+ country = rng.sample(["US", "GB", "FR", "DE", "JP", "CA", "AU"])
340
+ location = (0...2).map { rng.sample(('A'..'Z').to_a + ('0'..'9').to_a) }.join
341
+ branch = (0...3).map { rng.sample(('A'..'Z').to_a + ('0'..'9').to_a) }.join
342
+ "#{bank}#{country}#{location}#{branch}"
343
+ end
344
+
345
+ def generate_edge(rng, context, args)
346
+ ["AAAAUS00", "ZZZZZZ99"].sample(random: rng.instance_variable_get(:@random))
347
+ end
348
+
349
+ def generate_invalid(rng, context, args)
350
+ [nil, "invalid", "123", [], {}].sample(random: rng.instance_variable_get(:@random))
351
+ end
352
+ end
353
+
354
+ # BankLei type
355
+ class BankLei < Base
356
+ def generate_random(rng, context, args)
357
+ # Format: 20 alphanumeric characters
358
+ chars = ('A'..'Z').to_a + ('0'..'9').to_a
359
+ 20.times.map { rng.sample(chars) }.join
360
+ end
361
+
362
+ def generate_edge(rng, context, args)
363
+ ["00000000000000000000", "ZZZZZZZZZZZZZZZZZZZZ"].sample(random: rng.instance_variable_get(:@random))
364
+ end
365
+
366
+ def generate_invalid(rng, context, args)
367
+ [nil, "invalid", "123", [], {}].sample(random: rng.instance_variable_get(:@random))
368
+ end
369
+ end
370
+
371
+ # BankRiadCode type
372
+ class BankRiadCode < Base
373
+ def generate_random(rng, context, args)
374
+ # RIAD codes are typically 9 digits
375
+ format("%09d", rng.int(100000000, 999999999))
376
+ end
377
+
378
+ def generate_edge(rng, context, args)
379
+ ["000000000", "999999999"].sample(random: rng.instance_variable_get(:@random))
380
+ end
381
+
382
+ def generate_invalid(rng, context, args)
383
+ [nil, "not a number", [], {}].sample(random: rng.instance_variable_get(:@random))
384
+ end
385
+ end
386
+
387
+ # TransactionAmount type
388
+ class TransactionAmount < Base
389
+ # Generate random transaction amount
390
+ #
391
+ # @param rng [Generator::RNG] random number generator
392
+ # @param context [Generator::Context] generation context
393
+ # @param args [Hash] type arguments
394
+ # @option args [Range] :range amount range (e.g., 1.00..1000.00)
395
+ # @option args [Float] :min minimum amount (default: 0.01)
396
+ # @option args [Float] :max maximum amount (default: 10000.00)
397
+ # @option args [Integer] :precision decimal precision (default: 2)
398
+ # @return [Float] generated transaction amount
399
+ #
400
+ def generate_random(rng, context, args)
401
+ if args[:range].is_a?(Range)
402
+ min = args[:range].min
403
+ max = args[:range].max
404
+ else
405
+ min = args[:min] || 0.01
406
+ max = args[:max] || 10_000.00
407
+ end
408
+
409
+ precision = args[:precision] || 2
410
+ value = rng.rand * (max - min) + min
411
+ value.round(precision)
412
+ end
413
+
414
+ def generate_edge(rng, context, args)
415
+ [0.01, 0.0, 999999.99].sample(random: rng.instance_variable_get(:@random))
416
+ end
417
+
418
+ def generate_invalid(rng, context, args)
419
+ [nil, "not a number", -1.0, [], {}].sample(random: rng.instance_variable_get(:@random))
420
+ end
421
+ end
422
+ end
423
+ end
424
+ end
@@ -0,0 +1,24 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ module FinanceBanking
6
+ # CreditCardType type
7
+ class CreditCardType < Base
8
+ CARD_TYPES = ["Visa", "Mastercard", "American Express", "Discover", "JCB", "Diners Club"].freeze
9
+
10
+ def generate_random(rng, context, args)
11
+ rng.sample(CARD_TYPES)
12
+ end
13
+
14
+ def generate_edge(rng, context, args)
15
+ ["", "Visa", "Mastercard"].sample(random: rng.instance_variable_get(:@random))
16
+ end
17
+
18
+ def generate_invalid(rng, context, args)
19
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
20
+ end
21
+ end
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,93 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ module FinanceBanking
6
+ # Ssn type
7
+ class Ssn < Base
8
+ def generate_random(rng, context, args)
9
+ # Format: XXX-XX-XXXX
10
+ part1 = format("%03d", rng.int(100, 999))
11
+ part2 = format("%02d", rng.int(10, 99))
12
+ part3 = format("%04d", rng.int(1000, 9999))
13
+ "#{part1}-#{part2}-#{part3}"
14
+ end
15
+
16
+ def generate_edge(rng, context, args)
17
+ ["000-00-0000", "999-99-9999", "123-45-6789"].sample(random: rng.instance_variable_get(:@random))
18
+ end
19
+
20
+ def generate_invalid(rng, context, args)
21
+ [nil, "not a ssn", [], {}, "123456789"].sample(random: rng.instance_variable_get(:@random))
22
+ end
23
+ end
24
+
25
+ # Ein type
26
+ class Ein < Base
27
+ def generate_random(rng, context, args)
28
+ # Format: XX-XXXXXXX
29
+ part1 = format("%02d", rng.int(10, 99))
30
+ part2 = format("%07d", rng.int(1000000, 9999999))
31
+ "#{part1}-#{part2}"
32
+ end
33
+
34
+ def generate_edge(rng, context, args)
35
+ ["00-0000000", "99-9999999", "12-3456789"].sample(random: rng.instance_variable_get(:@random))
36
+ end
37
+
38
+ def generate_invalid(rng, context, args)
39
+ [nil, "not an ein", [], {}, "123456789"].sample(random: rng.instance_variable_get(:@random))
40
+ end
41
+ end
42
+
43
+ # NhsNumber type
44
+ class NhsNumber < Base
45
+ def generate_random(rng, context, args)
46
+ # NHS numbers are 10 digits
47
+ format("%010d", rng.int(1000000000, 9999999999))
48
+ end
49
+
50
+ def generate_edge(rng, context, args)
51
+ ["0000000000", "9999999999", "1234567890"].sample(random: rng.instance_variable_get(:@random))
52
+ end
53
+
54
+ def generate_invalid(rng, context, args)
55
+ [nil, "not a nhs", [], {}, "123456789"].sample(random: rng.instance_variable_get(:@random))
56
+ end
57
+ end
58
+
59
+ # MedicareBeneficiaryId type
60
+ class MedicareBeneficiaryId < Base
61
+ def generate_random(rng, context, args)
62
+ # Format: 1-2-3-4-5-6-7-8-9-10-11 (11 characters, alphanumeric)
63
+ chars = ('A'..'Z').to_a + ('0'..'9').to_a
64
+ 11.times.map { rng.sample(chars) }.join
65
+ end
66
+
67
+ def generate_edge(rng, context, args)
68
+ ["A" * 11, "0" * 11, "1A2B3C4D5E6"].sample(random: rng.instance_variable_get(:@random))
69
+ end
70
+
71
+ def generate_invalid(rng, context, args)
72
+ [nil, "too short", [], {}, "too long string here"].sample(random: rng.instance_variable_get(:@random))
73
+ end
74
+ end
75
+
76
+ # DunsNumber type
77
+ class DunsNumber < Base
78
+ def generate_random(rng, context, args)
79
+ # DUNS numbers are 9 digits, often formatted with dashes
80
+ format("%09d", rng.int(100000000, 999999999))
81
+ end
82
+
83
+ def generate_edge(rng, context, args)
84
+ ["000000000", "999999999", "123456789"].sample(random: rng.instance_variable_get(:@random))
85
+ end
86
+
87
+ def generate_invalid(rng, context, args)
88
+ [nil, "not a duns", [], {}, "12345678"].sample(random: rng.instance_variable_get(:@random))
89
+ end
90
+ end
91
+ end
92
+ end
93
+ end