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,532 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ module TextContent
6
+ # Paragraphs type
7
+ class Paragraphs < Base
8
+ def generate_random(rng, context, args)
9
+ min = args[:min] || 1
10
+ max = args[:max] || 3
11
+ count = rng.int(min, max)
12
+ # English-readable paragraphs — Faker::Hipster reads as English, unlike Latin Lorem.
13
+ Faker::Hipster.paragraphs(number: count).join("\n\n")
14
+
15
+ rescue StandardError
16
+ "Paragraph 1.\n\nParagraph 2.\n\nParagraph 3."
17
+ end
18
+
19
+
20
+ def generate_edge(rng, context, args)
21
+ ["", "A single paragraph."].sample(random: rng.instance_variable_get(:@random))
22
+ end
23
+
24
+ def generate_invalid(rng, context, args)
25
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
26
+ end
27
+ end
28
+
29
+ # Sentences type
30
+ class Sentences < Base
31
+ def generate_random(rng, context, args)
32
+ min = args[:min] || 1
33
+ max = args[:max] || 5
34
+ count = rng.int(min, max)
35
+ # English-readable sentences — Faker::Hipster reads as English, unlike Latin Lorem.
36
+ Faker::Hipster.sentences(number: count).join(" ")
37
+
38
+ rescue StandardError
39
+ "Sentence one. Sentence two. Sentence three."
40
+ end
41
+
42
+
43
+ def generate_edge(rng, context, args)
44
+ ["", "A single sentence."].sample(random: rng.instance_variable_get(:@random))
45
+ end
46
+
47
+ def generate_invalid(rng, context, args)
48
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
49
+ end
50
+ end
51
+
52
+ # Words type
53
+ class Words < Base
54
+ def generate_random(rng, context, args)
55
+ min = args[:min] || 3
56
+ max = args[:max] || 10
57
+ count = rng.int(min, max)
58
+ # English-readable words — Faker::Hipster reads as English, unlike Latin Lorem.
59
+ Faker::Hipster.words(number: count).join(" ")
60
+
61
+ rescue StandardError
62
+ rng.sample(["word one two", "test data here", "sample words"])
63
+ end
64
+
65
+
66
+ def generate_edge(rng, context, args)
67
+ ["", "a", "word"].sample(random: rng.instance_variable_get(:@random))
68
+ end
69
+
70
+ def generate_invalid(rng, context, args)
71
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
72
+ end
73
+ end
74
+
75
+ # Slug type
76
+ #
77
+ # Generates a URL-friendly slug — lowercase words joined by hyphens,
78
+ # e.g. "building-scalable-apis". Deterministic by seed.
79
+ #
80
+ # @example Generate a slug
81
+ # slug_type = Slug.new
82
+ # slug_type.generate_random(rng, context, {})
83
+ # # => "building-scalable-apis"
84
+ #
85
+ class Slug < Base
86
+ # Word pool used to assemble slugs (all lowercase, hyphen-safe)
87
+ #
88
+ # @return [Array<String>] candidate slug words
89
+ #
90
+ WORDS = %w[
91
+ building scalable apis modern web design fast simple guide
92
+ getting started with data driven cloud native rust ruby
93
+ best practices performance tuning real world examples
94
+ ].freeze
95
+
96
+ # Generate a random slug
97
+ #
98
+ # @param rng [Generator::RNG] random number generator
99
+ # @param context [Generator::Context] generation context
100
+ # @param args [Hash] type arguments
101
+ # @option args [Integer] :min minimum word count (default: 3)
102
+ # @option args [Integer] :max maximum word count (default: 5)
103
+ # @return [String] generated slug
104
+ #
105
+ def generate_random(rng, context, args)
106
+ min = args[:min] || 3
107
+ max = args[:max] || 5
108
+ count = rng.int(min, max)
109
+ count.times.map { rng.sample(WORDS) }.join("-")
110
+ end
111
+
112
+ def generate_edge(rng, context, args)
113
+ ["", "a", "post"].sample(random: rng.instance_variable_get(:@random))
114
+ end
115
+
116
+ def generate_invalid(rng, context, args)
117
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
118
+ end
119
+ end
120
+
121
+ # Message text type for chat/DM messages
122
+ #
123
+ # Generates realistic chat message text using Faker.
124
+ # Useful for simulating direct messages, chat conversations, comments.
125
+ #
126
+ # @example Generate message text
127
+ # message_type = MessageText.new
128
+ # message_type.generate_random(rng, context, {})
129
+ # # => "Hello! I need help with my order."
130
+ #
131
+ class MessageText < Base
132
+
133
+ # English sample messages
134
+ SAMPLE_MESSAGES_EN = [
135
+ "Hello! I need help with my order.",
136
+ "Hi, can you help me?",
137
+ "Thanks for reaching out!",
138
+ "I have a question about my account.",
139
+ "When will my order arrive?",
140
+ "Great, thank you so much!",
141
+ "Can I get more information?",
142
+ "Is this still available?",
143
+ "Perfect, that's what I needed!",
144
+ "Could you please check this for me?",
145
+ "I'm having trouble with my login.",
146
+ "Thanks for your quick response!",
147
+ "Just wanted to follow up on my request.",
148
+ "Appreciate your help! 🙏",
149
+ "Hi there! 👋",
150
+ "Got it, thanks!",
151
+ "Looking forward to hearing from you.",
152
+ "That sounds good to me.",
153
+ "No problem at all!",
154
+ "Let me know if you need anything else."
155
+ ].freeze
156
+
157
+ # Arabic sample messages
158
+ SAMPLE_MESSAGES_AR = [
159
+ "مرحباً! أحتاج مساعدة في طلبي.",
160
+ "أهلاً، هل يمكنك مساعدتي؟",
161
+ "شكراً على التواصل!",
162
+ "لدي سؤال عن حسابي.",
163
+ "متى سيصل طلبي؟",
164
+ "رائع، شكراً جزيلاً!",
165
+ "هل يمكنني الحصول على مزيد من المعلومات؟",
166
+ "هل هذا لا يزال متاحاً؟",
167
+ "ممتاز، هذا ما أحتاجه!",
168
+ "هل يمكنك التحقق من هذا من فضلك؟",
169
+ "أواجه مشكلة في تسجيل الدخول.",
170
+ "شكراً على ردك السريع!",
171
+ "أردت المتابعة بخصوص طلبي.",
172
+ "أقدر مساعدتك! 🙏",
173
+ "أهلاً! 👋",
174
+ "فهمت، شكراً!",
175
+ "في انتظار ردك.",
176
+ "يبدو جيداً بالنسبة لي.",
177
+ "لا مشكلة على الإطلاق!",
178
+ "أخبرني إذا احتجت أي شيء آخر."
179
+ ].freeze
180
+
181
+ # Spanish sample messages
182
+ SAMPLE_MESSAGES_ES = [
183
+ "¡Hola! Necesito ayuda con mi pedido.",
184
+ "Hola, ¿puedes ayudarme?",
185
+ "¡Gracias por contactarnos!",
186
+ "Tengo una pregunta sobre mi cuenta.",
187
+ "¿Cuándo llegará mi pedido?",
188
+ "¡Genial, muchas gracias!",
189
+ "¿Puedo obtener más información?",
190
+ "¿Esto todavía está disponible?",
191
+ "¡Perfecto, eso es lo que necesitaba!",
192
+ "¿Podrías verificar esto por mí?",
193
+ "Tengo problemas con mi inicio de sesión.",
194
+ "¡Gracias por tu rápida respuesta!",
195
+ "Solo quería dar seguimiento a mi solicitud.",
196
+ "¡Aprecio tu ayuda! 🙏",
197
+ "¡Hola! 👋",
198
+ "Entendido, ¡gracias!",
199
+ "Espero tu respuesta.",
200
+ "Me parece bien.",
201
+ "¡No hay problema!",
202
+ "Avísame si necesitas algo más."
203
+ ].freeze
204
+
205
+ # French sample messages
206
+ SAMPLE_MESSAGES_FR = [
207
+ "Bonjour ! J'ai besoin d'aide avec ma commande.",
208
+ "Salut, peux-tu m'aider ?",
209
+ "Merci de nous avoir contactés !",
210
+ "J'ai une question sur mon compte.",
211
+ "Quand ma commande arrivera-t-elle ?",
212
+ "Super, merci beaucoup !",
213
+ "Puis-je avoir plus d'informations ?",
214
+ "Est-ce toujours disponible ?",
215
+ "Parfait, c'est ce qu'il me fallait !",
216
+ "Pourriez-vous vérifier cela pour moi ?",
217
+ "J'ai des problèmes de connexion.",
218
+ "Merci pour votre réponse rapide !",
219
+ "Je voulais faire un suivi de ma demande.",
220
+ "J'apprécie votre aide ! 🙏",
221
+ "Salut ! 👋",
222
+ "Compris, merci !",
223
+ "J'attends votre réponse.",
224
+ "Ça me convient.",
225
+ "Pas de problème !",
226
+ "Faites-moi savoir si vous avez besoin d'autre chose."
227
+ ].freeze
228
+
229
+ # Locale to messages mapping
230
+ MESSAGES_BY_LOCALE = {
231
+ en: SAMPLE_MESSAGES_EN,
232
+ ar: SAMPLE_MESSAGES_AR,
233
+ es: SAMPLE_MESSAGES_ES,
234
+ fr: SAMPLE_MESSAGES_FR
235
+ }.freeze
236
+
237
+ # Keep backward compatibility
238
+ SAMPLE_MESSAGES = SAMPLE_MESSAGES_EN
239
+
240
+ # Faker locale mapping
241
+ FAKER_LOCALES = {
242
+ en: :en,
243
+ ar: :ar,
244
+ es: :es,
245
+ fr: :fr,
246
+ de: :de,
247
+ it: :it,
248
+ pt: :pt,
249
+ ru: :ru,
250
+ ja: :ja,
251
+ ko: :ko,
252
+ zh: :'zh-CN'
253
+ }.freeze
254
+
255
+ def generate_random(rng, context, args)
256
+ # Check for mixed locales BEFORE extracting locale
257
+ locale_arg = args[:locale] || args[:language] || args[:lang]
258
+
259
+ # If mixed locales requested (e.g., locale: "en,ar" or locales: ["en", "ar"])
260
+ if args[:locales] || args[:mixed] || (locale_arg.is_a?(String) && locale_arg.include?(","))
261
+ return generate_mixed_locale(rng, args)
262
+ end
263
+
264
+ locale = extract_locale(args)
265
+
266
+ # Single locale
267
+ messages = MESSAGES_BY_LOCALE[locale] || SAMPLE_MESSAGES_EN
268
+ faker_locale = FAKER_LOCALES[locale] || :en
269
+
270
+ # Use Faker with locale for variety (50% Faker, 50% static)
271
+ case rng.int(0, 9)
272
+ when 0..4
273
+ # Use Faker with the appropriate locale
274
+ generate_faker_message(rng, faker_locale)
275
+ else
276
+ # Use static messages for guaranteed quality
277
+ messages[rng.int(0, messages.length - 1)]
278
+ end
279
+
280
+ rescue StandardError
281
+ messages = MESSAGES_BY_LOCALE[locale] || SAMPLE_MESSAGES_EN
282
+ messages[rng.int(0, messages.length - 1)]
283
+ end
284
+
285
+ def generate_faker_message(rng, faker_locale)
286
+ original_locale = Faker::Config.locale
287
+ begin
288
+ Faker::Config.locale = faker_locale
289
+
290
+ # For Arabic, use different approach since Lorem doesn't support Arabic
291
+ if faker_locale == :ar
292
+ generate_faker_arabic_message(rng)
293
+ else
294
+ generate_faker_latin_message(rng)
295
+ end
296
+ ensure
297
+ Faker::Config.locale = original_locale
298
+ end
299
+ end
300
+
301
+ def generate_faker_arabic_message(rng)
302
+ # Arabic-specific message generation using supported Faker modules
303
+ templates = [
304
+ -> { "مرحباً #{Faker::Name.first_name}، كيف حالك؟" },
305
+ -> { "أهلاً بك في #{Faker::Company.name}" },
306
+ -> { "شكراً لتواصلك مع #{Faker::Company.name}" },
307
+ -> { "مرحباً، أنا #{Faker::Name.name}" },
308
+ -> { "تحياتي من #{Faker::Address.city}" },
309
+ -> { "#{Faker::Name.first_name} يرحب بك" },
310
+ -> { "نتطلع للعمل معك في #{Faker::Company.name}" },
311
+ -> { "#{Faker::Company.name} في خدمتك" },
312
+ -> { "أهلاً من #{Faker::Address.city}!" },
313
+ -> { "تواصل معنا في #{Faker::Company.name}" },
314
+ ]
315
+ templates[rng.int(0, templates.length - 1)].call
316
+ end
317
+
318
+ def generate_faker_latin_message(rng)
319
+ case rng.int(0, 5)
320
+ when 0
321
+ Faker::Lorem.sentence(word_count: rng.int(4, 10))
322
+ when 1
323
+ Faker::Lorem.question(word_count: rng.int(4, 8))
324
+ when 2
325
+ # Company catch phrase - works in most locales
326
+ Faker::Company.catch_phrase
327
+ when 3
328
+ # Try greeting - falls back gracefully
329
+ begin
330
+ Faker::Greeting.hi
331
+ rescue
332
+ Faker::Lorem.sentence(word_count: rng.int(3, 6))
333
+ end
334
+ when 4
335
+ # Quote - works in most locales
336
+ begin
337
+ Faker::Quote.famous_last_words
338
+ rescue
339
+ Faker::Lorem.sentence(word_count: rng.int(5, 10))
340
+ end
341
+ else
342
+ Faker::Lorem.sentences(number: 1).first
343
+ end
344
+ end
345
+
346
+ private
347
+
348
+ def extract_locale(args)
349
+ locale_arg = args[:locale] || args[:language] || args[:lang] || :en
350
+ locale_str = locale_arg.to_s.downcase.strip
351
+
352
+ case locale_str
353
+ when "en", "english" then :en
354
+ when "ar", "arabic" then :ar
355
+ when "es", "spanish" then :es
356
+ when "fr", "french" then :fr
357
+ else
358
+ locale_str.to_sym
359
+ end
360
+ end
361
+
362
+ def generate_mixed_locale(rng, args)
363
+ # Parse locales from various formats
364
+ locales = if args[:locales].is_a?(Array)
365
+ args[:locales].map { |l| normalize_locale(l) }
366
+ elsif args[:mixed].is_a?(Array)
367
+ args[:mixed].map { |l| normalize_locale(l) }
368
+ elsif args[:locale].is_a?(String) && args[:locale].include?(",")
369
+ args[:locale].split(",").map { |l| normalize_locale(l.strip) }
370
+ else
371
+ [:en, :ar] # Default mixed
372
+ end
373
+
374
+ # Pick a random locale
375
+ selected_locale = locales[rng.int(0, locales.length - 1)]
376
+ messages = MESSAGES_BY_LOCALE[selected_locale] || SAMPLE_MESSAGES_EN
377
+ messages[rng.int(0, messages.length - 1)]
378
+ end
379
+
380
+ def normalize_locale(locale)
381
+ case locale.to_s.downcase
382
+ when "en", "english" then :en
383
+ when "ar", "arabic" then :ar
384
+ when "es", "spanish" then :es
385
+ when "fr", "french" then :fr
386
+ else
387
+ locale.to_s.to_sym
388
+ end
389
+ end
390
+
391
+ public
392
+
393
+
394
+ def generate_edge(rng, context, args)
395
+ rng.sample([
396
+ "", # Empty message
397
+ "👋", # Single emoji
398
+ "a", # Single character
399
+ "OK", # Very short
400
+ "!" * 100, # Long repeated chars
401
+ "Hello 你好 مرحبا" # Unicode/multilingual
402
+ ])
403
+ end
404
+
405
+ def generate_invalid(rng, context, args)
406
+ rng.sample([
407
+ nil,
408
+ 123,
409
+ [],
410
+ {},
411
+ true
412
+ ])
413
+ end
414
+ end
415
+
416
+ # Empty array type for entities
417
+ #
418
+ # Always returns an empty array. Useful for API responses
419
+ # that have optional array fields.
420
+ #
421
+ class EmptyArray < Base
422
+ def generate_random(_rng, _context, _args)
423
+ []
424
+ end
425
+
426
+ def generate_edge(_rng, _context, _args)
427
+ []
428
+ end
429
+
430
+ def generate_invalid(rng, _context, _args)
431
+ rng.sample([nil, "", {}, "[]"])
432
+ end
433
+ end
434
+
435
+ # Hashtag text (without # prefix)
436
+ #
437
+ # Generates hashtag-style text without the # symbol.
438
+ #
439
+ class HashtagText < Base
440
+
441
+ HASHTAGS = %w[
442
+ help support question order shipping delivery customer service
443
+ tech coding ruby programming api dev javascript python
444
+ love awesome amazing cool great best new trending viral
445
+ business startup entrepreneur innovation growth success
446
+ ].freeze
447
+
448
+
449
+ def generate_random(rng, context, args)
450
+ adapter = faker_adapter(context)
451
+ if adapter
452
+ adapter.fetch("internet.slug").gsub("-", "")
453
+
454
+ else
455
+ HASHTAGS[rng.int(0, HASHTAGS.length - 1)]
456
+ end
457
+
458
+ rescue StandardError
459
+ HASHTAGS[rng.int(0, HASHTAGS.length - 1)]
460
+ end
461
+
462
+
463
+ def generate_edge(rng, context, args)
464
+ rng.sample(["", "a", "x" * 50])
465
+ end
466
+
467
+ def generate_invalid(rng, context, args)
468
+ rng.sample([nil, 123, [], "#invalid"])
469
+ end
470
+ end
471
+
472
+ # Twitter/social handle (without @ prefix)
473
+ #
474
+ # Generates username-style handles for social media.
475
+ #
476
+ class SocialHandle < Base
477
+ def generate_random(rng, context, args)
478
+ adapter = faker_adapter(context)
479
+ if adapter
480
+ adapter.fetch("internet.username")
481
+
482
+ else
483
+ Faker::Internet.username(specifier: 5..15)
484
+ end
485
+
486
+ rescue StandardError
487
+ "user_#{rng.int(1000, 99999)}"
488
+ end
489
+
490
+
491
+ def generate_edge(rng, context, args)
492
+ rng.sample(["", "a", "user_" + ("x" * 50)])
493
+ end
494
+
495
+ def generate_invalid(rng, context, args)
496
+ rng.sample([nil, 123, [], "@invalid"])
497
+ end
498
+ end
499
+
500
+ # Indices pair for entity positions
501
+ #
502
+ # Generates [start, end] pairs for text entity positions.
503
+ #
504
+ class IndicesPair < Base
505
+ def generate_random(rng, context, args)
506
+ start = rng.int(0, 100)
507
+ length = rng.int(5, 30)
508
+ [start, start + length]
509
+ end
510
+
511
+ def generate_edge(rng, context, args)
512
+ rng.sample([
513
+ [0, 0], # Zero length
514
+ [0, 1], # Single char
515
+ [0, 280], # Full tweet length
516
+ [100, 105] # End of text
517
+ ])
518
+ end
519
+
520
+ def generate_invalid(rng, context, args)
521
+ rng.sample([
522
+ nil,
523
+ [],
524
+ [10, 5], # End before start
525
+ [-1, 10], # Negative start
526
+ "invalid"
527
+ ])
528
+ end
529
+ end
530
+ end
531
+ end
532
+ end
@@ -0,0 +1,150 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ module Types
5
+ module Travel
6
+ # Airline type
7
+ class Airline < Base
8
+ # Generate random airline name
9
+ #
10
+ # @param rng [Generator::RNG] random number generator (not directly used)
11
+ # @param context [Generator::Context] generation context
12
+ # @param args [Hash] type arguments (not used)
13
+ # @return [String] generated airline name
14
+ #
15
+ def generate_random(rng, context, args)
16
+ adapter = faker_adapter(context)
17
+ if adapter
18
+ adapter.airline
19
+
20
+ else
21
+ Faker::Airline.name
22
+ end
23
+
24
+ rescue StandardError
25
+ rng.sample(["American Airlines", "United Airlines", "Delta Air Lines"])
26
+ end
27
+
28
+
29
+ def generate_edge(rng, context, args)
30
+ ["", "A", "Very Long Airline Name That Exceeds Normal Limits"].sample(random: rng.instance_variable_get(:@random))
31
+ end
32
+
33
+ def generate_invalid(rng, context, args)
34
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
35
+ end
36
+ end
37
+
38
+ # AirportCode type
39
+ class AirportCode < Base
40
+ # Common IATA airport codes
41
+ #
42
+ # List of major international airport codes.
43
+ #
44
+ # @return [Array<String>] list of 3-letter airport codes
45
+ #
46
+ AIRPORT_CODES = [
47
+ "JFK", "LAX", "LHR", "CDG", "DXB", "SIN", "HKG", "NRT",
48
+ "FRA", "AMS", "MAD", "BCN", "FCO", "MIA", "ORD", "ATL",
49
+ "DFW", "DEN", "SFO", "SEA", "BOS", "IAD", "IAH", "MSP"
50
+ ].freeze
51
+
52
+ # Generate random airport code
53
+ #
54
+ # @param rng [Generator::RNG] random number generator
55
+ # @param context [Generator::Context] generation context
56
+ # @param args [Hash] type arguments (not used)
57
+ # @return [String] generated airport code
58
+ #
59
+ def generate_random(rng, context, args)
60
+ rng.sample(AIRPORT_CODES)
61
+ end
62
+
63
+ def generate_edge(rng, context, args)
64
+ ["", "JFK", "LAX"].sample(random: rng.instance_variable_get(:@random))
65
+ end
66
+
67
+ def generate_invalid(rng, context, args)
68
+ [nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
69
+ end
70
+ end
71
+
72
+ # FlightNumber type
73
+ class FlightNumber < Base
74
+ # Common IATA airline codes
75
+ #
76
+ # Two-letter codes for major airlines.
77
+ #
78
+ # @return [Array<String>] list of airline codes
79
+ #
80
+ AIRLINE_CODES = ["AA", "UA", "DL", "SW", "LH", "BA", "AF", "EK", "SQ", "JL"].freeze
81
+
82
+ # Generate random flight number
83
+ #
84
+ # @param rng [Generator::RNG] random number generator
85
+ # @param context [Generator::Context] generation context
86
+ # @param args [Hash] type arguments
87
+ # @option args [String] :airline airline code (default: random)
88
+ # @option args [Integer] :length flight number length (default: 3..4 digits)
89
+ # @return [String] generated flight number
90
+ #
91
+ def generate_random(rng, context, args)
92
+ airline = args[:airline] || rng.sample(AIRLINE_CODES)
93
+ length = args[:length] || rng.int(3, 4)
94
+
95
+ # Generate flight number digits
96
+ max_value = 10**length - 1
97
+ number = format("%0#{length}d", rng.int(1, max_value))
98
+
99
+ "#{airline}#{number}"
100
+ end
101
+
102
+ def generate_edge(rng, context, args)
103
+ ["AA000", "AA9999", "ZZ000"].sample(random: rng.instance_variable_get(:@random))
104
+ end
105
+
106
+ def generate_invalid(rng, context, args)
107
+ [nil, "not a flight", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
108
+ end
109
+ end
110
+
111
+ # SeatNumber type
112
+ class SeatNumber < Base
113
+ # Default aircraft seat letters
114
+ #
115
+ # Standard 6-seat configuration (A-F) for economy class.
116
+ #
117
+ # @return [Array<String>] list of seat letters
118
+ #
119
+ DEFAULT_SEATS = ["A", "B", "C", "D", "E", "F"].freeze
120
+
121
+ # Generate random seat number
122
+ #
123
+ # @param rng [Generator::RNG] random number generator
124
+ # @param context [Generator::Context] generation context
125
+ # @param args [Hash] type arguments
126
+ # @option args [Integer] :rows number of rows (default: 1..30)
127
+ # @option args [Array<String>] :seats seat letters (default: A-F)
128
+ # @return [String] generated seat number
129
+ #
130
+ def generate_random(rng, context, args)
131
+ max_rows = args[:rows] || 30
132
+ seats = args[:seats] || DEFAULT_SEATS
133
+
134
+ row = rng.int(1, max_rows)
135
+ seat = rng.sample(seats)
136
+
137
+ "#{row}#{seat}"
138
+ end
139
+
140
+ def generate_edge(rng, context, args)
141
+ ["1A", "30F", "1B"].sample(random: rng.instance_variable_get(:@random))
142
+ end
143
+
144
+ def generate_invalid(rng, context, args)
145
+ [nil, "not a seat", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
146
+ end
147
+ end
148
+ end
149
+ end
150
+ end