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,387 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Synthra
|
|
4
|
+
module Types
|
|
5
|
+
module DateTime
|
|
6
|
+
# DateType type
|
|
7
|
+
class DateType < Base
|
|
8
|
+
def generate_random(rng, context, args)
|
|
9
|
+
# When a TimeTravel context is active, re-base the "N days back from today"
|
|
10
|
+
# distribution onto the anchor date instead of the real Date.today. Same
|
|
11
|
+
# shape (uniform over the last 2 years), just relative to a different origin.
|
|
12
|
+
anchor = TimeTravel.anchor_date
|
|
13
|
+
return anchor - rng.int(0, 730) if anchor
|
|
14
|
+
|
|
15
|
+
adapter = faker_adapter(context)
|
|
16
|
+
if adapter
|
|
17
|
+
adapter.date_backward(days: 365 * 2)
|
|
18
|
+
|
|
19
|
+
else
|
|
20
|
+
Faker::Date.backward(days: 365 * 2)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
rescue StandardError
|
|
24
|
+
Date.today - rng.int(0, 730)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def generate_edge(rng, context, args)
|
|
29
|
+
anchor = TimeTravel.anchor_date || Date.today
|
|
30
|
+
[anchor, anchor - 730].sample(random: rng.instance_variable_get(:@random))
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def generate_invalid(rng, context, args)
|
|
34
|
+
[nil, "not a date", 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
# DatetimeType type
|
|
39
|
+
class DatetimeType < Base
|
|
40
|
+
# Generate random datetime
|
|
41
|
+
#
|
|
42
|
+
# @param rng [Generator::RNG] random number generator
|
|
43
|
+
# @param context [Generator::Context] generation context
|
|
44
|
+
# @param args [Hash] type arguments
|
|
45
|
+
# @option args [Symbol] :format format type (:standard, :iso8601, :rfc3339)
|
|
46
|
+
# @option args [Range] :date_range date range for generation
|
|
47
|
+
# @return [String] generated datetime string
|
|
48
|
+
#
|
|
49
|
+
def generate_random(rng, context, args)
|
|
50
|
+
format_type = args[:format] || :standard
|
|
51
|
+
reference_date = TimeTravel.anchor_date || Date.today
|
|
52
|
+
date_range = args[:date_range] || (reference_date - 365..reference_date + 365)
|
|
53
|
+
|
|
54
|
+
# Generate date
|
|
55
|
+
date = rng.sample(date_range.to_a)
|
|
56
|
+
|
|
57
|
+
# Generate time
|
|
58
|
+
hour = rng.int(0, 23)
|
|
59
|
+
minute = rng.int(0, 59)
|
|
60
|
+
second = rng.int(0, 59)
|
|
61
|
+
|
|
62
|
+
case format_type
|
|
63
|
+
when :iso8601, :rfc3339
|
|
64
|
+
"#{date}T#{format('%02d:%02d:%02d', hour, minute, second)}Z"
|
|
65
|
+
else # :standard
|
|
66
|
+
"#{date} #{format('%02d:%02d:%02d', hour, minute, second)}"
|
|
67
|
+
end
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def generate_edge(rng, context, args)
|
|
71
|
+
["1970-01-01T00:00:00Z", "2099-12-31T23:59:59Z"].sample(random: rng.instance_variable_get(:@random))
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def generate_invalid(rng, context, args)
|
|
75
|
+
[nil, "not a datetime", 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
76
|
+
end
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
# TimeType type
|
|
80
|
+
class TimeType < Base
|
|
81
|
+
# Generate random time
|
|
82
|
+
#
|
|
83
|
+
# @param rng [Generator::RNG] random number generator
|
|
84
|
+
# @param context [Generator::Context] generation context
|
|
85
|
+
# @param args [Hash] type arguments
|
|
86
|
+
# @option args [Symbol] :format time format (:full, :hour_minute, :hour_only)
|
|
87
|
+
# @return [String] generated time string
|
|
88
|
+
#
|
|
89
|
+
def generate_random(rng, context, args)
|
|
90
|
+
format_type = args[:format] || :full
|
|
91
|
+
|
|
92
|
+
hour = rng.int(0, 23)
|
|
93
|
+
minute = rng.int(0, 59)
|
|
94
|
+
second = rng.int(0, 59)
|
|
95
|
+
|
|
96
|
+
case format_type
|
|
97
|
+
when :hour_only
|
|
98
|
+
format("%02d:00:00", hour)
|
|
99
|
+
when :hour_minute
|
|
100
|
+
format("%02d:%02d:00", hour, minute)
|
|
101
|
+
else # :full
|
|
102
|
+
format("%02d:%02d:%02d", hour, minute, second)
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
def generate_edge(rng, context, args)
|
|
107
|
+
["00:00:00", "23:59:59", "12:00:00"].sample(random: rng.instance_variable_get(:@random))
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
def generate_invalid(rng, context, args)
|
|
111
|
+
[nil, "not a time", 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
112
|
+
end
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
# Timestamp type
|
|
116
|
+
class Timestamp < Base
|
|
117
|
+
# Generate a random timestamp
|
|
118
|
+
#
|
|
119
|
+
# Generates a Time value in the past (up to 365 days ago).
|
|
120
|
+
#
|
|
121
|
+
# @param rng [Generator::RNG] random number generator
|
|
122
|
+
# @param context [Generator::Context] generation context
|
|
123
|
+
# @param args [Hash] type arguments (not used)
|
|
124
|
+
# @return [Time] time value in the past
|
|
125
|
+
#
|
|
126
|
+
def generate_random(rng, context, args)
|
|
127
|
+
# "timestamp" anchors exactly to the TimeTravel point-in-time (or a random
|
|
128
|
+
# point within it, if a range context is active) rather than a random past
|
|
129
|
+
# offset — the caller asked for data as it existed AT that moment.
|
|
130
|
+
return TimeTravel.current_time.current_time if TimeTravel.current_time
|
|
131
|
+
|
|
132
|
+
adapter = faker_adapter(context)
|
|
133
|
+
if adapter
|
|
134
|
+
adapter.time_backward(days: 365)
|
|
135
|
+
|
|
136
|
+
else
|
|
137
|
+
Faker::Time.backward(days: 365)
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
rescue StandardError
|
|
141
|
+
Time.now - rng.int(0, 365 * 86400) # fallback
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def generate_edge(rng, context, args)
|
|
146
|
+
reference = TimeTravel.current_time&.current_time || Time.now
|
|
147
|
+
[Time.at(0), reference].sample(random: rng.instance_variable_get(:@random))
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def generate_invalid(rng, context, args)
|
|
151
|
+
[nil, "not a timestamp", 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
152
|
+
end
|
|
153
|
+
end
|
|
154
|
+
|
|
155
|
+
# Now type
|
|
156
|
+
class Now < Base
|
|
157
|
+
# Generate a "current" date value
|
|
158
|
+
#
|
|
159
|
+
# Uses a fixed base date (2020-01-01) plus a random offset
|
|
160
|
+
# determined by the RNG for deterministic output.
|
|
161
|
+
#
|
|
162
|
+
# @param rng [Generator::RNG] random number generator
|
|
163
|
+
# @param context [Generator::Context] generation context (not used)
|
|
164
|
+
# @param args [Hash] type arguments (not used)
|
|
165
|
+
# @return [Date] date value representing "now"
|
|
166
|
+
#
|
|
167
|
+
# @example
|
|
168
|
+
# generate_random(rng, context, {})
|
|
169
|
+
# # => Date.new(2020, 1, 15) # Base + random days
|
|
170
|
+
#
|
|
171
|
+
def generate_random(rng, context, args)
|
|
172
|
+
# "now" anchors exactly to the TimeTravel point-in-time when one is set —
|
|
173
|
+
# it represents "the current moment", so under time travel that moment
|
|
174
|
+
# IS the anchor (or a random point within an anchored range).
|
|
175
|
+
if TimeTravel.current_time
|
|
176
|
+
anchored = TimeTravel.current_time.current_time
|
|
177
|
+
return anchored.respond_to?(:to_date) ? anchored.to_date : anchored
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
# Otherwise: use RNG to generate deterministic timestamps.
|
|
181
|
+
# Base time is 2020-01-01, add random offset up to 5 years
|
|
182
|
+
base_time = Date.new(2020, 1, 1)
|
|
183
|
+
days_offset = rng.int(0, 365 * 5)
|
|
184
|
+
base_time + days_offset
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def generate_edge(rng, context, args)
|
|
188
|
+
if TimeTravel.current_time
|
|
189
|
+
anchored = TimeTravel.current_time.current_time
|
|
190
|
+
return anchored.respond_to?(:to_date) ? anchored.to_date : anchored
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
[Date.new(2020, 1, 1), Date.new(2025, 1, 1)].sample(random: rng.instance_variable_get(:@random))
|
|
194
|
+
end
|
|
195
|
+
|
|
196
|
+
def generate_invalid(rng, context, args)
|
|
197
|
+
[nil, "not a date", 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
198
|
+
end
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
# PastDate type
|
|
202
|
+
class PastDate < Base
|
|
203
|
+
# Generate a random past date
|
|
204
|
+
#
|
|
205
|
+
# Generates a date in the past using the specified duration.
|
|
206
|
+
#
|
|
207
|
+
# @param rng [Generator::RNG] random number generator
|
|
208
|
+
# @param context [Generator::Context] generation context
|
|
209
|
+
# @param args [Hash] type arguments
|
|
210
|
+
# @option args [Integer, Hash] :value duration value
|
|
211
|
+
# @option args [String] :suffix duration unit (d, m, y, h)
|
|
212
|
+
# @return [Date] date in the past
|
|
213
|
+
#
|
|
214
|
+
def generate_random(rng, context, args)
|
|
215
|
+
duration = parse_duration(args)
|
|
216
|
+
anchor = TimeTravel.anchor_date
|
|
217
|
+
return anchor - duration if anchor
|
|
218
|
+
|
|
219
|
+
adapter = faker_adapter(context)
|
|
220
|
+
if adapter
|
|
221
|
+
adapter.date_backward(days: duration)
|
|
222
|
+
|
|
223
|
+
else
|
|
224
|
+
Faker::Date.backward(days: duration)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
rescue StandardError
|
|
228
|
+
Date.today - duration
|
|
229
|
+
end
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
def generate_edge(rng, context, args)
|
|
233
|
+
anchor = TimeTravel.anchor_date || Date.today
|
|
234
|
+
[anchor - 1, anchor - 365].sample(random: rng.instance_variable_get(:@random))
|
|
235
|
+
end
|
|
236
|
+
|
|
237
|
+
def generate_invalid(rng, context, args)
|
|
238
|
+
[nil, "not a date", 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
private
|
|
242
|
+
|
|
243
|
+
def parse_duration(args)
|
|
244
|
+
# Handle nested value hash like {value: {value: 2, suffix: "y"}}
|
|
245
|
+
if args[:value].is_a?(Hash)
|
|
246
|
+
value = args[:value][:value] || 1
|
|
247
|
+
suffix = args[:value][:suffix] || "d"
|
|
248
|
+
else
|
|
249
|
+
value = args[:value] || 365
|
|
250
|
+
suffix = args[:suffix] || "d"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
case suffix.to_s.downcase
|
|
254
|
+
when "y", "year", "years"
|
|
255
|
+
value * 365
|
|
256
|
+
when "m", "month", "months"
|
|
257
|
+
value * 30
|
|
258
|
+
when "d", "day", "days"
|
|
259
|
+
value
|
|
260
|
+
when "h", "hour", "hours"
|
|
261
|
+
(value / 24.0).ceil
|
|
262
|
+
else
|
|
263
|
+
value
|
|
264
|
+
end
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
# FutureDate type
|
|
269
|
+
class FutureDate < Base
|
|
270
|
+
# Generate a random future date
|
|
271
|
+
#
|
|
272
|
+
# Generates a date in the future using the specified duration.
|
|
273
|
+
#
|
|
274
|
+
# @param rng [Generator::RNG] random number generator
|
|
275
|
+
# @param context [Generator::Context] generation context
|
|
276
|
+
# @param args [Hash] type arguments
|
|
277
|
+
# @option args [Integer, Hash] :value duration value
|
|
278
|
+
# @option args [String] :suffix duration unit (d, m, y)
|
|
279
|
+
# @return [Date] date in the future
|
|
280
|
+
#
|
|
281
|
+
def generate_random(rng, context, args)
|
|
282
|
+
duration = parse_duration(args)
|
|
283
|
+
anchor = TimeTravel.anchor_date
|
|
284
|
+
return anchor + duration if anchor
|
|
285
|
+
|
|
286
|
+
adapter = faker_adapter(context)
|
|
287
|
+
if adapter
|
|
288
|
+
adapter.date_forward(days: duration)
|
|
289
|
+
|
|
290
|
+
else
|
|
291
|
+
Faker::Date.forward(days: duration)
|
|
292
|
+
end
|
|
293
|
+
|
|
294
|
+
rescue StandardError
|
|
295
|
+
Date.today + duration
|
|
296
|
+
end
|
|
297
|
+
|
|
298
|
+
|
|
299
|
+
def generate_edge(rng, context, args)
|
|
300
|
+
anchor = TimeTravel.anchor_date || Date.today
|
|
301
|
+
[anchor + 1, anchor + 365].sample(random: rng.instance_variable_get(:@random))
|
|
302
|
+
end
|
|
303
|
+
|
|
304
|
+
def generate_invalid(rng, context, args)
|
|
305
|
+
[nil, "not a date", 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
306
|
+
end
|
|
307
|
+
|
|
308
|
+
private
|
|
309
|
+
|
|
310
|
+
def parse_duration(args)
|
|
311
|
+
# Handle nested value hash like {value: {value: 7, suffix: "d"}}
|
|
312
|
+
if args[:value].is_a?(Hash)
|
|
313
|
+
value = args[:value][:value] || 1
|
|
314
|
+
suffix = args[:value][:suffix] || "d"
|
|
315
|
+
else
|
|
316
|
+
value = args[:value] || 365
|
|
317
|
+
suffix = args[:suffix] || "d"
|
|
318
|
+
end
|
|
319
|
+
|
|
320
|
+
case suffix.to_s.downcase
|
|
321
|
+
when "y", "year", "years"
|
|
322
|
+
value * 365
|
|
323
|
+
when "m", "month", "months"
|
|
324
|
+
value * 30
|
|
325
|
+
when "d", "day", "days"
|
|
326
|
+
value
|
|
327
|
+
else
|
|
328
|
+
value
|
|
329
|
+
end
|
|
330
|
+
end
|
|
331
|
+
end
|
|
332
|
+
|
|
333
|
+
# DateBetween type
|
|
334
|
+
class DateBetween < Base
|
|
335
|
+
# Generate random date between range
|
|
336
|
+
#
|
|
337
|
+
# @param rng [Generator::RNG] random number generator
|
|
338
|
+
# @param context [Generator::Context] generation context
|
|
339
|
+
# @param args [Hash] type arguments
|
|
340
|
+
# @option args [String, Date] :start start date
|
|
341
|
+
# @option args [String, Date] :from alias for :start
|
|
342
|
+
# @option args [String, Date] :end end date
|
|
343
|
+
# @option args [String, Date] :to alias for :end
|
|
344
|
+
# @return [Date] generated date
|
|
345
|
+
#
|
|
346
|
+
def generate_random(rng, context, args)
|
|
347
|
+
reference_date = TimeTravel.anchor_date || Date.today
|
|
348
|
+
start_date = parse_date(args[:start] || args[:from] || (reference_date - 365))
|
|
349
|
+
end_date = parse_date(args[:end] || args[:to] || reference_date)
|
|
350
|
+
|
|
351
|
+
# Ensure valid range
|
|
352
|
+
start_date, end_date = end_date, start_date if start_date > end_date
|
|
353
|
+
|
|
354
|
+
# Generate random date in range
|
|
355
|
+
days = (end_date - start_date).to_i
|
|
356
|
+
start_date + rng.int(0, days)
|
|
357
|
+
end
|
|
358
|
+
|
|
359
|
+
def generate_edge(rng, context, args)
|
|
360
|
+
anchor = TimeTravel.anchor_date || Date.today
|
|
361
|
+
[anchor - 365, anchor].sample(random: rng.instance_variable_get(:@random))
|
|
362
|
+
end
|
|
363
|
+
|
|
364
|
+
def generate_invalid(rng, context, args)
|
|
365
|
+
[nil, "not a date", 123, [], {}].sample(random: rng.instance_variable_get(:@random))
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
private
|
|
369
|
+
|
|
370
|
+
def parse_date(date)
|
|
371
|
+
case date
|
|
372
|
+
when Date
|
|
373
|
+
date
|
|
374
|
+
when String
|
|
375
|
+
Date.parse(date)
|
|
376
|
+
else
|
|
377
|
+
TimeTravel.anchor_date || Date.today
|
|
378
|
+
end
|
|
379
|
+
|
|
380
|
+
rescue StandardError
|
|
381
|
+
TimeTravel.anchor_date || Date.today
|
|
382
|
+
end
|
|
383
|
+
|
|
384
|
+
end
|
|
385
|
+
end
|
|
386
|
+
end
|
|
387
|
+
end
|