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,187 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module PersonalNames
|
|
6
|
+
# Username type
|
|
7
|
+
class Username < Base
|
|
8
|
+
# Generate random username
|
|
9
|
+
#
|
|
10
|
+
# @param rng [Generator::RNG] random number generator
|
|
11
|
+
# @param context [Generator::Context] generation context
|
|
12
|
+
# @param args [Hash] type arguments
|
|
13
|
+
# @option args [String] :prefix prefix for username
|
|
14
|
+
# @option args [Integer] :length total length (default: 6..12)
|
|
15
|
+
# @return [String] generated username
|
|
16
|
+
#
|
|
17
|
+
def generate_random(rng, context, args)
|
|
18
|
+
prefix = args[:prefix] || ""
|
|
19
|
+
length = args[:length] || rng.int(6, 12)
|
|
20
|
+
|
|
21
|
+
# Use first_name/last_name from context if available
|
|
22
|
+
base = if context && (context["first_name"] || context[:first_name])
|
|
23
|
+
first = (context["first_name"] || context[:first_name]).to_s.downcase
|
|
24
|
+
last = (context["last_name"] || context[:last_name]).to_s.downcase
|
|
25
|
+
"#{first}#{last}".gsub(/[^a-z0-9]/, "")
|
|
26
|
+
else
|
|
27
|
+
generate_random_base(rng, length - prefix.length)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
"#{prefix}#{base}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_edge(rng, context, args)
|
|
34
|
+
["", "a", "user123"].sample(random: rng.instance_variable_get(:@random))
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def generate_invalid(rng, context, args)
|
|
38
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
private
|
|
42
|
+
|
|
43
|
+
def generate_random_base(rng, length)
|
|
44
|
+
chars = ('a'..'z').to_a + ('0'..'9').to_a
|
|
45
|
+
length.times.map { rng.sample(chars) }.join
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Age type
|
|
50
|
+
class Age < Base
|
|
51
|
+
# Generate random age
|
|
52
|
+
#
|
|
53
|
+
# @param rng [Generator::RNG] random number generator
|
|
54
|
+
# @param context [Generator::Context] generation context
|
|
55
|
+
# @param args [Hash] type arguments
|
|
56
|
+
# @option args [Range] :range age range (e.g., 18..65)
|
|
57
|
+
# @option args [Integer] :min minimum age (default: 0)
|
|
58
|
+
# @option args [Integer] :max maximum age (default: 120)
|
|
59
|
+
# @return [Integer] generated age
|
|
60
|
+
#
|
|
61
|
+
def generate_random(rng, context, args)
|
|
62
|
+
if args[:range].is_a?(Range)
|
|
63
|
+
min = args[:range].min
|
|
64
|
+
max = args[:range].max
|
|
65
|
+
else
|
|
66
|
+
min = args[:min] || 0
|
|
67
|
+
max = args[:max] || 120
|
|
68
|
+
end
|
|
69
|
+
rng.int(min, max)
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def generate_edge(rng, context, args)
|
|
73
|
+
[0, 120, 18].sample(random: rng.instance_variable_get(:@random))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
def generate_invalid(rng, context, args)
|
|
77
|
+
[nil, "not a number", -1, 200, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
# Gender type
|
|
82
|
+
class Gender < Base
|
|
83
|
+
# Available gender identity values
|
|
84
|
+
#
|
|
85
|
+
# @return [Array<String>] list of gender identity strings
|
|
86
|
+
#
|
|
87
|
+
GENDERS = ["male", "female", "non-binary", "other", "prefer not to say"].freeze
|
|
88
|
+
|
|
89
|
+
# Generate random gender
|
|
90
|
+
#
|
|
91
|
+
# @param rng [Generator::RNG] random number generator
|
|
92
|
+
# @param context [Generator::Context] generation context
|
|
93
|
+
# @param args [Hash] type arguments
|
|
94
|
+
# @option args [Symbol] :format format type (:short, :full, :capitalized)
|
|
95
|
+
# @return [String] generated gender value
|
|
96
|
+
#
|
|
97
|
+
def generate_random(rng, context, args)
|
|
98
|
+
format_type = args[:format] || :short
|
|
99
|
+
|
|
100
|
+
# Try Faker first
|
|
101
|
+
adapter = faker_adapter(context)
|
|
102
|
+
if adapter
|
|
103
|
+
gender = adapter.gender
|
|
104
|
+
|
|
105
|
+
else
|
|
106
|
+
gender = Faker::Gender.type
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
case format_type
|
|
111
|
+
when :capitalized
|
|
112
|
+
gender.to_s.capitalize
|
|
113
|
+
when :full
|
|
114
|
+
gender.to_s
|
|
115
|
+
else
|
|
116
|
+
gender.to_s[0..2] # Short format
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
rescue StandardError
|
|
120
|
+
rng.sample(GENDERS) # fallback
|
|
121
|
+
end
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
def generate_edge(rng, context, args)
|
|
125
|
+
["", "male", "female"].sample(random: rng.instance_variable_get(:@random))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def generate_invalid(rng, context, args)
|
|
129
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# GenderAbbrev type
|
|
134
|
+
class GenderAbbrev < Base
|
|
135
|
+
ABBREVS = ["M", "F", "NB", "O"].freeze
|
|
136
|
+
|
|
137
|
+
def generate_random(rng, context, args)
|
|
138
|
+
rng.sample(ABBREVS)
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
def generate_edge(rng, context, args)
|
|
142
|
+
["", "M", "F"].sample(random: rng.instance_variable_get(:@random))
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
def generate_invalid(rng, context, args)
|
|
146
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
# GenderBinary type
|
|
151
|
+
class GenderBinary < Base
|
|
152
|
+
BINARY = ["Male", "Female"].freeze
|
|
153
|
+
|
|
154
|
+
def generate_random(rng, context, args)
|
|
155
|
+
rng.sample(BINARY)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
def generate_edge(rng, context, args)
|
|
159
|
+
["Male", "Female"].sample(random: rng.instance_variable_get(:@random))
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
def generate_invalid(rng, context, args)
|
|
163
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# GenderFacebook type
|
|
168
|
+
class GenderFacebook < Base
|
|
169
|
+
FACEBOOK_GENDERS = [
|
|
170
|
+
"Male", "Female", "Custom", "Prefer not to say"
|
|
171
|
+
].freeze
|
|
172
|
+
|
|
173
|
+
def generate_random(rng, context, args)
|
|
174
|
+
rng.sample(FACEBOOK_GENDERS)
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
def generate_edge(rng, context, args)
|
|
178
|
+
["Male", "Female"].sample(random: rng.instance_variable_get(:@random))
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
def generate_invalid(rng, context, args)
|
|
182
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
end
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module PersonalNames
|
|
6
|
+
# Name type
|
|
7
|
+
class Name < Base
|
|
8
|
+
# Generate a random name
|
|
9
|
+
#
|
|
10
|
+
# Uses Faker::Name to generate realistic names that may include
|
|
11
|
+
# titles, first names, middle names, last names, and suffixes.
|
|
12
|
+
#
|
|
13
|
+
# @param rng [Generator::RNG] random number generator (not directly used)
|
|
14
|
+
# @param args [Hash] type arguments (not used for basic name)
|
|
15
|
+
# @return [String] generated full name
|
|
16
|
+
#
|
|
17
|
+
# @example
|
|
18
|
+
# generate_random(rng, {})
|
|
19
|
+
# # => "Maria Garcia"
|
|
20
|
+
#
|
|
21
|
+
def generate_random(rng, context, args)
|
|
22
|
+
adapter = faker_adapter(context)
|
|
23
|
+
if adapter
|
|
24
|
+
adapter.name
|
|
25
|
+
|
|
26
|
+
else
|
|
27
|
+
Faker::Name.name
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
rescue StandardError
|
|
31
|
+
rng.sample(["John Doe", "Jane Smith", "Peter Jones"])
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def generate_edge(rng, context, args)
|
|
36
|
+
["", "A", "Very Long Name That Exceeds Normal Limits"].sample(random: rng.instance_variable_get(:@random))
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def generate_invalid(rng, context, args)
|
|
40
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# FirstName type
|
|
45
|
+
class FirstName < Base
|
|
46
|
+
# Generate random first name
|
|
47
|
+
#
|
|
48
|
+
# @param rng [Generator::RNG] random number generator (not directly used)
|
|
49
|
+
# @param context [Generator::Context] generation context
|
|
50
|
+
# @param args [Hash] type arguments (not used)
|
|
51
|
+
# @return [String] generated first name
|
|
52
|
+
#
|
|
53
|
+
def generate_random(rng, context, args)
|
|
54
|
+
adapter = faker_adapter(context)
|
|
55
|
+
if adapter
|
|
56
|
+
adapter.first_name
|
|
57
|
+
|
|
58
|
+
else
|
|
59
|
+
Faker::Name.first_name
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
rescue StandardError
|
|
63
|
+
rng.sample(["John", "Jane", "Peter"])
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def generate_edge(rng, context, args)
|
|
68
|
+
["", "A", "Very Long FirstName That Exceeds Normal Limits"].sample(random: rng.instance_variable_get(:@random))
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def generate_invalid(rng, context, args)
|
|
72
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# LastName type
|
|
77
|
+
class LastName < Base
|
|
78
|
+
# Generate random last name
|
|
79
|
+
#
|
|
80
|
+
# @param rng [Generator::RNG] random number generator (not directly used)
|
|
81
|
+
# @param context [Generator::Context] generation context
|
|
82
|
+
# @param args [Hash] type arguments (not used)
|
|
83
|
+
# @return [String] generated last name
|
|
84
|
+
#
|
|
85
|
+
def generate_random(rng, context, args)
|
|
86
|
+
adapter = faker_adapter(context)
|
|
87
|
+
if adapter
|
|
88
|
+
adapter.last_name
|
|
89
|
+
|
|
90
|
+
else
|
|
91
|
+
Faker::Name.last_name
|
|
92
|
+
end
|
|
93
|
+
|
|
94
|
+
rescue StandardError
|
|
95
|
+
rng.sample(["Doe", "Smith", "Jones"])
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
def generate_edge(rng, context, args)
|
|
100
|
+
["", "A", "Very Long LastName That Exceeds Normal Limits"].sample(random: rng.instance_variable_get(:@random))
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def generate_invalid(rng, context, args)
|
|
104
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
# FullName type
|
|
109
|
+
class FullName < Base
|
|
110
|
+
# Generate random full name
|
|
111
|
+
#
|
|
112
|
+
# @param rng [Generator::RNG] random number generator (not directly used)
|
|
113
|
+
# @param context [Generator::Context] generation context
|
|
114
|
+
# @param args [Hash] type arguments
|
|
115
|
+
# @option args [String] :format format string (default: "{first} {last}")
|
|
116
|
+
# @return [String] generated full name
|
|
117
|
+
#
|
|
118
|
+
def generate_random(rng, context, args)
|
|
119
|
+
format_str = args[:format] || "{first} {last}"
|
|
120
|
+
|
|
121
|
+
# Try to use context values if available
|
|
122
|
+
if context && context["first_name"] && context["last_name"]
|
|
123
|
+
format_str.gsub("{first}", context["first_name"].to_s)
|
|
124
|
+
.gsub("{last}", context["last_name"].to_s)
|
|
125
|
+
elsif context && context[:first_name] && context[:last_name]
|
|
126
|
+
format_str.gsub("{first}", context[:first_name].to_s)
|
|
127
|
+
.gsub("{last}", context[:last_name].to_s)
|
|
128
|
+
else
|
|
129
|
+
# Generate from Faker
|
|
130
|
+
adapter = faker_adapter(context)
|
|
131
|
+
if adapter
|
|
132
|
+
name = adapter.name
|
|
133
|
+
|
|
134
|
+
else
|
|
135
|
+
name = Faker::Name.name
|
|
136
|
+
end
|
|
137
|
+
|
|
138
|
+
name
|
|
139
|
+
end
|
|
140
|
+
|
|
141
|
+
rescue StandardError
|
|
142
|
+
rng.sample(["John Doe", "Jane Smith", "Peter Jones"])
|
|
143
|
+
end
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
def generate_edge(rng, context, args)
|
|
147
|
+
["", "A", "Very Long FullName That Exceeds Normal Limits"].sample(random: rng.instance_variable_get(:@random))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def generate_invalid(rng, context, args)
|
|
151
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# FirstNameFemale type
|
|
156
|
+
class FirstNameFemale < Base
|
|
157
|
+
FALLBACK_NAMES = [
|
|
158
|
+
"Emma", "Olivia", "Sophia", "Isabella", "Charlotte",
|
|
159
|
+
"Amelia", "Mia", "Harper", "Evelyn", "Abigail"
|
|
160
|
+
].freeze
|
|
161
|
+
|
|
162
|
+
def generate_random(rng, context, args)
|
|
163
|
+
adapter = faker_adapter(context)
|
|
164
|
+
if adapter && adapter.respond_to?(:female_first_name)
|
|
165
|
+
adapter.female_first_name
|
|
166
|
+
|
|
167
|
+
else
|
|
168
|
+
Faker::Name.female_first_name
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
rescue StandardError
|
|
172
|
+
rng.sample(FALLBACK_NAMES)
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def generate_edge(rng, context, args)
|
|
177
|
+
["", "A", "Very Long Female FirstName"].sample(random: rng.instance_variable_get(:@random))
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
def generate_invalid(rng, context, args)
|
|
181
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
# FirstNameMale type
|
|
186
|
+
class FirstNameMale < Base
|
|
187
|
+
FALLBACK_NAMES = [
|
|
188
|
+
"Liam", "Noah", "Oliver", "Elijah", "James",
|
|
189
|
+
"William", "Benjamin", "Lucas", "Henry", "Theodore"
|
|
190
|
+
].freeze
|
|
191
|
+
|
|
192
|
+
def generate_random(rng, context, args)
|
|
193
|
+
adapter = faker_adapter(context)
|
|
194
|
+
if adapter && adapter.respond_to?(:male_first_name)
|
|
195
|
+
adapter.male_first_name
|
|
196
|
+
|
|
197
|
+
else
|
|
198
|
+
Faker::Name.male_first_name
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
rescue StandardError
|
|
202
|
+
rng.sample(FALLBACK_NAMES)
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
def generate_edge(rng, context, args)
|
|
207
|
+
["", "A", "Very Long Male FirstName"].sample(random: rng.instance_variable_get(:@random))
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
def generate_invalid(rng, context, args)
|
|
211
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
212
|
+
end
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
# FirstNameEuropean type
|
|
216
|
+
class FirstNameEuropean < Base
|
|
217
|
+
EUROPEAN_FIRST_NAMES = [
|
|
218
|
+
"Maria", "Sophie", "Anna", "Elena", "Laura",
|
|
219
|
+
"Luca", "Marco", "David", "Thomas", "Paul"
|
|
220
|
+
].freeze
|
|
221
|
+
|
|
222
|
+
def generate_random(rng, context, args)
|
|
223
|
+
adapter = faker_adapter(context)
|
|
224
|
+
if adapter && adapter.respond_to?(:european_first_name)
|
|
225
|
+
adapter.european_first_name
|
|
226
|
+
|
|
227
|
+
else
|
|
228
|
+
rng.sample(EUROPEAN_FIRST_NAMES)
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
rescue StandardError
|
|
232
|
+
rng.sample(EUROPEAN_FIRST_NAMES)
|
|
233
|
+
end
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def generate_edge(rng, context, args)
|
|
237
|
+
["", "A", "Very Long European FirstName"].sample(random: rng.instance_variable_get(:@random))
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
def generate_invalid(rng, context, args)
|
|
241
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
242
|
+
end
|
|
243
|
+
end
|
|
244
|
+
end
|
|
245
|
+
end
|
|
246
|
+
end
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# =============================================================================
|
|
4
|
+
# Synthra National ID Type Generator
|
|
5
|
+
# =============================================================================
|
|
6
|
+
#
|
|
7
|
+
# Generates government-issued identifier numbers (SSN, etc.).
|
|
8
|
+
# Format varies by country.
|
|
9
|
+
#
|
|
10
|
+
# @example DSL usage
|
|
11
|
+
# Person:
|
|
12
|
+
# ssn: national_id(country: :us)
|
|
13
|
+
# nino: national_id(country: :uk)
|
|
14
|
+
#
|
|
15
|
+
# @example Generated values
|
|
16
|
+
# "123-45-6789" (US SSN)
|
|
17
|
+
# "AB123456C" (UK NINO)
|
|
18
|
+
#
|
|
19
|
+
# =============================================================================
|
|
20
|
+
|
|
21
|
+
module Synthra
|
|
22
|
+
module Types
|
|
23
|
+
module PersonalNames
|
|
24
|
+
# National ID type generator
|
|
25
|
+
#
|
|
26
|
+
# Generates government-issued identifier numbers.
|
|
27
|
+
# Supports multiple country formats.
|
|
28
|
+
#
|
|
29
|
+
# @example Generate SSN
|
|
30
|
+
# national_id = NationalId.new
|
|
31
|
+
# national_id.generate(rng, context, { country: :us }, :random)
|
|
32
|
+
# # => "123-45-6789"
|
|
33
|
+
#
|
|
34
|
+
class NationalId < Base
|
|
35
|
+
# Generate national ID based on country
|
|
36
|
+
#
|
|
37
|
+
# @param rng [Generator::RNG] random number generator
|
|
38
|
+
# @param context [Generator::Context] generation context
|
|
39
|
+
# @param args [Hash] type arguments
|
|
40
|
+
# @option args [Symbol, String] :country country code (:us, :uk, :ca, etc.)
|
|
41
|
+
# @return [String] generated national ID
|
|
42
|
+
#
|
|
43
|
+
def generate_random(rng, context, args)
|
|
44
|
+
country = (args[:country] || :us).to_sym
|
|
45
|
+
|
|
46
|
+
case country
|
|
47
|
+
when :us
|
|
48
|
+
# US SSN format: XXX-XX-XXXX
|
|
49
|
+
format("%03d-%02d-%04d", rng.int(100, 999), rng.int(10, 99), rng.int(1000, 9999))
|
|
50
|
+
when :uk
|
|
51
|
+
# UK NINO format: AB123456C
|
|
52
|
+
prefix = rng.sample(("AA".."ZZ").to_a.reject { |x| x.match?(/[IOQZ]/) })
|
|
53
|
+
numbers = format("%06d", rng.int(0, 999_999))
|
|
54
|
+
suffix = rng.sample(("A".."Z").to_a.reject { |x| ["I", "O", "Q", "Z"].include?(x) })
|
|
55
|
+
"#{prefix}#{numbers}#{suffix}"
|
|
56
|
+
when :ca
|
|
57
|
+
# Canadian SIN format: XXX XXX XXX
|
|
58
|
+
format("%03d %03d %03d", rng.int(100, 999), rng.int(100, 999), rng.int(100, 999))
|
|
59
|
+
else
|
|
60
|
+
# Default: simple numeric format
|
|
61
|
+
format("%09d", rng.int(100_000_000, 999_999_999)) # fallback
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# Generate edge case values
|
|
66
|
+
#
|
|
67
|
+
# @param rng [Generator::RNG] random number generator
|
|
68
|
+
# @param context [Generator::Context] generation context
|
|
69
|
+
# @param args [Hash] type arguments
|
|
70
|
+
# @return [String] edge case value
|
|
71
|
+
#
|
|
72
|
+
def generate_edge(rng, context, args)
|
|
73
|
+
["", "000-00-0000", "999-99-9999", "123"].sample(random: rng.instance_variable_get(:@random))
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
# Generate invalid values
|
|
77
|
+
#
|
|
78
|
+
# @param rng [Generator::RNG] random number generator
|
|
79
|
+
# @param context [Generator::Context] generation context
|
|
80
|
+
# @param args [Hash] type arguments
|
|
81
|
+
# @return [Object] invalid value
|
|
82
|
+
#
|
|
83
|
+
def generate_invalid(rng, context, args)
|
|
84
|
+
[nil, 123, [], {}, true, "invalid-ssn", "ABC-DEF-GHIJ"].sample(random: rng.instance_variable_get(:@random))
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module PersonalNames
|
|
6
|
+
# Title type
|
|
7
|
+
class Title < Base
|
|
8
|
+
TITLES = ["Mr.", "Mrs.", "Ms.", "Miss", "Dr.", "Prof.", "Rev.", "Hon."].freeze
|
|
9
|
+
|
|
10
|
+
def generate_random(rng, context, args)
|
|
11
|
+
rng.sample(TITLES)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def generate_edge(rng, context, args)
|
|
15
|
+
["", "Mr.", "Dr."].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
|
+
|
|
23
|
+
# Suffix type
|
|
24
|
+
class Suffix < Base
|
|
25
|
+
SUFFIXES = ["Jr.", "Sr.", "II", "III", "IV", "V", "PhD", "MD", "Esq."].freeze
|
|
26
|
+
|
|
27
|
+
def generate_random(rng, context, args)
|
|
28
|
+
rng.sample(SUFFIXES)
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def generate_edge(rng, context, args)
|
|
32
|
+
["", "Jr.", "PhD"].sample(random: rng.instance_variable_get(:@random))
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def generate_invalid(rng, context, args)
|
|
36
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|