synthra 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +40 -0
  4. data/.yardopts +18 -0
  5. data/ACCESS_DOCS.md +116 -0
  6. data/ADVANCED_FEATURES_SUMMARY.md +245 -0
  7. data/CHANGELOG.md +498 -0
  8. data/CODE_OF_CONDUCT.md +122 -0
  9. data/CONTRIBUTING.md +307 -0
  10. data/DOCUMENTATION.md +204 -0
  11. data/DOCUMENTATION_GUIDE.md +194 -0
  12. data/LICENSE +22 -0
  13. data/README.md +1458 -0
  14. data/Rakefile +128 -0
  15. data/benchmark/README.md +591 -0
  16. data/benchmark/batch_generation.rb +63 -0
  17. data/benchmark/engine_comparison.rb +295 -0
  18. data/benchmark/single_record.rb +108 -0
  19. data/benchmark/streaming.rb +74 -0
  20. data/docs/ACTIVERECORD_INFERENCE.md +524 -0
  21. data/docs/API_SERVER.md +278 -0
  22. data/docs/CONFIG_FILE.md +315 -0
  23. data/docs/DATA_CONTRACTS.md +312 -0
  24. data/docs/ENHANCED_REPL.md +304 -0
  25. data/docs/FACTORY_BOT.md +271 -0
  26. data/docs/GITHUB_ACTION.md +420 -0
  27. data/docs/GRAPHQL_EXPORT.md +303 -0
  28. data/docs/GRAPHQL_FEDERATION.md +651 -0
  29. data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
  30. data/docs/LSP.md +189 -0
  31. data/docs/MIGRATION_GENERATOR.md +547 -0
  32. data/docs/MOCK_SERVER.md +280 -0
  33. data/docs/NATIVE_ENGINE.md +298 -0
  34. data/docs/OPENAPI_EXPORT.md +252 -0
  35. data/docs/PERFORMANCE_MODE.md +228 -0
  36. data/docs/PERSONAS.md +547 -0
  37. data/docs/PROPERTY_TESTING.md +284 -0
  38. data/docs/PROTOBUF_EXPORT.md +276 -0
  39. data/docs/QUALITY_METRICS.md +555 -0
  40. data/docs/QUICK_REFERENCE.md +88 -0
  41. data/docs/RAILS_ENGINE.md +404 -0
  42. data/docs/RAILS_INTEGRATION.md +454 -0
  43. data/docs/README.md +175 -0
  44. data/docs/REPL_QUICK_REFERENCE.md +149 -0
  45. data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
  46. data/docs/SCENARIOS.md +576 -0
  47. data/docs/SECURITY_TESTING.md +318 -0
  48. data/docs/SNAPSHOT_TESTING.md +308 -0
  49. data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
  50. data/docs/TERRAFORM_EXPORT.md +447 -0
  51. data/docs/TIME_TRAVEL.md +507 -0
  52. data/docs/USAGE_GUIDE.md +980 -0
  53. data/docs/WEBHOOK_SIMULATOR.md +579 -0
  54. data/docs/advanced_features.md +183 -0
  55. data/docs/api_reference.md +153 -0
  56. data/docs/behaviors.md +138 -0
  57. data/docs/best_practices.md +191 -0
  58. data/docs/dsl_reference.md +172 -0
  59. data/docs/examples.md +162 -0
  60. data/docs/generation_modes.md +125 -0
  61. data/docs/getting_started.md +92 -0
  62. data/docs/index.html +177 -0
  63. data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
  64. data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
  65. data/docs/troubleshooting.md +215 -0
  66. data/docs/type_reference.md +224 -0
  67. data/examples/hostile_mode_demo.rb +64 -0
  68. data/exe/synthra +6 -0
  69. data/ext/synthra_native/Cargo.lock +972 -0
  70. data/ext/synthra_native/Cargo.toml +46 -0
  71. data/ext/synthra_native/extconf.rb +41 -0
  72. data/ext/synthra_native/src/generator.rs +397 -0
  73. data/ext/synthra_native/src/lib.rs +229 -0
  74. data/ext/synthra_native/src/types.rs +1485 -0
  75. data/lib/generators/synthra/install_generator.rb +86 -0
  76. data/lib/generators/synthra/templates/api_response.dsl +15 -0
  77. data/lib/generators/synthra/templates/fake_data.rake +116 -0
  78. data/lib/generators/synthra/templates/synthra.yml +31 -0
  79. data/lib/generators/synthra/templates/synthra_support.rb +19 -0
  80. data/lib/generators/synthra/templates/user.dsl +11 -0
  81. data/lib/synthra/activerecord_inference.rb +394 -0
  82. data/lib/synthra/api.rb +381 -0
  83. data/lib/synthra/api_server.rb +718 -0
  84. data/lib/synthra/behaviors/applicator.rb +165 -0
  85. data/lib/synthra/behaviors/base.rb +155 -0
  86. data/lib/synthra/behaviors/close_connection.rb +60 -0
  87. data/lib/synthra/behaviors/deprecated.rb +100 -0
  88. data/lib/synthra/behaviors/failure.rb +69 -0
  89. data/lib/synthra/behaviors/latency.rb +103 -0
  90. data/lib/synthra/behaviors/partial_data.rb +83 -0
  91. data/lib/synthra/behaviors/randomize_order.rb +74 -0
  92. data/lib/synthra/behaviors/registry.rb +238 -0
  93. data/lib/synthra/behaviors/simulate_error.rb +79 -0
  94. data/lib/synthra/cli/commands/base.rb +105 -0
  95. data/lib/synthra/cli/commands/diff.rb +227 -0
  96. data/lib/synthra/cli/commands/docs.rb +67 -0
  97. data/lib/synthra/cli/commands/export.rb +150 -0
  98. data/lib/synthra/cli/commands/generate.rb +72 -0
  99. data/lib/synthra/cli/commands/import.rb +80 -0
  100. data/lib/synthra/cli/commands/lint.rb +53 -0
  101. data/lib/synthra/cli/commands/live.rb +57 -0
  102. data/lib/synthra/cli/commands/seed.rb +99 -0
  103. data/lib/synthra/cli/commands/validate.rb +28 -0
  104. data/lib/synthra/cli.rb +2471 -0
  105. data/lib/synthra/config_file.rb +129 -0
  106. data/lib/synthra/configuration.rb +281 -0
  107. data/lib/synthra/contracts_registry.rb +408 -0
  108. data/lib/synthra/database_seeder.rb +268 -0
  109. data/lib/synthra/deterministic_ids.rb +218 -0
  110. data/lib/synthra/documentation_generator.rb +414 -0
  111. data/lib/synthra/engine.rb +251 -0
  112. data/lib/synthra/errors.rb +1169 -0
  113. data/lib/synthra/export/base.rb +85 -0
  114. data/lib/synthra/export/csv.rb +101 -0
  115. data/lib/synthra/export/graphql.rb +266 -0
  116. data/lib/synthra/export/graphql_federation.rb +377 -0
  117. data/lib/synthra/export/graphviz.rb +258 -0
  118. data/lib/synthra/export/javascript.rb +327 -0
  119. data/lib/synthra/export/json_data.rb +61 -0
  120. data/lib/synthra/export/json_schema.rb +290 -0
  121. data/lib/synthra/export/openapi.rb +514 -0
  122. data/lib/synthra/export/protobuf.rb +483 -0
  123. data/lib/synthra/export/python.rb +560 -0
  124. data/lib/synthra/export/sql.rb +381 -0
  125. data/lib/synthra/export/sql_insert.rb +152 -0
  126. data/lib/synthra/export/terraform.rb +501 -0
  127. data/lib/synthra/export/type_mapping.rb +389 -0
  128. data/lib/synthra/export/typescript.rb +300 -0
  129. data/lib/synthra/export/xml_data.rb +104 -0
  130. data/lib/synthra/export/yaml_data.rb +56 -0
  131. data/lib/synthra/export.rb +404 -0
  132. data/lib/synthra/factory_bot_integration.rb +157 -0
  133. data/lib/synthra/field.rb +440 -0
  134. data/lib/synthra/functions/registry.rb +104 -0
  135. data/lib/synthra/generator/context.rb +372 -0
  136. data/lib/synthra/generator/engine.rb +336 -0
  137. data/lib/synthra/generator/faker_adapter.rb +425 -0
  138. data/lib/synthra/generator/modes.rb +444 -0
  139. data/lib/synthra/generator/resolver.rb +256 -0
  140. data/lib/synthra/generator/rng.rb +273 -0
  141. data/lib/synthra/generator/streamer.rb +63 -0
  142. data/lib/synthra/generator/uniqueness.rb +118 -0
  143. data/lib/synthra/initializer.rb +97 -0
  144. data/lib/synthra/limits.rb +279 -0
  145. data/lib/synthra/live_preview.rb +518 -0
  146. data/lib/synthra/loader_config.rb +78 -0
  147. data/lib/synthra/lsp/server.rb +689 -0
  148. data/lib/synthra/migration_generator.rb +301 -0
  149. data/lib/synthra/mixin.rb +174 -0
  150. data/lib/synthra/mock_server.rb +458 -0
  151. data/lib/synthra/native_engine.rb +304 -0
  152. data/lib/synthra/openapi_importer.rb +228 -0
  153. data/lib/synthra/output/json_formatter.rb +40 -0
  154. data/lib/synthra/output/ndjson_formatter.rb +55 -0
  155. data/lib/synthra/parser/ast.rb +1287 -0
  156. data/lib/synthra/parser/lexer.rb +1152 -0
  157. data/lib/synthra/parser/parser.rb +1664 -0
  158. data/lib/synthra/parser/tokens.rb +461 -0
  159. data/lib/synthra/performance_mode.rb +364 -0
  160. data/lib/synthra/personas.rb +397 -0
  161. data/lib/synthra/property_testing.rb +172 -0
  162. data/lib/synthra/quality_metrics.rb +405 -0
  163. data/lib/synthra/rails_test_helper.rb +248 -0
  164. data/lib/synthra/registry.rb +533 -0
  165. data/lib/synthra/relationships.rb +193 -0
  166. data/lib/synthra/repl/enhanced_repl.rb +607 -0
  167. data/lib/synthra/repl/formatter.rb +191 -0
  168. data/lib/synthra/scenarios.rb +423 -0
  169. data/lib/synthra/schema.rb +605 -0
  170. data/lib/synthra/schema_inheritance.rb +104 -0
  171. data/lib/synthra/schema_versioning.rb +212 -0
  172. data/lib/synthra/snapshot_testing.rb +199 -0
  173. data/lib/synthra/time_travel.rb +338 -0
  174. data/lib/synthra/type_definitions.rb +274 -0
  175. data/lib/synthra/types/address_location/addresses.rb +125 -0
  176. data/lib/synthra/types/address_location/airports.rb +200 -0
  177. data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
  178. data/lib/synthra/types/address_location/locations.rb +406 -0
  179. data/lib/synthra/types/base.rb +48 -0
  180. data/lib/synthra/types/commerce_products/commerce.rb +119 -0
  181. data/lib/synthra/types/commerce_products/companies.rb +71 -0
  182. data/lib/synthra/types/commerce_products/construction.rb +126 -0
  183. data/lib/synthra/types/commerce_products/products.rb +205 -0
  184. data/lib/synthra/types/core/collections.rb +513 -0
  185. data/lib/synthra/types/core/defaults.rb +76 -0
  186. data/lib/synthra/types/core/enums.rb +102 -0
  187. data/lib/synthra/types/core/identifiers.rb +445 -0
  188. data/lib/synthra/types/core/primitives.rb +586 -0
  189. data/lib/synthra/types/core/references.rb +466 -0
  190. data/lib/synthra/types/core/sequences.rb +304 -0
  191. data/lib/synthra/types/crypto/crypto.rb +162 -0
  192. data/lib/synthra/types/date_time/dates.rb +387 -0
  193. data/lib/synthra/types/finance_banking/banking.rb +424 -0
  194. data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
  195. data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
  196. data/lib/synthra/types/formula.rb +184 -0
  197. data/lib/synthra/types/health_medical/medical.rb +150 -0
  198. data/lib/synthra/types/hostile_payloads.rb +122 -0
  199. data/lib/synthra/types/json_array.rb +51 -0
  200. data/lib/synthra/types/media_entertainment/media.rb +62 -0
  201. data/lib/synthra/types/naughty_string.rb +30 -0
  202. data/lib/synthra/types/personal_names/chinese.rb +41 -0
  203. data/lib/synthra/types/personal_names/identifiers.rb +187 -0
  204. data/lib/synthra/types/personal_names/names.rb +246 -0
  205. data/lib/synthra/types/personal_names/national_id.rb +89 -0
  206. data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
  207. data/lib/synthra/types/regex.rb +248 -0
  208. data/lib/synthra/types/registry.rb +182 -0
  209. data/lib/synthra/types/repeating_element.rb +50 -0
  210. data/lib/synthra/types/scenario.rb +29 -0
  211. data/lib/synthra/types/technology_internet/apps.rb +67 -0
  212. data/lib/synthra/types/technology_internet/communication.rb +162 -0
  213. data/lib/synthra/types/technology_internet/devices.rb +80 -0
  214. data/lib/synthra/types/technology_internet/formats.rb +139 -0
  215. data/lib/synthra/types/technology_internet/networking.rb +143 -0
  216. data/lib/synthra/types/template.rb +92 -0
  217. data/lib/synthra/types/text_content/business.rb +128 -0
  218. data/lib/synthra/types/text_content/colors.rb +70 -0
  219. data/lib/synthra/types/text_content/misc.rb +237 -0
  220. data/lib/synthra/types/text_content/security.rb +130 -0
  221. data/lib/synthra/types/text_content/text_generation.rb +532 -0
  222. data/lib/synthra/types/travel/travel.rb +150 -0
  223. data/lib/synthra/utils/string_distance.rb +133 -0
  224. data/lib/synthra/validator/dsl_validator.rb +339 -0
  225. data/lib/synthra/validator/path_validator.rb +615 -0
  226. data/lib/synthra/version.rb +35 -0
  227. data/lib/synthra/webhook_simulator.rb +341 -0
  228. data/lib/synthra.rb +259 -0
  229. data/schemas/address.dsl +16 -0
  230. data/schemas/api_response.dsl +8 -0
  231. data/schemas/error_payload.dsl +9 -0
  232. data/schemas/order.dsl +10 -0
  233. data/schemas/order_item.dsl +8 -0
  234. data/schemas/payment.dsl +11 -0
  235. data/schemas/social_post.dsl +13 -0
  236. data/schemas/user.dsl +10 -0
  237. data/scripts/batch_fix_all.rb +96 -0
  238. data/scripts/delete_old_files.rb +32 -0
  239. data/scripts/fix_all_grouped_files.rb +137 -0
  240. data/scripts/fix_all_indentation.rb +48 -0
  241. data/scripts/fix_all_syntax.rb +124 -0
  242. data/scripts/fix_grouped_files.rb +184 -0
  243. data/scripts/fix_syntax_errors.rb +108 -0
  244. data/scripts/group_domain_types.rb +112 -0
  245. data/scripts/group_domain_types_fixed.rb +150 -0
  246. data/scripts/merge_domains_to_one_file.rb +68 -0
  247. data/scripts/move_existing_types.rb +130 -0
  248. data/scripts/split_grouped_types.rb +142 -0
  249. data/tech_docs/README.md +134 -0
  250. data/tech_docs/advanced/streaming.md +405 -0
  251. data/tech_docs/advanced/thread_safety.md +253 -0
  252. data/tech_docs/api/overview.md +485 -0
  253. data/tech_docs/appendices/type_chart.md +193 -0
  254. data/tech_docs/basic_concepts.md +383 -0
  255. data/tech_docs/behaviors/overview.md +346 -0
  256. data/tech_docs/dsl/complex_types.md +790 -0
  257. data/tech_docs/dsl/core_types.md +464 -0
  258. data/tech_docs/dsl/datetime_types.md +325 -0
  259. data/tech_docs/dsl/field_modifiers.md +414 -0
  260. data/tech_docs/dsl/grammar.md +431 -0
  261. data/tech_docs/dsl/schema_definition.md +399 -0
  262. data/tech_docs/export/README.md +276 -0
  263. data/tech_docs/installation.md +273 -0
  264. data/tech_docs/integration/ci_cd.md +707 -0
  265. data/tech_docs/integration/ci_cd_guide.md +579 -0
  266. data/tech_docs/integration/factory_bot.md +485 -0
  267. data/tech_docs/integration/rails.md +630 -0
  268. data/tech_docs/integration/rspec.md +449 -0
  269. data/tech_docs/modes/overview.md +350 -0
  270. data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
  271. data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
  272. data/tech_docs/quick_start.md +256 -0
  273. data/tech_docs/templates/README.md +805 -0
  274. data/tech_docs/tutorials/advanced.md +371 -0
  275. data/tech_docs/tutorials/getting_started.md +189 -0
  276. data/tech_docs/tutorials/intermediate.md +231 -0
  277. data/tech_docs/tutorials/template_gallery.md +569 -0
  278. data/tech_example/01_basic_usage.rb +238 -0
  279. data/tech_example/02_types_demo.rb +336 -0
  280. data/tech_example/04_cli_usage.md +429 -0
  281. data/tech_example/05_database_seeding.rb +283 -0
  282. data/tech_example/07_rspec_integration.rb +359 -0
  283. data/tech_example/10_custom_types.rb +387 -0
  284. data/tech_example/12_behaviors.rb +372 -0
  285. data/tech_example/13_twitter_dm_example.rb +120 -0
  286. data/tech_example/14_exact_json_structure.rb +253 -0
  287. data/tech_example/16_lsp_server.rb +56 -0
  288. data/tech_example/17_property_testing.rb +92 -0
  289. data/tech_example/18_enhanced_repl.rb +120 -0
  290. data/tech_example/19_security_fuzzing.rb +145 -0
  291. data/tech_example/NEW_FEATURES.md +169 -0
  292. data/tech_example/README.md +118 -0
  293. data/tech_example/schemas/api_response.dsl +123 -0
  294. data/tech_example/schemas/ecommerce.dsl +121 -0
  295. data/tech_example/schemas/twitter_dm.dsl +109 -0
  296. data/tech_example/schemas/user.dsl +36 -0
  297. data/vscode-extension/README.md +246 -0
  298. data/vscode-extension/language-configuration.json +31 -0
  299. data/vscode-extension/package.json +55 -0
  300. data/vscode-extension/snippets/fakedatadsl.json +198 -0
  301. data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
  302. metadata +478 -0
