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.
- checksums.yaml +7 -0
- data/.rspec +4 -0
- data/.rubocop.yml +40 -0
- data/.yardopts +18 -0
- data/ACCESS_DOCS.md +116 -0
- data/ADVANCED_FEATURES_SUMMARY.md +245 -0
- data/CHANGELOG.md +498 -0
- data/CODE_OF_CONDUCT.md +122 -0
- data/CONTRIBUTING.md +307 -0
- data/DOCUMENTATION.md +204 -0
- data/DOCUMENTATION_GUIDE.md +194 -0
- data/LICENSE +22 -0
- data/README.md +1458 -0
- data/Rakefile +128 -0
- data/benchmark/README.md +591 -0
- data/benchmark/batch_generation.rb +63 -0
- data/benchmark/engine_comparison.rb +295 -0
- data/benchmark/single_record.rb +108 -0
- data/benchmark/streaming.rb +74 -0
- data/docs/ACTIVERECORD_INFERENCE.md +524 -0
- data/docs/API_SERVER.md +278 -0
- data/docs/CONFIG_FILE.md +315 -0
- data/docs/DATA_CONTRACTS.md +312 -0
- data/docs/ENHANCED_REPL.md +304 -0
- data/docs/FACTORY_BOT.md +271 -0
- data/docs/GITHUB_ACTION.md +420 -0
- data/docs/GRAPHQL_EXPORT.md +303 -0
- data/docs/GRAPHQL_FEDERATION.md +651 -0
- data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
- data/docs/LSP.md +189 -0
- data/docs/MIGRATION_GENERATOR.md +547 -0
- data/docs/MOCK_SERVER.md +280 -0
- data/docs/NATIVE_ENGINE.md +298 -0
- data/docs/OPENAPI_EXPORT.md +252 -0
- data/docs/PERFORMANCE_MODE.md +228 -0
- data/docs/PERSONAS.md +547 -0
- data/docs/PROPERTY_TESTING.md +284 -0
- data/docs/PROTOBUF_EXPORT.md +276 -0
- data/docs/QUALITY_METRICS.md +555 -0
- data/docs/QUICK_REFERENCE.md +88 -0
- data/docs/RAILS_ENGINE.md +404 -0
- data/docs/RAILS_INTEGRATION.md +454 -0
- data/docs/README.md +175 -0
- data/docs/REPL_QUICK_REFERENCE.md +149 -0
- data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
- data/docs/SCENARIOS.md +576 -0
- data/docs/SECURITY_TESTING.md +318 -0
- data/docs/SNAPSHOT_TESTING.md +308 -0
- data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
- data/docs/TERRAFORM_EXPORT.md +447 -0
- data/docs/TIME_TRAVEL.md +507 -0
- data/docs/USAGE_GUIDE.md +980 -0
- data/docs/WEBHOOK_SIMULATOR.md +579 -0
- data/docs/advanced_features.md +183 -0
- data/docs/api_reference.md +153 -0
- data/docs/behaviors.md +138 -0
- data/docs/best_practices.md +191 -0
- data/docs/dsl_reference.md +172 -0
- data/docs/examples.md +162 -0
- data/docs/generation_modes.md +125 -0
- data/docs/getting_started.md +92 -0
- data/docs/index.html +177 -0
- data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
- data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
- data/docs/troubleshooting.md +215 -0
- data/docs/type_reference.md +224 -0
- data/examples/hostile_mode_demo.rb +64 -0
- data/exe/synthra +6 -0
- data/ext/synthra_native/Cargo.lock +972 -0
- data/ext/synthra_native/Cargo.toml +46 -0
- data/ext/synthra_native/extconf.rb +41 -0
- data/ext/synthra_native/src/generator.rs +397 -0
- data/ext/synthra_native/src/lib.rs +229 -0
- data/ext/synthra_native/src/types.rs +1485 -0
- data/lib/generators/synthra/install_generator.rb +86 -0
- data/lib/generators/synthra/templates/api_response.dsl +15 -0
- data/lib/generators/synthra/templates/fake_data.rake +116 -0
- data/lib/generators/synthra/templates/synthra.yml +31 -0
- data/lib/generators/synthra/templates/synthra_support.rb +19 -0
- data/lib/generators/synthra/templates/user.dsl +11 -0
- data/lib/synthra/activerecord_inference.rb +394 -0
- data/lib/synthra/api.rb +381 -0
- data/lib/synthra/api_server.rb +718 -0
- data/lib/synthra/behaviors/applicator.rb +165 -0
- data/lib/synthra/behaviors/base.rb +155 -0
- data/lib/synthra/behaviors/close_connection.rb +60 -0
- data/lib/synthra/behaviors/deprecated.rb +100 -0
- data/lib/synthra/behaviors/failure.rb +69 -0
- data/lib/synthra/behaviors/latency.rb +103 -0
- data/lib/synthra/behaviors/partial_data.rb +83 -0
- data/lib/synthra/behaviors/randomize_order.rb +74 -0
- data/lib/synthra/behaviors/registry.rb +238 -0
- data/lib/synthra/behaviors/simulate_error.rb +79 -0
- data/lib/synthra/cli/commands/base.rb +105 -0
- data/lib/synthra/cli/commands/diff.rb +227 -0
- data/lib/synthra/cli/commands/docs.rb +67 -0
- data/lib/synthra/cli/commands/export.rb +150 -0
- data/lib/synthra/cli/commands/generate.rb +72 -0
- data/lib/synthra/cli/commands/import.rb +80 -0
- data/lib/synthra/cli/commands/lint.rb +53 -0
- data/lib/synthra/cli/commands/live.rb +57 -0
- data/lib/synthra/cli/commands/seed.rb +99 -0
- data/lib/synthra/cli/commands/validate.rb +28 -0
- data/lib/synthra/cli.rb +2471 -0
- data/lib/synthra/config_file.rb +129 -0
- data/lib/synthra/configuration.rb +281 -0
- data/lib/synthra/contracts_registry.rb +408 -0
- data/lib/synthra/database_seeder.rb +268 -0
- data/lib/synthra/deterministic_ids.rb +218 -0
- data/lib/synthra/documentation_generator.rb +414 -0
- data/lib/synthra/engine.rb +251 -0
- data/lib/synthra/errors.rb +1169 -0
- data/lib/synthra/export/base.rb +85 -0
- data/lib/synthra/export/csv.rb +101 -0
- data/lib/synthra/export/graphql.rb +266 -0
- data/lib/synthra/export/graphql_federation.rb +377 -0
- data/lib/synthra/export/graphviz.rb +258 -0
- data/lib/synthra/export/javascript.rb +327 -0
- data/lib/synthra/export/json_data.rb +61 -0
- data/lib/synthra/export/json_schema.rb +290 -0
- data/lib/synthra/export/openapi.rb +514 -0
- data/lib/synthra/export/protobuf.rb +483 -0
- data/lib/synthra/export/python.rb +560 -0
- data/lib/synthra/export/sql.rb +381 -0
- data/lib/synthra/export/sql_insert.rb +152 -0
- data/lib/synthra/export/terraform.rb +501 -0
- data/lib/synthra/export/type_mapping.rb +389 -0
- data/lib/synthra/export/typescript.rb +300 -0
- data/lib/synthra/export/xml_data.rb +104 -0
- data/lib/synthra/export/yaml_data.rb +56 -0
- data/lib/synthra/export.rb +404 -0
- data/lib/synthra/factory_bot_integration.rb +157 -0
- data/lib/synthra/field.rb +440 -0
- data/lib/synthra/functions/registry.rb +104 -0
- data/lib/synthra/generator/context.rb +372 -0
- data/lib/synthra/generator/engine.rb +336 -0
- data/lib/synthra/generator/faker_adapter.rb +425 -0
- data/lib/synthra/generator/modes.rb +444 -0
- data/lib/synthra/generator/resolver.rb +256 -0
- data/lib/synthra/generator/rng.rb +273 -0
- data/lib/synthra/generator/streamer.rb +63 -0
- data/lib/synthra/generator/uniqueness.rb +118 -0
- data/lib/synthra/initializer.rb +97 -0
- data/lib/synthra/limits.rb +279 -0
- data/lib/synthra/live_preview.rb +518 -0
- data/lib/synthra/loader_config.rb +78 -0
- data/lib/synthra/lsp/server.rb +689 -0
- data/lib/synthra/migration_generator.rb +301 -0
- data/lib/synthra/mixin.rb +174 -0
- data/lib/synthra/mock_server.rb +458 -0
- data/lib/synthra/native_engine.rb +304 -0
- data/lib/synthra/openapi_importer.rb +228 -0
- data/lib/synthra/output/json_formatter.rb +40 -0
- data/lib/synthra/output/ndjson_formatter.rb +55 -0
- data/lib/synthra/parser/ast.rb +1287 -0
- data/lib/synthra/parser/lexer.rb +1152 -0
- data/lib/synthra/parser/parser.rb +1664 -0
- data/lib/synthra/parser/tokens.rb +461 -0
- data/lib/synthra/performance_mode.rb +364 -0
- data/lib/synthra/personas.rb +397 -0
- data/lib/synthra/property_testing.rb +172 -0
- data/lib/synthra/quality_metrics.rb +405 -0
- data/lib/synthra/rails_test_helper.rb +248 -0
- data/lib/synthra/registry.rb +533 -0
- data/lib/synthra/relationships.rb +193 -0
- data/lib/synthra/repl/enhanced_repl.rb +607 -0
- data/lib/synthra/repl/formatter.rb +191 -0
- data/lib/synthra/scenarios.rb +423 -0
- data/lib/synthra/schema.rb +605 -0
- data/lib/synthra/schema_inheritance.rb +104 -0
- data/lib/synthra/schema_versioning.rb +212 -0
- data/lib/synthra/snapshot_testing.rb +199 -0
- data/lib/synthra/time_travel.rb +338 -0
- data/lib/synthra/type_definitions.rb +274 -0
- data/lib/synthra/types/address_location/addresses.rb +125 -0
- data/lib/synthra/types/address_location/airports.rb +200 -0
- data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
- data/lib/synthra/types/address_location/locations.rb +406 -0
- data/lib/synthra/types/base.rb +48 -0
- data/lib/synthra/types/commerce_products/commerce.rb +119 -0
- data/lib/synthra/types/commerce_products/companies.rb +71 -0
- data/lib/synthra/types/commerce_products/construction.rb +126 -0
- data/lib/synthra/types/commerce_products/products.rb +205 -0
- data/lib/synthra/types/core/collections.rb +513 -0
- data/lib/synthra/types/core/defaults.rb +76 -0
- data/lib/synthra/types/core/enums.rb +102 -0
- data/lib/synthra/types/core/identifiers.rb +445 -0
- data/lib/synthra/types/core/primitives.rb +586 -0
- data/lib/synthra/types/core/references.rb +466 -0
- data/lib/synthra/types/core/sequences.rb +304 -0
- data/lib/synthra/types/crypto/crypto.rb +162 -0
- data/lib/synthra/types/date_time/dates.rb +387 -0
- data/lib/synthra/types/finance_banking/banking.rb +424 -0
- data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
- data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
- data/lib/synthra/types/formula.rb +184 -0
- data/lib/synthra/types/health_medical/medical.rb +150 -0
- data/lib/synthra/types/hostile_payloads.rb +122 -0
- data/lib/synthra/types/json_array.rb +51 -0
- data/lib/synthra/types/media_entertainment/media.rb +62 -0
- data/lib/synthra/types/naughty_string.rb +30 -0
- data/lib/synthra/types/personal_names/chinese.rb +41 -0
- data/lib/synthra/types/personal_names/identifiers.rb +187 -0
- data/lib/synthra/types/personal_names/names.rb +246 -0
- data/lib/synthra/types/personal_names/national_id.rb +89 -0
- data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
- data/lib/synthra/types/regex.rb +248 -0
- data/lib/synthra/types/registry.rb +182 -0
- data/lib/synthra/types/repeating_element.rb +50 -0
- data/lib/synthra/types/scenario.rb +29 -0
- data/lib/synthra/types/technology_internet/apps.rb +67 -0
- data/lib/synthra/types/technology_internet/communication.rb +162 -0
- data/lib/synthra/types/technology_internet/devices.rb +80 -0
- data/lib/synthra/types/technology_internet/formats.rb +139 -0
- data/lib/synthra/types/technology_internet/networking.rb +143 -0
- data/lib/synthra/types/template.rb +92 -0
- data/lib/synthra/types/text_content/business.rb +128 -0
- data/lib/synthra/types/text_content/colors.rb +70 -0
- data/lib/synthra/types/text_content/misc.rb +237 -0
- data/lib/synthra/types/text_content/security.rb +130 -0
- data/lib/synthra/types/text_content/text_generation.rb +532 -0
- data/lib/synthra/types/travel/travel.rb +150 -0
- data/lib/synthra/utils/string_distance.rb +133 -0
- data/lib/synthra/validator/dsl_validator.rb +339 -0
- data/lib/synthra/validator/path_validator.rb +615 -0
- data/lib/synthra/version.rb +35 -0
- data/lib/synthra/webhook_simulator.rb +341 -0
- data/lib/synthra.rb +259 -0
- data/schemas/address.dsl +16 -0
- data/schemas/api_response.dsl +8 -0
- data/schemas/error_payload.dsl +9 -0
- data/schemas/order.dsl +10 -0
- data/schemas/order_item.dsl +8 -0
- data/schemas/payment.dsl +11 -0
- data/schemas/social_post.dsl +13 -0
- data/schemas/user.dsl +10 -0
- data/scripts/batch_fix_all.rb +96 -0
- data/scripts/delete_old_files.rb +32 -0
- data/scripts/fix_all_grouped_files.rb +137 -0
- data/scripts/fix_all_indentation.rb +48 -0
- data/scripts/fix_all_syntax.rb +124 -0
- data/scripts/fix_grouped_files.rb +184 -0
- data/scripts/fix_syntax_errors.rb +108 -0
- data/scripts/group_domain_types.rb +112 -0
- data/scripts/group_domain_types_fixed.rb +150 -0
- data/scripts/merge_domains_to_one_file.rb +68 -0
- data/scripts/move_existing_types.rb +130 -0
- data/scripts/split_grouped_types.rb +142 -0
- data/tech_docs/README.md +134 -0
- data/tech_docs/advanced/streaming.md +405 -0
- data/tech_docs/advanced/thread_safety.md +253 -0
- data/tech_docs/api/overview.md +485 -0
- data/tech_docs/appendices/type_chart.md +193 -0
- data/tech_docs/basic_concepts.md +383 -0
- data/tech_docs/behaviors/overview.md +346 -0
- data/tech_docs/dsl/complex_types.md +790 -0
- data/tech_docs/dsl/core_types.md +464 -0
- data/tech_docs/dsl/datetime_types.md +325 -0
- data/tech_docs/dsl/field_modifiers.md +414 -0
- data/tech_docs/dsl/grammar.md +431 -0
- data/tech_docs/dsl/schema_definition.md +399 -0
- data/tech_docs/export/README.md +276 -0
- data/tech_docs/installation.md +273 -0
- data/tech_docs/integration/ci_cd.md +707 -0
- data/tech_docs/integration/ci_cd_guide.md +579 -0
- data/tech_docs/integration/factory_bot.md +485 -0
- data/tech_docs/integration/rails.md +630 -0
- data/tech_docs/integration/rspec.md +449 -0
- data/tech_docs/modes/overview.md +350 -0
- data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
- data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
- data/tech_docs/quick_start.md +256 -0
- data/tech_docs/templates/README.md +805 -0
- data/tech_docs/tutorials/advanced.md +371 -0
- data/tech_docs/tutorials/getting_started.md +189 -0
- data/tech_docs/tutorials/intermediate.md +231 -0
- data/tech_docs/tutorials/template_gallery.md +569 -0
- data/tech_example/01_basic_usage.rb +238 -0
- data/tech_example/02_types_demo.rb +336 -0
- data/tech_example/04_cli_usage.md +429 -0
- data/tech_example/05_database_seeding.rb +283 -0
- data/tech_example/07_rspec_integration.rb +359 -0
- data/tech_example/10_custom_types.rb +387 -0
- data/tech_example/12_behaviors.rb +372 -0
- data/tech_example/13_twitter_dm_example.rb +120 -0
- data/tech_example/14_exact_json_structure.rb +253 -0
- data/tech_example/16_lsp_server.rb +56 -0
- data/tech_example/17_property_testing.rb +92 -0
- data/tech_example/18_enhanced_repl.rb +120 -0
- data/tech_example/19_security_fuzzing.rb +145 -0
- data/tech_example/NEW_FEATURES.md +169 -0
- data/tech_example/README.md +118 -0
- data/tech_example/schemas/api_response.dsl +123 -0
- data/tech_example/schemas/ecommerce.dsl +121 -0
- data/tech_example/schemas/twitter_dm.dsl +109 -0
- data/tech_example/schemas/user.dsl +36 -0
- data/vscode-extension/README.md +246 -0
- data/vscode-extension/language-configuration.json +31 -0
- data/vscode-extension/package.json +55 -0
- data/vscode-extension/snippets/fakedatadsl.json +198 -0
- data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
- 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
|