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,162 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module TechnologyInternet
|
|
6
|
+
# Email type
|
|
7
|
+
class Email < Base
|
|
8
|
+
# Generate a random email address
|
|
9
|
+
#
|
|
10
|
+
# Uses Faker::Internet to generate realistic email addresses.
|
|
11
|
+
#
|
|
12
|
+
# @param rng [Generator::RNG] random number generator (not directly used)
|
|
13
|
+
# @param args [Hash] type arguments (not used for basic email)
|
|
14
|
+
# @return [String] generated email address
|
|
15
|
+
#
|
|
16
|
+
# @example
|
|
17
|
+
# generate_random(rng, {})
|
|
18
|
+
# # => "maria.garcia@example.com"
|
|
19
|
+
#
|
|
20
|
+
def generate_random(rng, context, args)
|
|
21
|
+
adapter = faker_adapter(context)
|
|
22
|
+
if adapter
|
|
23
|
+
adapter.email
|
|
24
|
+
|
|
25
|
+
else
|
|
26
|
+
Faker::Internet.email
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
rescue StandardError
|
|
30
|
+
"#{rng.sample(['user', 'test', 'admin'])}@#{rng.sample(['example.com', 'test.com', 'demo.com'])}"
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def generate_edge(rng, context, args)
|
|
35
|
+
["", "a@b", "user@", "@domain", "user@domain"].sample(random: rng.instance_variable_get(:@random))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def generate_invalid(rng, context, args)
|
|
39
|
+
["notanemail", "@missing.local", "missing@", "double@@at.com", "spaces in@email.com", nil, 123].sample(random: rng.instance_variable_get(:@random))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# Phone type
|
|
44
|
+
class Phone < Base
|
|
45
|
+
def generate_random(rng, context, args)
|
|
46
|
+
adapter = faker_adapter(context)
|
|
47
|
+
if adapter
|
|
48
|
+
adapter.phone_number
|
|
49
|
+
|
|
50
|
+
else
|
|
51
|
+
Faker::PhoneNumber.phone_number
|
|
52
|
+
end
|
|
53
|
+
|
|
54
|
+
rescue StandardError
|
|
55
|
+
rng.sample(["+1-555-123-4567", "+44-20-1234-5678", "+33-1-23-45-67-89"])
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def generate_edge(rng, context, args)
|
|
60
|
+
["", "+1-555-000-0000", "000-000-0000"].sample(random: rng.instance_variable_get(:@random))
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def generate_invalid(rng, context, args)
|
|
64
|
+
[nil, 123, [], {}, "not a phone"].sample(random: rng.instance_variable_get(:@random))
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
# Url type
|
|
69
|
+
class Url < Base
|
|
70
|
+
def generate_random(rng, context, args)
|
|
71
|
+
adapter = faker_adapter(context)
|
|
72
|
+
if adapter
|
|
73
|
+
adapter.url
|
|
74
|
+
|
|
75
|
+
else
|
|
76
|
+
Faker::Internet.url
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
rescue StandardError
|
|
80
|
+
"https://#{rng.sample(['example.com', 'test.com', 'demo.com'])}/#{rng.sample(['page', 'path', 'resource'])}"
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def generate_edge(rng, context, args)
|
|
85
|
+
["", "http://", "http://a", "https://example.com"].sample(random: rng.instance_variable_get(:@random))
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def generate_invalid(rng, context, args)
|
|
89
|
+
[nil, 123, [], {}, "not-a-url", "missing-protocol.com"].sample(random: rng.instance_variable_get(:@random))
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
# Domain type
|
|
94
|
+
class Domain < Base
|
|
95
|
+
# Generate random domain name
|
|
96
|
+
#
|
|
97
|
+
# @param rng [Generator::RNG] random number generator (not directly used)
|
|
98
|
+
# @param context [Generator::Context] generation context
|
|
99
|
+
# @param args [Hash] type arguments (not used)
|
|
100
|
+
# @return [String] generated domain name
|
|
101
|
+
#
|
|
102
|
+
def generate_random(rng, context, args)
|
|
103
|
+
adapter = faker_adapter(context)
|
|
104
|
+
if adapter
|
|
105
|
+
adapter.domain_name
|
|
106
|
+
|
|
107
|
+
else
|
|
108
|
+
Faker::Internet.domain_name
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
rescue StandardError
|
|
112
|
+
"#{rng.sample(['example', 'test', 'demo'])}.#{rng.sample(['com', 'org', 'net'])}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
def generate_edge(rng, context, args)
|
|
117
|
+
["", "a.b", "very-long-domain-name-that-exceeds-normal-limits.com"].sample(random: rng.instance_variable_get(:@random))
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def generate_invalid(rng, context, args)
|
|
121
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
122
|
+
end
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
# UserAgent type
|
|
126
|
+
class UserAgent < Base
|
|
127
|
+
# Generate random user agent
|
|
128
|
+
#
|
|
129
|
+
# @param rng [Generator::RNG] random number generator (not directly used)
|
|
130
|
+
# @param context [Generator::Context] generation context
|
|
131
|
+
# @param args [Hash] type arguments (not used)
|
|
132
|
+
# @return [String] generated user agent string
|
|
133
|
+
#
|
|
134
|
+
def generate_random(rng, context, args)
|
|
135
|
+
adapter = faker_adapter(context)
|
|
136
|
+
if adapter
|
|
137
|
+
adapter.user_agent
|
|
138
|
+
|
|
139
|
+
else
|
|
140
|
+
Faker::Internet.user_agent
|
|
141
|
+
end
|
|
142
|
+
|
|
143
|
+
rescue StandardError
|
|
144
|
+
rng.sample([
|
|
145
|
+
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36",
|
|
146
|
+
"Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36",
|
|
147
|
+
"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36"
|
|
148
|
+
])
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def generate_edge(rng, context, args)
|
|
153
|
+
["", "Mozilla/5.0", "Invalid User Agent"].sample(random: rng.instance_variable_get(:@random))
|
|
154
|
+
end
|
|
155
|
+
|
|
156
|
+
def generate_invalid(rng, context, args)
|
|
157
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
158
|
+
end
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
end
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module TechnologyInternet
|
|
6
|
+
# MobileDeviceBrand type
|
|
7
|
+
class MobileDeviceBrand < Base
|
|
8
|
+
BRANDS = ["Apple", "Samsung", "Google", "Huawei", "Xiaomi", "OnePlus", "LG", "Motorola"].freeze
|
|
9
|
+
|
|
10
|
+
def generate_random(rng, context, args)
|
|
11
|
+
rng.sample(BRANDS)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def generate_edge(rng, context, args)
|
|
15
|
+
["", "Apple", "Samsung"].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
|
+
# MobileDeviceModel type
|
|
24
|
+
class MobileDeviceModel < Base
|
|
25
|
+
MODELS = [
|
|
26
|
+
"iPhone 14", "iPhone 13", "Galaxy S23", "Galaxy S22",
|
|
27
|
+
"Pixel 7", "Pixel 6", "P50", "Mi 13", "OnePlus 11"
|
|
28
|
+
].freeze
|
|
29
|
+
|
|
30
|
+
def generate_random(rng, context, args)
|
|
31
|
+
rng.sample(MODELS)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def generate_edge(rng, context, args)
|
|
35
|
+
["", "iPhone 14", "A"].sample(random: rng.instance_variable_get(:@random))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def generate_invalid(rng, context, args)
|
|
39
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# MobileDeviceOs type
|
|
44
|
+
class MobileDeviceOs < Base
|
|
45
|
+
OS_TYPES = ["iOS", "Android", "Windows Mobile", "BlackBerry OS"].freeze
|
|
46
|
+
|
|
47
|
+
def generate_random(rng, context, args)
|
|
48
|
+
rng.sample(OS_TYPES)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def generate_edge(rng, context, args)
|
|
52
|
+
["", "iOS", "Android"].sample(random: rng.instance_variable_get(:@random))
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
def generate_invalid(rng, context, args)
|
|
56
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
# MobileDeviceReleaseDate type
|
|
61
|
+
class MobileDeviceReleaseDate < Base
|
|
62
|
+
def generate_random(rng, context, args)
|
|
63
|
+
# Generate date between 2015 and 2024
|
|
64
|
+
year = rng.int(2015, 2024)
|
|
65
|
+
month = rng.int(1, 12)
|
|
66
|
+
day = rng.int(1, 28)
|
|
67
|
+
Date.new(year, month, day)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def generate_edge(rng, context, args)
|
|
71
|
+
[Date.new(2015, 1, 1), Date.new(2024, 12, 31)].sample(random: rng.instance_variable_get(:@random))
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def generate_invalid(rng, context, args)
|
|
75
|
+
[nil, "not a date", 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
end
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module TechnologyInternet
|
|
6
|
+
# Language type
|
|
7
|
+
class Language < Base
|
|
8
|
+
LANGUAGES = [
|
|
9
|
+
"English", "Spanish", "French", "German", "Italian", "Portuguese",
|
|
10
|
+
"Russian", "Chinese", "Japanese", "Korean", "Arabic", "Hindi"
|
|
11
|
+
].freeze
|
|
12
|
+
|
|
13
|
+
def generate_random(rng, context, args)
|
|
14
|
+
rng.sample(LANGUAGES)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def generate_edge(rng, context, args)
|
|
18
|
+
["", "English", "A"].sample(random: rng.instance_variable_get(:@random))
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def generate_invalid(rng, context, args)
|
|
22
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
# LanguageCode type
|
|
27
|
+
class LanguageCode < Base
|
|
28
|
+
LANGUAGE_CODES = ["en", "es", "fr", "de", "it", "pt", "ru", "zh", "ja", "ko", "ar", "hi"].freeze
|
|
29
|
+
|
|
30
|
+
def generate_random(rng, context, args)
|
|
31
|
+
rng.sample(LANGUAGE_CODES)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def generate_edge(rng, context, args)
|
|
35
|
+
["", "en", "xx"].sample(random: rng.instance_variable_get(:@random))
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def generate_invalid(rng, context, args)
|
|
39
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
# MimeType type
|
|
44
|
+
class MimeType < Base
|
|
45
|
+
MIME_TYPES = [
|
|
46
|
+
"text/plain", "text/html", "text/css", "text/javascript",
|
|
47
|
+
"application/json", "application/xml", "application/pdf",
|
|
48
|
+
"image/jpeg", "image/png", "image/gif", "image/svg+xml",
|
|
49
|
+
"video/mp4", "video/quicktime", "audio/mpeg", "audio/wav"
|
|
50
|
+
].freeze
|
|
51
|
+
|
|
52
|
+
def generate_random(rng, context, args)
|
|
53
|
+
rng.sample(MIME_TYPES)
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def generate_edge(rng, context, args)
|
|
57
|
+
["", "text/plain", "application/json"].sample(random: rng.instance_variable_get(:@random))
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def generate_invalid(rng, context, args)
|
|
61
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# TopLevelDomain type
|
|
66
|
+
class TopLevelDomain < Base
|
|
67
|
+
TLDS = [".com", ".org", ".net", ".edu", ".gov", ".io", ".co", ".ai", ".dev", ".app"].freeze
|
|
68
|
+
|
|
69
|
+
def generate_random(rng, context, args)
|
|
70
|
+
rng.sample(TLDS)
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def generate_edge(rng, context, args)
|
|
74
|
+
["", ".com", ".xxx"].sample(random: rng.instance_variable_get(:@random))
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def generate_invalid(rng, context, args)
|
|
78
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
# Base64ImageUrl type
|
|
83
|
+
class Base64ImageUrl < Base
|
|
84
|
+
def generate_random(rng, context, args)
|
|
85
|
+
# Generate a data URL with base64 encoded image
|
|
86
|
+
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
def generate_edge(rng, context, args)
|
|
90
|
+
["", "data:image/png;base64,invalid"].sample(random: rng.instance_variable_get(:@random))
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def generate_invalid(rng, context, args)
|
|
94
|
+
[nil, 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
95
|
+
end
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# DummyImageUrl type
|
|
99
|
+
class DummyImageUrl < Base
|
|
100
|
+
def generate_random(rng, context, args)
|
|
101
|
+
min_height = args[:minHeight] || 100
|
|
102
|
+
max_height = args[:maxHeight] || 800
|
|
103
|
+
min_width = args[:minWidth] || 100
|
|
104
|
+
max_width = args[:maxWidth] || 1200
|
|
105
|
+
format = args[:format] || "jpg"
|
|
106
|
+
|
|
107
|
+
height = rng.int(min_height, max_height)
|
|
108
|
+
width = rng.int(min_width, max_width)
|
|
109
|
+
"https://dummyimage.com/#{width}x#{height}/000/fff.#{format}"
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def generate_edge(rng, context, args)
|
|
113
|
+
["", "https://dummyimage.com/1x1/000/fff.jpg"].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
|
+
# MongodbObjectId type
|
|
122
|
+
class MongodbObjectId < Base
|
|
123
|
+
def generate_random(rng, context, args)
|
|
124
|
+
# MongoDB ObjectID is 24 hex characters
|
|
125
|
+
chars = ('0'..'9').to_a + ('a'..'f').to_a
|
|
126
|
+
24.times.map { rng.sample(chars) }.join
|
|
127
|
+
end
|
|
128
|
+
|
|
129
|
+
def generate_edge(rng, context, args)
|
|
130
|
+
["000000000000000000000000", "ffffffffffffffffffffffff"].sample(random: rng.instance_variable_get(:@random))
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def generate_invalid(rng, context, args)
|
|
134
|
+
[nil, "not an id", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
135
|
+
end
|
|
136
|
+
end
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
end
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module TechnologyInternet
|
|
6
|
+
# Ip type
|
|
7
|
+
class Ip < Base
|
|
8
|
+
# Generate a random IPv4 address
|
|
9
|
+
#
|
|
10
|
+
# @param rng [Generator::RNG] random number generator
|
|
11
|
+
# @param context [Generator::Context] generation context
|
|
12
|
+
# @param args [Hash] type arguments (not used)
|
|
13
|
+
# @return [String] IPv4 address string
|
|
14
|
+
#
|
|
15
|
+
def generate_random(rng, context, args)
|
|
16
|
+
adapter = faker_adapter(context)
|
|
17
|
+
if adapter
|
|
18
|
+
adapter.ip_v4_address
|
|
19
|
+
|
|
20
|
+
else
|
|
21
|
+
Faker::Internet.ip_v4_address
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
rescue StandardError
|
|
25
|
+
"#{rng.int(1, 255)}.#{rng.int(0, 255)}.#{rng.int(0, 255)}.#{rng.int(1, 255)}"
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def generate_edge(rng, context, args)
|
|
30
|
+
["0.0.0.0", "255.255.255.255", "127.0.0.1"].sample(random: rng.instance_variable_get(:@random))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_invalid(rng, context, args)
|
|
34
|
+
[nil, "not an ip", "256.256.256.256", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# Ipv6 type
|
|
39
|
+
class Ipv6 < Base
|
|
40
|
+
# Generate a random IPv6 address
|
|
41
|
+
#
|
|
42
|
+
# @param rng [Generator::RNG] random number generator
|
|
43
|
+
# @param context [Generator::Context] generation context
|
|
44
|
+
# @param args [Hash] type arguments (not used)
|
|
45
|
+
# @return [String] IPv6 address string
|
|
46
|
+
#
|
|
47
|
+
def generate_random(rng, context, args)
|
|
48
|
+
adapter = faker_adapter(context)
|
|
49
|
+
if adapter
|
|
50
|
+
adapter.ip_v6_address
|
|
51
|
+
|
|
52
|
+
else
|
|
53
|
+
# Manual IPv6 generation fallback
|
|
54
|
+
8.times.map { format("%x", rng.int(0, 65535)) }.join(":")
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
rescue StandardError
|
|
58
|
+
8.times.map { format("%x", rng.int(0, 65535)) }.join(":") # Fallback
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def generate_edge(rng, context, args)
|
|
63
|
+
["::1", "fe80::1", "2001:0db8:85a3:0000:0000:8a2e:0370:7334"].sample(random: rng.instance_variable_get(:@random))
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def generate_invalid(rng, context, args)
|
|
67
|
+
[nil, "not:an:ipv6", "::g", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
# MacAddress type
|
|
72
|
+
class MacAddress < Base
|
|
73
|
+
# Generate random MAC address
|
|
74
|
+
#
|
|
75
|
+
# @param rng [Generator::RNG] random number generator
|
|
76
|
+
# @param context [Generator::Context] generation context
|
|
77
|
+
# @param args [Hash] type arguments
|
|
78
|
+
# @option args [Symbol] :format format type (:colon, :dash, :none)
|
|
79
|
+
# @return [String] generated MAC address
|
|
80
|
+
#
|
|
81
|
+
def generate_random(rng, context, args)
|
|
82
|
+
format_type = args[:format] || :colon
|
|
83
|
+
|
|
84
|
+
# Generate 6 octets (12 hex digits)
|
|
85
|
+
octets = 6.times.map { format("%02X", rng.int(0, 255)) }
|
|
86
|
+
|
|
87
|
+
case format_type
|
|
88
|
+
when :dash
|
|
89
|
+
octets.join("-")
|
|
90
|
+
when :none
|
|
91
|
+
octets.join
|
|
92
|
+
else # :colon
|
|
93
|
+
octets.join(":")
|
|
94
|
+
end
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
def generate_edge(rng, context, args)
|
|
98
|
+
["00:00:00:00:00:00", "FF:FF:FF:FF:FF:FF"].sample(random: rng.instance_variable_get(:@random))
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def generate_invalid(rng, context, args)
|
|
102
|
+
[nil, "not a mac", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
# IpV4Cidr type
|
|
107
|
+
class IpV4Cidr < Base
|
|
108
|
+
def generate_random(rng, context, args)
|
|
109
|
+
# Generate IP with CIDR notation (e.g., 192.168.1.0/24)
|
|
110
|
+
ip = TechnologyInternet::Ip.new.generate_random(rng, context, {})
|
|
111
|
+
cidr = rng.int(8, 30) # Common CIDR ranges
|
|
112
|
+
"#{ip}/#{cidr}"
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
def generate_edge(rng, context, args)
|
|
116
|
+
["0.0.0.0/0", "255.255.255.255/32"].sample(random: rng.instance_variable_get(:@random))
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def generate_invalid(rng, context, args)
|
|
120
|
+
[nil, "not cidr", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
121
|
+
end
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
# IpV6Cidr type
|
|
125
|
+
class IpV6Cidr < Base
|
|
126
|
+
def generate_random(rng, context, args)
|
|
127
|
+
# Generate IPv6 with CIDR notation
|
|
128
|
+
ipv6 = TechnologyInternet::Ipv6.new.generate_random(rng, context, {})
|
|
129
|
+
cidr = rng.int(64, 128) # Common IPv6 CIDR ranges
|
|
130
|
+
"#{ipv6}/#{cidr}"
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
def generate_edge(rng, context, args)
|
|
134
|
+
["::/0", "ffff:ffff:ffff:ffff:ffff:ffff:ffff:ffff/128"].sample(random: rng.instance_variable_get(:@random))
|
|
135
|
+
end
|
|
136
|
+
|
|
137
|
+
def generate_invalid(rng, context, args)
|
|
138
|
+
[nil, "not cidr", "invalid", [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
139
|
+
end
|
|
140
|
+
end
|
|
141
|
+
end
|
|
142
|
+
end
|
|
143
|
+
end
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
# Template type generator.
|
|
6
|
+
#
|
|
7
|
+
# Substitutes +${field}+ (and legacy +{field}+) placeholders with the
|
|
8
|
+
# already-generated value of that field in the same record. Field values
|
|
9
|
+
# come from the generation +context+, which holds every field generated
|
|
10
|
+
# before this one (fields are generated in definition order).
|
|
11
|
+
#
|
|
12
|
+
# first: name
|
|
13
|
+
# greeting: template("Hello ${first}") # => "Hello Ada Lovelace"
|
|
14
|
+
#
|
|
15
|
+
# A referenced field that is not (yet) available is left as the literal
|
|
16
|
+
# placeholder text rather than blanking the whole string.
|
|
17
|
+
class Template < Base
|
|
18
|
+
# Matches ${field} or {field}; the field name is captured.
|
|
19
|
+
PLACEHOLDER = /\$\{([^}]+)\}|\{([^}]+)\}/
|
|
20
|
+
|
|
21
|
+
def generate_random(rng, context, args)
|
|
22
|
+
pattern = args[:pattern] || args[:template] || ""
|
|
23
|
+
return "" if pattern.to_s.empty?
|
|
24
|
+
|
|
25
|
+
values = context_values(context)
|
|
26
|
+
|
|
27
|
+
pattern.to_s.gsub(PLACEHOLDER) do
|
|
28
|
+
key = (Regexp.last_match(1) || Regexp.last_match(2)).strip
|
|
29
|
+
if values.key?(key)
|
|
30
|
+
values[key].to_s
|
|
31
|
+
else
|
|
32
|
+
Regexp.last_match(0) # leave the literal placeholder untouched
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def generate_edge(rng, context, args)
|
|
38
|
+
rng.sample(["", "${missing}", "template"])
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def generate_invalid(rng, context, args)
|
|
42
|
+
rng.sample([nil, 123, [], {}])
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
private
|
|
46
|
+
|
|
47
|
+
# Flatten the context into a String-keyed lookup of field => value.
|
|
48
|
+
# Accepts a Context, a Hash, or anything responding to #each.
|
|
49
|
+
#
|
|
50
|
+
# @param context [Object]
|
|
51
|
+
# @return [Hash{String=>Object}]
|
|
52
|
+
def context_values(context)
|
|
53
|
+
return {} unless context.respond_to?(:each)
|
|
54
|
+
|
|
55
|
+
values = {}
|
|
56
|
+
context.each do |key, value|
|
|
57
|
+
values[key.to_s] = value
|
|
58
|
+
end
|
|
59
|
+
values
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
# Look up a single value from a context by key.
|
|
63
|
+
#
|
|
64
|
+
# Retained as a public-by-#send helper for callers/tests that resolve one
|
|
65
|
+
# key at a time (e.g. lazy interpolation). The primary generate_random path
|
|
66
|
+
# uses context_values for bulk substitution; this mirrors that resolution
|
|
67
|
+
# order (hash-style [] first, then a same-named method) for a single key.
|
|
68
|
+
#
|
|
69
|
+
# @param context [Object] context object (Hash, Context, or any object)
|
|
70
|
+
# @param key [String] key to retrieve
|
|
71
|
+
# @return [Object] value (stringified) or "" if not found
|
|
72
|
+
def get_context_value(context, key)
|
|
73
|
+
return "" if context.nil?
|
|
74
|
+
|
|
75
|
+
if context.respond_to?(:[])
|
|
76
|
+
value = context[key] || context[key.to_sym]
|
|
77
|
+
return value.to_s if value
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
if context.respond_to?(key)
|
|
81
|
+
begin
|
|
82
|
+
return context.public_send(key).to_s
|
|
83
|
+
rescue NoMethodError
|
|
84
|
+
return "" # empty result
|
|
85
|
+
end
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
""
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
end
|
|
92
|
+
end
|