@@ -0,0 +1,507 @@
1
+ # Time Travel for Data
2
+
3
+ FakeDataDSL's Time Travel feature lets you generate data **as it would have existed at a specific point in time**, or create realistic progressions of data over time. Perfect for testing time-sensitive features, historical reports, and temporal queries.
4
+
5
+ ## Quick Start
6
+
7
+ ```ruby
8
+ # Generate data from 30 days ago
9
+ FakeDataDSL::TimeTravel.at(30.days.ago) do
10
+ user = FakeDataDSL.generate("User")
11
+ # user[:created_at] is 30 days ago
12
+ # user[:updated_at] is 30 days ago
13
+ end
14
+
15
+ # Generate data progression
16
+ orders = FakeDataDSL::TimeTravel.progression("Order",
17
+ from: 1.year.ago,
18
+ to: Time.current,
19
+ count: 12
20
+ )
21
+ # 12 orders spread across the past year
22
+ ```
23
+
24
+ ## Basic Usage
25
+
26
+ ### Generate at Specific Time
27
+
28
+ ```ruby
29
+ # All timestamps generated within the block are relative to the travel time
30
+ FakeDataDSL::TimeTravel.at(Date.new(2025, 1, 1)) do
31
+ user = FakeDataDSL.generate("User")
32
+ # user[:created_at] => 2025-01-01 (random time on that day)
33
+ end
34
+
35
+ # With specific time
36
+ FakeDataDSL::TimeTravel.at(Time.new(2025, 6, 15, 14, 30, 0)) do
37
+ event = FakeDataDSL.generate("Event")
38
+ # event[:scheduled_at] => around 2025-06-15 14:30
39
+ end
40
+ ```
41
+
42
+ ### Generate Between Dates
43
+
44
+ ```ruby
45
+ # Timestamps fall randomly within the range
46
+ FakeDataDSL::TimeTravel.between(1.month.ago, Time.current) do
47
+ log = FakeDataDSL.generate("AuditLog")
48
+ # log[:created_at] is somewhere in the last month
49
+ end
50
+
51
+ # Generate multiple records spread across range
52
+ records = FakeDataDSL::TimeTravel.between(1.year.ago, Time.current) do
53
+ 10.times.map { FakeDataDSL.generate("Transaction") }
54
+ end
55
+ # Transactions spread randomly across the year
56
+ ```
57
+
58
+ ## Time Progressions
59
+
60
+ ### Linear Progression
61
+
62
+ Generate records evenly distributed over time:
63
+
64
+ ```ruby
65
+ # 12 monthly reports
66
+ reports = FakeDataDSL::TimeTravel.progression("MonthlyReport",
67
+ from: 1.year.ago,
68
+ to: Time.current,
69
+ count: 12
70
+ )
71
+
72
+ reports.each_with_index do |report, i|
73
+ puts "#{report[:created_at].strftime('%B %Y')}: $#{report[:revenue]}"
74
+ end
75
+ # January 2025: $12,345
76
+ # February 2025: $13,456
77
+ # ...
78
+ ```
79
+
80
+ ### Custom Intervals
81
+
82
+ ```ruby
83
+ # Daily records
84
+ daily = FakeDataDSL::TimeTravel.progression("DailyMetric",
85
+ from: 7.days.ago,
86
+ to: Time.current,
87
+ interval: 1.day
88
+ )
89
+
90
+ # Hourly records
91
+ hourly = FakeDataDSL::TimeTravel.progression("HourlyLog",
92
+ from: 24.hours.ago,
93
+ to: Time.current,
94
+ interval: 1.hour
95
+ )
96
+
97
+ # Weekly records
98
+ weekly = FakeDataDSL::TimeTravel.progression("WeeklyDigest",
99
+ from: 3.months.ago,
100
+ to: Time.current,
101
+ interval: 1.week
102
+ )
103
+ ```
104
+
105
+ ### With Variations
106
+
107
+ ```ruby
108
+ # Add realistic variation to timestamps
109
+ orders = FakeDataDSL::TimeTravel.progression("Order",
110
+ from: 1.month.ago,
111
+ to: Time.current,
112
+ count: 30,
113
+ jitter: 6.hours # ±6 hours variation
114
+ )
115
+ ```
116
+
117
+ ## Time Series Data
118
+
119
+ ### Generate Time Series
120
+
121
+ ```ruby
122
+ # Stock prices over time
123
+ prices = FakeDataDSL::TimeTravel.time_series("StockPrice",
124
+ from: 1.year.ago,
125
+ to: Time.current,
126
+ interval: 1.day,
127
+ fields: {
128
+ open: { type: :trending, start: 100, volatility: 0.02 },
129
+ close: { type: :trending, start: 100, volatility: 0.02 },
130
+ volume: { type: :random, range: 1000..10000 }
131
+ }
132
+ )
133
+ ```
134
+
135
+ ### Trending Data
136
+
137
+ ```ruby
138
+ # Metrics that grow over time
139
+ FakeDataDSL::TimeTravel.time_series("UserMetrics",
140
+ from: 1.year.ago,
141
+ to: Time.current,
142
+ interval: 1.week,
143
+ trend: :growth, # :growth, :decline, :stable, :seasonal
144
+ growth_rate: 0.05 # 5% weekly growth
145
+ )
146
+ ```
147
+
148
+ ### Seasonal Patterns
149
+
150
+ ```ruby
151
+ # E-commerce orders with seasonal variation
152
+ orders = FakeDataDSL::TimeTravel.time_series("Order",
153
+ from: 1.year.ago,
154
+ to: Time.current,
155
+ interval: 1.day,
156
+ seasonality: {
157
+ pattern: :weekly,
158
+ peaks: [5, 6], # Saturday, Sunday
159
+ peak_multiplier: 2.5
160
+ }
161
+ )
162
+ ```
163
+
164
+ ## Schema Annotations
165
+
166
+ ### In DSL Files
167
+
168
+ ```ruby
169
+ # db/schemas/historical_order.dsl
170
+ HistoricalOrder:
171
+ @time_travel -365.days..-1.day
172
+
173
+ id: uuid
174
+ created_at: timestamp
175
+ shipped_at: timestamp(after: created_at, within: 7.days)
176
+ delivered_at: timestamp(after: shipped_at, within: 14.days)
177
+ ```
178
+
179
+ ### Relative Timestamps
180
+
181
+ ```ruby
182
+ # Timestamps that depend on each other
183
+ Order:
184
+ id: uuid
185
+ created_at: timestamp
186
+ confirmed_at: timestamp(after: created_at, within: 1.hour)
187
+ shipped_at: timestamp(after: confirmed_at, within: 3.days)
188
+ delivered_at: timestamp(after: shipped_at, within: 7.days)
189
+ ```
190
+
191
+ When time traveling:
192
+
193
+ ```ruby
194
+ FakeDataDSL::TimeTravel.at(30.days.ago) do
195
+ order = FakeDataDSL.generate("Order")
196
+ # order[:created_at] => ~30 days ago
197
+ # order[:confirmed_at] => ~30 days ago (within 1 hour of created_at)
198
+ # order[:shipped_at] => ~27 days ago (within 3 days of confirmed_at)
199
+ # order[:delivered_at] => ~20 days ago (within 7 days of shipped_at)
200
+ end
201
+ ```
202
+
203
+ ## Nested Time Contexts
204
+
205
+ ### Time Blocks
206
+
207
+ ```ruby
208
+ FakeDataDSL::TimeTravel.at(1.year.ago) do
209
+ old_user = FakeDataDSL.generate("User")
210
+
211
+ FakeDataDSL::TimeTravel.at(6.months.ago) do
212
+ mid_user = FakeDataDSL.generate("User")
213
+
214
+ FakeDataDSL::TimeTravel.at(Time.current) do
215
+ new_user = FakeDataDSL.generate("User")
216
+ end
217
+ end
218
+ end
219
+ ```
220
+
221
+ ### Progressive Nesting
222
+
223
+ ```ruby
224
+ # User signs up, then creates content over time
225
+ FakeDataDSL::TimeTravel.at(1.year.ago) do
226
+ user = FakeDataDSL.generate("User")
227
+
228
+ # User writes posts over the following months
229
+ posts = (1..12).map do |month|
230
+ FakeDataDSL::TimeTravel.at(1.year.ago + month.months) do
231
+ FakeDataDSL.generate("Post", user_id: user[:id])
232
+ end
233
+ end
234
+ end
235
+ ```
236
+
237
+ ## Integration with Scenarios
238
+
239
+ ```ruby
240
+ FakeDataDSL::Scenarios.define(:historical_activity) do
241
+ # User from a year ago
242
+ let(:veteran_user) {
243
+ FakeDataDSL::TimeTravel.at(1.year.ago) do
244
+ create(:user, name: "Veteran User")
245
+ end
246
+ }
247
+
248
+ # Their posts over time
249
+ let(:posts) {
250
+ FakeDataDSL::TimeTravel.progression("Post",
251
+ from: 11.months.ago,
252
+ to: Time.current,
253
+ count: 24
254
+ ).map { |p| p.merge(user_id: veteran_user[:id]) }
255
+ }
256
+
257
+ # Recent new user
258
+ let(:new_user) {
259
+ FakeDataDSL::TimeTravel.at(1.week.ago) do
260
+ create(:user, name: "New User")
261
+ end
262
+ }
263
+ end
264
+ ```
265
+
266
+ ## Time Zone Handling
267
+
268
+ ### Explicit Time Zones
269
+
270
+ ```ruby
271
+ # Generate in specific timezone
272
+ FakeDataDSL::TimeTravel.at(Time.current, zone: "America/New_York") do
273
+ event = FakeDataDSL.generate("Event")
274
+ # event[:start_time] is in Eastern time
275
+ end
276
+
277
+ # Generate in user's timezone
278
+ FakeDataDSL::TimeTravel.at(Time.current, zone: user.time_zone) do
279
+ notification = FakeDataDSL.generate("Notification")
280
+ end
281
+ ```
282
+
283
+ ### Multiple Zones
284
+
285
+ ```ruby
286
+ # Generate events across timezones
287
+ offices = ["America/New_York", "Europe/London", "Asia/Tokyo"]
288
+ events = offices.map do |zone|
289
+ FakeDataDSL::TimeTravel.at(Time.current, zone: zone) do
290
+ FakeDataDSL.generate("Meeting", timezone: zone)
291
+ end
292
+ end
293
+ ```
294
+
295
+ ## Testing Patterns
296
+
297
+ ### Historical Data Tests
298
+
299
+ ```ruby
300
+ RSpec.describe "ReportGenerator" do
301
+ it "generates monthly reports" do
302
+ # Create historical data
303
+ FakeDataDSL::TimeTravel.progression("Order",
304
+ from: 3.months.ago,
305
+ to: Time.current,
306
+ count: 90
307
+ ).each { |order| Order.create!(order) }
308
+
309
+ report = ReportGenerator.monthly_summary(2.months.ago)
310
+ expect(report.total_orders).to be > 0
311
+ end
312
+ end
313
+ ```
314
+
315
+ ### Time-Sensitive Features
316
+
317
+ ```ruby
318
+ RSpec.describe "SubscriptionExpiry" do
319
+ it "expires subscriptions correctly" do
320
+ # Create subscription that expires today
321
+ subscription = FakeDataDSL::TimeTravel.at(31.days.ago) do
322
+ Subscription.create!(
323
+ FakeDataDSL.generate("Subscription", duration: 30.days)
324
+ )
325
+ end
326
+
327
+ expect(subscription).to be_expired
328
+ end
329
+ end
330
+ ```
331
+
332
+ ### Aging Data
333
+
334
+ ```ruby
335
+ RSpec.describe "DataRetention" do
336
+ it "purges old records" do
337
+ # Create records at various ages
338
+ old_record = FakeDataDSL::TimeTravel.at(2.years.ago) do
339
+ AuditLog.create!(FakeDataDSL.generate("AuditLog"))
340
+ end
341
+
342
+ recent_record = FakeDataDSL::TimeTravel.at(1.month.ago) do
343
+ AuditLog.create!(FakeDataDSL.generate("AuditLog"))
344
+ end
345
+
346
+ DataRetention.purge_old_records!
347
+
348
+ expect { old_record.reload }.to raise_error(ActiveRecord::RecordNotFound)
349
+ expect(recent_record.reload).to be_present
350
+ end
351
+ end
352
+ ```
353
+
354
+ ## API Reference
355
+
356
+ ### TimeTravel.at
357
+
358
+ ```ruby
359
+ FakeDataDSL::TimeTravel.at(time, options = {}, &block)
360
+ ```
361
+
362
+ **Parameters:**
363
+ - `time` - Time/DateTime/Date to travel to
364
+ - `options[:zone]` - Time zone name (default: system)
365
+ - `block` - Block where all timestamps are relative to `time`
366
+
367
+ **Returns:** Result of the block
368
+
369
+ ### TimeTravel.between
370
+
371
+ ```ruby
372
+ FakeDataDSL::TimeTravel.between(start_time, end_time, options = {}, &block)
373
+ ```
374
+
375
+ **Parameters:**
376
+ - `start_time` - Start of the time range
377
+ - `end_time` - End of the time range
378
+ - `options[:distribution]` - :uniform (default), :weighted_recent, :weighted_old
379
+ - `block` - Block where timestamps fall within range
380
+
381
+ **Returns:** Result of the block
382
+
383
+ ### TimeTravel.progression
384
+
385
+ ```ruby
386
+ FakeDataDSL::TimeTravel.progression(schema_name, options = {})
387
+ ```
388
+
389
+ **Parameters:**
390
+ - `schema_name` - Name of the schema to generate
391
+ - `options[:from]` - Start time (required)
392
+ - `options[:to]` - End time (required)
393
+ - `options[:count]` - Number of records
394
+ - `options[:interval]` - Fixed interval between records
395
+ - `options[:jitter]` - Random variation for timestamps
396
+ - `options[:overrides]` - Field overrides
397
+
398
+ **Returns:** Array of generated records
399
+
400
+ ### TimeTravel.time_series
401
+
402
+ ```ruby
403
+ FakeDataDSL::TimeTravel.time_series(schema_name, options = {})
404
+ ```
405
+
406
+ **Parameters:**
407
+ - `schema_name` - Name of the schema
408
+ - `options[:from]` - Start time
409
+ - `options[:to]` - End time
410
+ - `options[:interval]` - Time between data points
411
+ - `options[:trend]` - :growth, :decline, :stable, :seasonal
412
+ - `options[:growth_rate]` - Rate of change per interval
413
+ - `options[:seasonality]` - Seasonal pattern configuration
414
+ - `options[:fields]` - Field-specific configurations
415
+
416
+ **Returns:** Array of time series data points
417
+
418
+ ## Configuration
419
+
420
+ ```ruby
421
+ FakeDataDSL::TimeTravel.configure do |config|
422
+ # Default time zone
423
+ config.default_zone = "UTC"
424
+
425
+ # Default jitter for progressions
426
+ config.default_jitter = 0
427
+
428
+ # Whether to freeze time during generation
429
+ config.freeze_time = true
430
+
431
+ # Hook for before/after time travel
432
+ config.before_travel = ->(time) { Rails.logger.debug "Traveling to #{time}" }
433
+ config.after_travel = ->(time) { Rails.logger.debug "Returned from #{time}" }
434
+ end
435
+ ```
436
+
437
+ ## Best Practices
438
+
439
+ ### 1. Use Relative Times
440
+
441
+ ```ruby
442
+ # Good: Relative times adapt to when tests run
443
+ FakeDataDSL::TimeTravel.at(30.days.ago) { ... }
444
+
445
+ # Avoid: Hardcoded dates become stale
446
+ FakeDataDSL::TimeTravel.at(Date.new(2025, 1, 1)) { ... }
447
+ ```
448
+
449
+ ### 2. Be Explicit About Relationships
450
+
451
+ ```ruby
452
+ # Good: Clear temporal relationships
453
+ Order:
454
+ created_at: timestamp
455
+ shipped_at: timestamp(after: created_at) # Explicit dependency
456
+
457
+ # Avoid: Implicit relationships that may not hold
458
+ Order:
459
+ created_at: timestamp
460
+ shipped_at: timestamp # Could be before created_at!
461
+ ```
462
+
463
+ ### 3. Test Edge Cases
464
+
465
+ ```ruby
466
+ # Test around daylight saving time
467
+ FakeDataDSL::TimeTravel.at(Time.new(2025, 3, 9, 2, 30, 0, "-05:00")) do
468
+ # DST transition edge case
469
+ end
470
+
471
+ # Test at year boundaries
472
+ FakeDataDSL::TimeTravel.at(Time.new(2025, 12, 31, 23, 59, 59)) do
473
+ # Year-end edge case
474
+ end
475
+ ```
476
+
477
+ ## Troubleshooting
478
+
479
+ ### Timestamps Not Affected
480
+
481
+ ```ruby
482
+ # Make sure you're using the block
483
+ FakeDataDSL::TimeTravel.at(1.month.ago) do
484
+ FakeDataDSL.generate("User") # ✓ Affected
485
+ end
486
+
487
+ FakeDataDSL.generate("User") # ✗ Not affected (outside block)
488
+ ```
489
+
490
+ ### Time Zone Confusion
491
+
492
+ ```ruby
493
+ # Always be explicit when time zones matter
494
+ FakeDataDSL::TimeTravel.at(
495
+ Time.current.in_time_zone("America/New_York"),
496
+ zone: "America/New_York"
497
+ ) do
498
+ # Clear time zone handling
499
+ end
500
+ ```
501
+
502
+ ## See Also
503
+
504
+ - [Scenarios](SCENARIOS.md)
505
+ - [Generation Modes](generation_modes.md)
506
+ - [Timestamp Type Reference](type_reference.md#timestamp)
507
+ - [Testing Best Practices](best_practices.md)