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
data/docs/USAGE_GUIDE.md
ADDED
|
@@ -0,0 +1,980 @@
|
|
|
1
|
+
# FakeDataDSL Usage Guide
|
|
2
|
+
|
|
3
|
+
Complete guide on how to use FakeDataDSL: DSL syntax, types, formulas, unique constraints, and options.
|
|
4
|
+
|
|
5
|
+
## Table of Contents
|
|
6
|
+
|
|
7
|
+
- [Basic DSL Syntax](#basic-dsl-syntax)
|
|
8
|
+
- [Types](#types)
|
|
9
|
+
- [Formula](#formula)
|
|
10
|
+
- [Unique Constraints](#unique-constraints)
|
|
11
|
+
- [Options and Modifiers](#options-and-modifiers)
|
|
12
|
+
- [Deterministic Generation (Same Data Every Time)](#deterministic-generation-same-data-every-time)
|
|
13
|
+
- [Complete Examples](#complete-examples)
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
## Basic DSL Syntax
|
|
18
|
+
|
|
19
|
+
### Schema Definition
|
|
20
|
+
|
|
21
|
+
A schema starts with a name followed by a colon:
|
|
22
|
+
|
|
23
|
+
```ruby
|
|
24
|
+
SchemaName:
|
|
25
|
+
field1: type1
|
|
26
|
+
field2: type2
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### Field Definition
|
|
30
|
+
|
|
31
|
+
Basic field syntax:
|
|
32
|
+
|
|
33
|
+
```
|
|
34
|
+
field_name: type
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
**Example:**
|
|
38
|
+
|
|
39
|
+
```ruby
|
|
40
|
+
User:
|
|
41
|
+
id: uuid
|
|
42
|
+
name: name
|
|
43
|
+
email: email
|
|
44
|
+
age: number(18..65)
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
### Comments
|
|
48
|
+
|
|
49
|
+
Use `#` for comments:
|
|
50
|
+
|
|
51
|
+
```ruby
|
|
52
|
+
User:
|
|
53
|
+
# Primary identifier
|
|
54
|
+
id: uuid
|
|
55
|
+
name: name # User's full name
|
|
56
|
+
email: email
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## Types
|
|
62
|
+
|
|
63
|
+
### Basic Types
|
|
64
|
+
|
|
65
|
+
FakeDataDSL provides many built-in types:
|
|
66
|
+
|
|
67
|
+
```ruby
|
|
68
|
+
User:
|
|
69
|
+
# Identifiers
|
|
70
|
+
id: uuid
|
|
71
|
+
user_id: id_sequence
|
|
72
|
+
|
|
73
|
+
# Text
|
|
74
|
+
name: name
|
|
75
|
+
first_name: first_name
|
|
76
|
+
last_name: last_name
|
|
77
|
+
email: email
|
|
78
|
+
bio: text(50..500)
|
|
79
|
+
username: username
|
|
80
|
+
|
|
81
|
+
# Numbers
|
|
82
|
+
age: number(18..100)
|
|
83
|
+
score: number(min: 0, max: 100)
|
|
84
|
+
price: number(10.0..99.99)
|
|
85
|
+
|
|
86
|
+
# Boolean
|
|
87
|
+
active: boolean
|
|
88
|
+
verified: boolean(true: 80%) # 80% chance of true
|
|
89
|
+
|
|
90
|
+
# Dates
|
|
91
|
+
created_at: timestamp
|
|
92
|
+
birth_date: date
|
|
93
|
+
last_login: past_date(days: 30)
|
|
94
|
+
|
|
95
|
+
# Location
|
|
96
|
+
country: country
|
|
97
|
+
city: city
|
|
98
|
+
address: address
|
|
99
|
+
postal_code: postal_code
|
|
100
|
+
|
|
101
|
+
# Internet
|
|
102
|
+
website: url
|
|
103
|
+
ip_address: ip
|
|
104
|
+
mac_address: mac_address
|
|
105
|
+
user_agent: user_agent
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Type Arguments
|
|
109
|
+
|
|
110
|
+
Many types accept arguments to customize generation:
|
|
111
|
+
|
|
112
|
+
#### Number with Range
|
|
113
|
+
|
|
114
|
+
```ruby
|
|
115
|
+
age: number(18..65) # Range
|
|
116
|
+
price: number(min: 10, max: 100) # Min/max
|
|
117
|
+
score: number(0..100) # Integer range
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
#### Text with Length
|
|
121
|
+
|
|
122
|
+
```ruby
|
|
123
|
+
title: text(10..50) # 10-50 characters
|
|
124
|
+
description: text(100) # Exactly 100 characters
|
|
125
|
+
bio: text(min: 50, max: 500)
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
#### Enum with Weights
|
|
129
|
+
|
|
130
|
+
```ruby
|
|
131
|
+
status: enum(active, inactive, pending)
|
|
132
|
+
priority: enum(low: 50%, medium: 30%, high: 20%)
|
|
133
|
+
role: enum(user: 80%, admin: 10%, moderator: 10%)
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
#### Array Types
|
|
137
|
+
|
|
138
|
+
```ruby
|
|
139
|
+
tags: array(text, 3..10) # Array of text, 3-10 items
|
|
140
|
+
scores: array(number(0..100), 5) # Array of numbers, exactly 5 items
|
|
141
|
+
items: array(Item, 1..5) # Array of nested schemas
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
#### Date Types
|
|
145
|
+
|
|
146
|
+
```ruby
|
|
147
|
+
created_at: timestamp
|
|
148
|
+
birth_date: date
|
|
149
|
+
last_week: past_date(days: 7)
|
|
150
|
+
next_month: future_date(days: 30)
|
|
151
|
+
between: date_between(start: "2024-01-01", end: "2024-12-31")
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### All Available Types
|
|
155
|
+
|
|
156
|
+
See [Type Reference](type_reference.md) for a complete list of all 80+ types including:
|
|
157
|
+
|
|
158
|
+
- **Personal**: `first_name`, `last_name`, `full_name`, `gender`, `age`, `username`, `email`, `phone`, `national_id`
|
|
159
|
+
- **Location**: `country`, `city`, `state`, `address`, `latitude`, `longitude`, `timezone`, `postal_code`
|
|
160
|
+
- **Commerce**: `product_name`, `product_category`, `sku`, `price`, `discount`, `tax_rate`
|
|
161
|
+
- **Finance**: `credit_card`, `bank_name`, `account_number`, `iban`, `money`, `currency`, `transaction_amount`
|
|
162
|
+
- **Date & Time**: `date`, `time`, `datetime`, `timestamp`, `past_date`, `future_date`, `date_between`
|
|
163
|
+
- **Internet/IT**: `uuid`, `ip`, `ipv6`, `mac_address`, `domain`, `url`, `user_agent`
|
|
164
|
+
- **Crypto**: `hash`, `bitcoin_address`, `ethereum_address`, `private_key`
|
|
165
|
+
- **Health**: `blood_type`, `bmi`, `heart_rate`, `medical_code`
|
|
166
|
+
- **Travel**: `airport_code`, `airline`, `flight_number`, `seat_number`
|
|
167
|
+
- **Advanced**: `sequence`, `default_value`, `formula`, `template`, `regex`, `json_array`
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## Formula
|
|
172
|
+
|
|
173
|
+
The `formula` type computes values from other fields using mathematical expressions.
|
|
174
|
+
|
|
175
|
+
### Basic Formula
|
|
176
|
+
|
|
177
|
+
```ruby
|
|
178
|
+
Order:
|
|
179
|
+
quantity: number(1..10)
|
|
180
|
+
unit_price: number(10..100)
|
|
181
|
+
total: formula(quantity * unit_price)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
**Generated Example:**
|
|
185
|
+
```ruby
|
|
186
|
+
{
|
|
187
|
+
"quantity" => 5,
|
|
188
|
+
"unit_price" => 50,
|
|
189
|
+
"total" => 250
|
|
190
|
+
}
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
### Complex Formulas
|
|
194
|
+
|
|
195
|
+
```ruby
|
|
196
|
+
Order:
|
|
197
|
+
quantity: number(1..10)
|
|
198
|
+
unit_price: number(10..100)
|
|
199
|
+
subtotal: formula(quantity * unit_price)
|
|
200
|
+
tax_rate: number(0.05..0.15)
|
|
201
|
+
tax: formula(subtotal * tax_rate)
|
|
202
|
+
discount: number(0..20)
|
|
203
|
+
discount_amount: formula(subtotal * discount / 100)
|
|
204
|
+
total: formula(subtotal + tax - discount_amount)
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**Generated Example:**
|
|
208
|
+
```ruby
|
|
209
|
+
{
|
|
210
|
+
"quantity" => 3,
|
|
211
|
+
"unit_price" => 75,
|
|
212
|
+
"subtotal" => 225,
|
|
213
|
+
"tax_rate" => 0.08,
|
|
214
|
+
"tax" => 18,
|
|
215
|
+
"discount" => 10,
|
|
216
|
+
"discount_amount" => 22.5,
|
|
217
|
+
"total" => 220.5
|
|
218
|
+
}
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
### Formula Operations
|
|
222
|
+
|
|
223
|
+
Formulas support:
|
|
224
|
+
|
|
225
|
+
- **Arithmetic**: `+`, `-`, `*`, `/`, `%`
|
|
226
|
+
- **Parentheses**: `(quantity * price) + tax`
|
|
227
|
+
- **Field References**: Use field names directly in expressions
|
|
228
|
+
- **Numbers**: `total * 0.08`, `price + 10`
|
|
229
|
+
|
|
230
|
+
**Example:**
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
Product:
|
|
234
|
+
base_price: number(10..100)
|
|
235
|
+
markup: number(1.1..1.5)
|
|
236
|
+
final_price: formula(base_price * markup)
|
|
237
|
+
shipping: number(5..15)
|
|
238
|
+
total: formula(final_price + shipping)
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
### Formula Limitations
|
|
242
|
+
|
|
243
|
+
- Only works with **numeric** field values
|
|
244
|
+
- Field must be generated **before** the formula field
|
|
245
|
+
- Returns `0` if expression fails to evaluate
|
|
246
|
+
- No string concatenation (use `template` type instead)
|
|
247
|
+
|
|
248
|
+
---
|
|
249
|
+
|
|
250
|
+
## Unique Constraints
|
|
251
|
+
|
|
252
|
+
The `unique` modifier ensures generated values are unique within a batch.
|
|
253
|
+
|
|
254
|
+
### Basic Unique
|
|
255
|
+
|
|
256
|
+
```ruby
|
|
257
|
+
User:
|
|
258
|
+
id: uuid unique
|
|
259
|
+
email: email unique
|
|
260
|
+
username: username unique
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Unique with Types
|
|
264
|
+
|
|
265
|
+
```ruby
|
|
266
|
+
Product:
|
|
267
|
+
sku: sku unique
|
|
268
|
+
code: text(10) unique
|
|
269
|
+
serial_number: sequence(start: 1000) unique
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Unique with Enums
|
|
273
|
+
|
|
274
|
+
```ruby
|
|
275
|
+
Order:
|
|
276
|
+
status: enum(pending, processing, completed) unique
|
|
277
|
+
# Each generated order will have a different status
|
|
278
|
+
```
|
|
279
|
+
|
|
280
|
+
### How Unique Works
|
|
281
|
+
|
|
282
|
+
1. **Per-Batch**: Uniqueness is enforced within a single `generate_many` call
|
|
283
|
+
2. **Retries**: Generator retries up to `max_unique_retries` times (default: 100)
|
|
284
|
+
3. **Error**: Raises `UniquenessError` if unique value cannot be generated
|
|
285
|
+
|
|
286
|
+
**Example:**
|
|
287
|
+
|
|
288
|
+
```ruby
|
|
289
|
+
registry = FakeDataDSL::Registry.new
|
|
290
|
+
registry.load_string(<<~DSL)
|
|
291
|
+
User:
|
|
292
|
+
id: uuid unique
|
|
293
|
+
email: email unique
|
|
294
|
+
DSL
|
|
295
|
+
|
|
296
|
+
schema = registry.schema("User")
|
|
297
|
+
|
|
298
|
+
# Generate 10 unique users
|
|
299
|
+
users = schema.generate_many(10, seed: 42)
|
|
300
|
+
# All users will have different IDs and emails
|
|
301
|
+
```
|
|
302
|
+
|
|
303
|
+
### Unique Configuration
|
|
304
|
+
|
|
305
|
+
Configure max retries:
|
|
306
|
+
|
|
307
|
+
```ruby
|
|
308
|
+
FakeDataDSL.configure do |config|
|
|
309
|
+
config.max_unique_retries = 200 # Default: 100
|
|
310
|
+
end
|
|
311
|
+
```
|
|
312
|
+
|
|
313
|
+
---
|
|
314
|
+
|
|
315
|
+
## Options and Modifiers
|
|
316
|
+
|
|
317
|
+
### Nullable Fields
|
|
318
|
+
|
|
319
|
+
Add `?` to make a field nullable (can be `nil`):
|
|
320
|
+
|
|
321
|
+
```ruby
|
|
322
|
+
User:
|
|
323
|
+
middle_name: name? # Type is nullable
|
|
324
|
+
nickname: text? # May be nil
|
|
325
|
+
manager: User? # Reference can be nil
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
### Optional Fields
|
|
329
|
+
|
|
330
|
+
Use `optional` modifier to omit fields sometimes:
|
|
331
|
+
|
|
332
|
+
```ruby
|
|
333
|
+
User:
|
|
334
|
+
phone: phone optional # May be omitted from output
|
|
335
|
+
address: address optional # Sometimes not included
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
**Difference:**
|
|
339
|
+
- **Nullable (`?`)**: Field is always included, but value may be `nil`
|
|
340
|
+
- **Optional**: Field may be completely omitted from output
|
|
341
|
+
|
|
342
|
+
### Conditional Fields
|
|
343
|
+
|
|
344
|
+
Use `if` to conditionally include fields:
|
|
345
|
+
|
|
346
|
+
```ruby
|
|
347
|
+
User:
|
|
348
|
+
is_admin: boolean
|
|
349
|
+
admin_note: text if is_admin # Only if is_admin is true
|
|
350
|
+
employee_id: text if is_employee # Only if is_employee is true
|
|
351
|
+
```
|
|
352
|
+
|
|
353
|
+
**Note**: The condition field must be defined **before** the conditional field.
|
|
354
|
+
|
|
355
|
+
### Type Options
|
|
356
|
+
|
|
357
|
+
Many types support options:
|
|
358
|
+
|
|
359
|
+
#### Number Options
|
|
360
|
+
|
|
361
|
+
```ruby
|
|
362
|
+
age: number(18..65) # Range
|
|
363
|
+
price: number(min: 10, max: 100) # Named arguments
|
|
364
|
+
score: number(0..100, precision: 2) # With precision
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
#### Text Options
|
|
368
|
+
|
|
369
|
+
```ruby
|
|
370
|
+
title: text(10..50) # Length range
|
|
371
|
+
description: text(min: 100, max: 500) # Named arguments
|
|
372
|
+
code: text(10, prefix: "CODE-") # With prefix
|
|
373
|
+
|
|
374
|
+
# Language support (English/Arabic)
|
|
375
|
+
english_text: text(50, language: :en) # English (default)
|
|
376
|
+
arabic_text: text(50, language: :ar) # Arabic
|
|
377
|
+
arabic_text: text(50, lang: :arabic) # Also accepts :arabic
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
#### Enum Options
|
|
381
|
+
|
|
382
|
+
```ruby
|
|
383
|
+
status: enum(active, inactive)
|
|
384
|
+
priority: enum(low: 50%, medium: 30%, high: 20%) # With weights
|
|
385
|
+
role: enum(user: 80%, admin: 10%, moderator: 10%) # Percentages
|
|
386
|
+
```
|
|
387
|
+
|
|
388
|
+
#### Array Options
|
|
389
|
+
|
|
390
|
+
```ruby
|
|
391
|
+
tags: array(text, 3..10) # Element type and size
|
|
392
|
+
items: array(Item, min: 1, max: 5) # Named arguments
|
|
393
|
+
scores: array(number(0..100), 5) # Fixed size
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
#### Date Options
|
|
397
|
+
|
|
398
|
+
```ruby
|
|
399
|
+
created_at: timestamp
|
|
400
|
+
last_week: past_date(days: 7)
|
|
401
|
+
next_month: future_date(days: 30)
|
|
402
|
+
between: date_between(start: "2024-01-01", end: "2024-12-31")
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
#### Sequence Options
|
|
406
|
+
|
|
407
|
+
```ruby
|
|
408
|
+
id: sequence(start: 1, step: 1)
|
|
409
|
+
code: sequence(start: "A001", step: 1, format: "ID-%03d")
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
#### Template Options
|
|
413
|
+
|
|
414
|
+
```ruby
|
|
415
|
+
full_name: template("{{first_name}} {{last_name}}")
|
|
416
|
+
email: template("{{username}}@example.com")
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
### Combining Modifiers
|
|
420
|
+
|
|
421
|
+
You can combine multiple modifiers:
|
|
422
|
+
|
|
423
|
+
```ruby
|
|
424
|
+
User:
|
|
425
|
+
id: uuid unique # Unique constraint
|
|
426
|
+
email: email unique optional # Unique and optional
|
|
427
|
+
nickname: text? optional # Nullable and optional
|
|
428
|
+
admin_note: text? if is_admin # Nullable and conditional
|
|
429
|
+
```
|
|
430
|
+
|
|
431
|
+
---
|
|
432
|
+
|
|
433
|
+
## Deterministic Generation (Same Data Every Time)
|
|
434
|
+
|
|
435
|
+
Use the `seed` parameter to generate the **same data every time**. This is essential for:
|
|
436
|
+
- **Testing** - Reproducible test data
|
|
437
|
+
- **Debugging** - Same data to debug issues
|
|
438
|
+
- **Documentation** - Consistent examples
|
|
439
|
+
- **CI/CD** - Predictable test results
|
|
440
|
+
|
|
441
|
+
### Basic Usage
|
|
442
|
+
|
|
443
|
+
```ruby
|
|
444
|
+
require 'fake_data_dsl'
|
|
445
|
+
|
|
446
|
+
registry = FakeDataDSL::Registry.new
|
|
447
|
+
registry.load_string(<<~DSL)
|
|
448
|
+
User:
|
|
449
|
+
id: uuid
|
|
450
|
+
name: name
|
|
451
|
+
email: email
|
|
452
|
+
age: number(18..65)
|
|
453
|
+
DSL
|
|
454
|
+
|
|
455
|
+
schema = registry.schema("User")
|
|
456
|
+
|
|
457
|
+
# Generate with seed - same data every time
|
|
458
|
+
user1 = schema.generate(seed: 42)
|
|
459
|
+
user2 = schema.generate(seed: 42)
|
|
460
|
+
|
|
461
|
+
# user1 and user2 will be IDENTICAL
|
|
462
|
+
puts user1 == user2 # => true
|
|
463
|
+
```
|
|
464
|
+
|
|
465
|
+
### Generate Multiple Records
|
|
466
|
+
|
|
467
|
+
```ruby
|
|
468
|
+
# Generate 10 records with same seed
|
|
469
|
+
users1 = schema.generate_many(10, seed: 12345)
|
|
470
|
+
users2 = schema.generate_many(10, seed: 12345)
|
|
471
|
+
|
|
472
|
+
# All records will be identical
|
|
473
|
+
users1.each_with_index do |user, i|
|
|
474
|
+
puts user == users2[i] # => true for all
|
|
475
|
+
end
|
|
476
|
+
```
|
|
477
|
+
|
|
478
|
+
### Different Seeds = Different Data
|
|
479
|
+
|
|
480
|
+
```ruby
|
|
481
|
+
# Different seeds produce different data
|
|
482
|
+
user1 = schema.generate(seed: 42)
|
|
483
|
+
user2 = schema.generate(seed: 43)
|
|
484
|
+
|
|
485
|
+
puts user1 == user2 # => false (different data)
|
|
486
|
+
```
|
|
487
|
+
|
|
488
|
+
### Streaming with Seed
|
|
489
|
+
|
|
490
|
+
```ruby
|
|
491
|
+
# Streaming also supports seeds for determinism
|
|
492
|
+
streamer = schema.generate_many_stream(count: 100, seed: 999)
|
|
493
|
+
|
|
494
|
+
# All records will be deterministic
|
|
495
|
+
streamer.each do |record|
|
|
496
|
+
# Process record
|
|
497
|
+
end
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
### Complete Example
|
|
501
|
+
|
|
502
|
+
```ruby
|
|
503
|
+
require 'fake_data_dsl'
|
|
504
|
+
|
|
505
|
+
# Define schema
|
|
506
|
+
registry = FakeDataDSL::Registry.new
|
|
507
|
+
registry.load_string(<<~DSL)
|
|
508
|
+
Product:
|
|
509
|
+
id: uuid
|
|
510
|
+
name: product_name
|
|
511
|
+
price: number(10..100)
|
|
512
|
+
category: product_category
|
|
513
|
+
in_stock: boolean
|
|
514
|
+
DSL
|
|
515
|
+
|
|
516
|
+
schema = registry.schema("Product")
|
|
517
|
+
|
|
518
|
+
# Generate same product every time
|
|
519
|
+
product = schema.generate(seed: 42)
|
|
520
|
+
puts product
|
|
521
|
+
# => {
|
|
522
|
+
# "id" => "550e8400-e29b-41d4-a716-446655440000",
|
|
523
|
+
# "name" => "Ergonomic Granite Chair",
|
|
524
|
+
# "price" => 45.67,
|
|
525
|
+
# "category" => "Electronics",
|
|
526
|
+
# "in_stock" => true
|
|
527
|
+
# }
|
|
528
|
+
|
|
529
|
+
# Generate again with same seed - IDENTICAL
|
|
530
|
+
product2 = schema.generate(seed: 42)
|
|
531
|
+
puts product == product2 # => true
|
|
532
|
+
|
|
533
|
+
# Different seed - different data
|
|
534
|
+
product3 = schema.generate(seed: 99)
|
|
535
|
+
puts product == product3 # => false
|
|
536
|
+
```
|
|
537
|
+
|
|
538
|
+
### Seed Best Practices
|
|
539
|
+
|
|
540
|
+
1. **Use Fixed Seeds for Tests**
|
|
541
|
+
```ruby
|
|
542
|
+
# In your test
|
|
543
|
+
user = schema.generate(seed: 12345)
|
|
544
|
+
expect(user["age"]).to be_between(18, 65)
|
|
545
|
+
```
|
|
546
|
+
|
|
547
|
+
2. **Use Different Seeds for Variety**
|
|
548
|
+
```ruby
|
|
549
|
+
# Generate varied test data
|
|
550
|
+
(1..100).each do |seed|
|
|
551
|
+
user = schema.generate(seed: seed)
|
|
552
|
+
# Each user will be different
|
|
553
|
+
end
|
|
554
|
+
```
|
|
555
|
+
|
|
556
|
+
3. **Store Seeds for Reproducibility**
|
|
557
|
+
```ruby
|
|
558
|
+
# Save seed with generated data
|
|
559
|
+
seed = 42
|
|
560
|
+
user = schema.generate(seed: seed)
|
|
561
|
+
user["_seed"] = seed # Store for later reproduction
|
|
562
|
+
```
|
|
563
|
+
|
|
564
|
+
4. **Seed Per Schema**
|
|
565
|
+
```ruby
|
|
566
|
+
# Each schema can have different seeds
|
|
567
|
+
user = user_schema.generate(seed: 100)
|
|
568
|
+
order = order_schema.generate(seed: 200)
|
|
569
|
+
```
|
|
570
|
+
|
|
571
|
+
### Determinism Guarantees
|
|
572
|
+
|
|
573
|
+
✅ **Guaranteed Deterministic:**
|
|
574
|
+
- Same seed + same schema = **identical output**
|
|
575
|
+
- Works across multiple runs
|
|
576
|
+
- Works across different machines
|
|
577
|
+
- Works with `generate`, `generate_many`, and `generate_many_stream`
|
|
578
|
+
|
|
579
|
+
⚠️ **Not Deterministic:**
|
|
580
|
+
- Without seed (uses random seed each time)
|
|
581
|
+
- Different Ruby versions (may vary)
|
|
582
|
+
- Different Faker versions (may vary)
|
|
583
|
+
- Schema changes (different schema = different output)
|
|
584
|
+
|
|
585
|
+
### Example: Testing with Seeds
|
|
586
|
+
|
|
587
|
+
```ruby
|
|
588
|
+
# spec/user_spec.rb
|
|
589
|
+
RSpec.describe "User generation" do
|
|
590
|
+
let(:schema) do
|
|
591
|
+
registry = FakeDataDSL::Registry.new
|
|
592
|
+
registry.load_string(<<~DSL)
|
|
593
|
+
User:
|
|
594
|
+
id: uuid
|
|
595
|
+
name: name
|
|
596
|
+
email: email
|
|
597
|
+
age: number(18..65)
|
|
598
|
+
DSL
|
|
599
|
+
registry.schema("User")
|
|
600
|
+
end
|
|
601
|
+
|
|
602
|
+
it "generates consistent data with seed" do
|
|
603
|
+
user1 = schema.generate(seed: 42)
|
|
604
|
+
user2 = schema.generate(seed: 42)
|
|
605
|
+
|
|
606
|
+
expect(user1).to eq(user2)
|
|
607
|
+
expect(user1["age"]).to be_between(18, 65)
|
|
608
|
+
end
|
|
609
|
+
|
|
610
|
+
it "generates different data with different seeds" do
|
|
611
|
+
user1 = schema.generate(seed: 1)
|
|
612
|
+
user2 = schema.generate(seed: 2)
|
|
613
|
+
|
|
614
|
+
expect(user1).not_to eq(user2)
|
|
615
|
+
end
|
|
616
|
+
end
|
|
617
|
+
```
|
|
618
|
+
|
|
619
|
+
### Configuration
|
|
620
|
+
|
|
621
|
+
You can also set a default seed globally:
|
|
622
|
+
|
|
623
|
+
```ruby
|
|
624
|
+
FakeDataDSL.configure do |config|
|
|
625
|
+
# Note: There's no global seed setting, always pass seed explicitly
|
|
626
|
+
# This ensures explicit control over determinism
|
|
627
|
+
end
|
|
628
|
+
|
|
629
|
+
# Always pass seed explicitly for clarity
|
|
630
|
+
schema.generate(seed: 42)
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
---
|
|
634
|
+
|
|
635
|
+
## Fixed Values (Same Value Every Time)
|
|
636
|
+
|
|
637
|
+
Sometimes you need specific fields to always have the **same value** every time you generate data, regardless of seed. This is useful for:
|
|
638
|
+
- **Timestamps** - Fixed `created_at` for all records
|
|
639
|
+
- **Constants** - Fixed status, category, or identifier
|
|
640
|
+
- **Test Data** - Known values for testing
|
|
641
|
+
|
|
642
|
+
### Method 1: Using `default_value` Type (Recommended)
|
|
643
|
+
|
|
644
|
+
Use the `default_value` type to set a fixed value:
|
|
645
|
+
|
|
646
|
+
```ruby
|
|
647
|
+
User:
|
|
648
|
+
id: uuid unique
|
|
649
|
+
username: username unique
|
|
650
|
+
email: email unique
|
|
651
|
+
first_name: first_name
|
|
652
|
+
last_name: last_name
|
|
653
|
+
full_name: template("{{first_name}} {{last_name}}")
|
|
654
|
+
age: number(18..100)
|
|
655
|
+
gender: gender
|
|
656
|
+
phone: phone? optional
|
|
657
|
+
address: address? optional
|
|
658
|
+
is_admin: boolean(true: 10%)
|
|
659
|
+
admin_level: enum(none: 90%, moderator: 8%, admin: 2%) if is_admin
|
|
660
|
+
created_at: default_value(value: "2024-01-15 10:30:00 UTC")
|
|
661
|
+
last_login: past_date(days: 30)? optional
|
|
662
|
+
```
|
|
663
|
+
|
|
664
|
+
**For Timestamps:**
|
|
665
|
+
|
|
666
|
+
```ruby
|
|
667
|
+
User:
|
|
668
|
+
# Fixed timestamp (same for all records)
|
|
669
|
+
created_at: default_value(value: "2024-01-15T10:30:00Z")
|
|
670
|
+
|
|
671
|
+
# Or use Time object format
|
|
672
|
+
updated_at: default_value(value: "2024-01-15 10:30:00 UTC")
|
|
673
|
+
|
|
674
|
+
# Or Unix timestamp
|
|
675
|
+
timestamp: default_value(value: 1705315800)
|
|
676
|
+
```
|
|
677
|
+
|
|
678
|
+
**For Other Types:**
|
|
679
|
+
|
|
680
|
+
```ruby
|
|
681
|
+
Product:
|
|
682
|
+
# Fixed string
|
|
683
|
+
status: default_value(value: "active")
|
|
684
|
+
|
|
685
|
+
# Fixed number
|
|
686
|
+
version: default_value(value: 1)
|
|
687
|
+
|
|
688
|
+
# Fixed boolean
|
|
689
|
+
enabled: default_value(value: true)
|
|
690
|
+
|
|
691
|
+
# Fixed array
|
|
692
|
+
tags: default_value(value: ["featured", "new"])
|
|
693
|
+
```
|
|
694
|
+
|
|
695
|
+
### Method 2: Using Overrides
|
|
696
|
+
|
|
697
|
+
Override specific fields when generating:
|
|
698
|
+
|
|
699
|
+
```ruby
|
|
700
|
+
require 'fake_data_dsl'
|
|
701
|
+
|
|
702
|
+
registry = FakeDataDSL::Registry.new
|
|
703
|
+
registry.load_string(<<~DSL)
|
|
704
|
+
User:
|
|
705
|
+
id: uuid unique
|
|
706
|
+
username: username unique
|
|
707
|
+
email: email unique
|
|
708
|
+
created_at: timestamp
|
|
709
|
+
DSL
|
|
710
|
+
|
|
711
|
+
schema = registry.schema("User")
|
|
712
|
+
|
|
713
|
+
# Generate with fixed created_at
|
|
714
|
+
fixed_time = Time.parse("2024-01-15 10:30:00 UTC")
|
|
715
|
+
|
|
716
|
+
user1 = schema.generate(
|
|
717
|
+
seed: 42,
|
|
718
|
+
overrides: { "created_at" => fixed_time }
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
user2 = schema.generate(
|
|
722
|
+
seed: 43,
|
|
723
|
+
overrides: { "created_at" => fixed_time }
|
|
724
|
+
)
|
|
725
|
+
|
|
726
|
+
# Both have same created_at
|
|
727
|
+
puts user1["created_at"] == user2["created_at"] # => true
|
|
728
|
+
```
|
|
729
|
+
|
|
730
|
+
**For Multiple Records:**
|
|
731
|
+
|
|
732
|
+
```ruby
|
|
733
|
+
fixed_time = Time.parse("2024-01-15 10:30:00 UTC")
|
|
734
|
+
|
|
735
|
+
users = schema.generate_many(
|
|
736
|
+
10,
|
|
737
|
+
seed: 42,
|
|
738
|
+
overrides: { "created_at" => fixed_time }
|
|
739
|
+
)
|
|
740
|
+
|
|
741
|
+
# All users have the same created_at
|
|
742
|
+
users.each do |user|
|
|
743
|
+
puts user["created_at"] == fixed_time # => true
|
|
744
|
+
end
|
|
745
|
+
```
|
|
746
|
+
|
|
747
|
+
### Method 3: Combining Seed + Overrides
|
|
748
|
+
|
|
749
|
+
Use seed for most fields, but override specific ones:
|
|
750
|
+
|
|
751
|
+
```ruby
|
|
752
|
+
User:
|
|
753
|
+
id: uuid unique
|
|
754
|
+
username: username unique
|
|
755
|
+
email: email unique
|
|
756
|
+
first_name: first_name
|
|
757
|
+
last_name: last_name
|
|
758
|
+
full_name: template("{{first_name}} {{last_name}}")
|
|
759
|
+
age: number(18..100)
|
|
760
|
+
gender: gender
|
|
761
|
+
phone: phone? optional
|
|
762
|
+
address: address? optional
|
|
763
|
+
is_admin: boolean(true: 10%)
|
|
764
|
+
admin_level: enum(none: 90%, moderator: 8%, admin: 2%) if is_admin
|
|
765
|
+
created_at: timestamp # Will be overridden
|
|
766
|
+
last_login: past_date(days: 30)? optional
|
|
767
|
+
```
|
|
768
|
+
|
|
769
|
+
```ruby
|
|
770
|
+
# Generate with seed for randomness, but fixed created_at
|
|
771
|
+
fixed_created_at = Time.parse("2024-01-15 10:30:00 UTC")
|
|
772
|
+
|
|
773
|
+
user = schema.generate(
|
|
774
|
+
seed: 42,
|
|
775
|
+
overrides: { "created_at" => fixed_created_at }
|
|
776
|
+
)
|
|
777
|
+
|
|
778
|
+
# Other fields vary with seed, but created_at is always the same
|
|
779
|
+
```
|
|
780
|
+
|
|
781
|
+
### Complete Example
|
|
782
|
+
|
|
783
|
+
```ruby
|
|
784
|
+
require 'fake_data_dsl'
|
|
785
|
+
|
|
786
|
+
registry = FakeDataDSL::Registry.new
|
|
787
|
+
registry.load_string(<<~DSL)
|
|
788
|
+
User:
|
|
789
|
+
id: uuid unique
|
|
790
|
+
username: username unique
|
|
791
|
+
email: email unique
|
|
792
|
+
first_name: first_name
|
|
793
|
+
last_name: last_name
|
|
794
|
+
full_name: template("{{first_name}} {{last_name}}")
|
|
795
|
+
age: number(18..100)
|
|
796
|
+
gender: gender
|
|
797
|
+
phone: phone? optional
|
|
798
|
+
address: address? optional
|
|
799
|
+
is_admin: boolean(true: 10%)
|
|
800
|
+
admin_level: enum(none: 90%, moderator: 8%, admin: 2%) if is_admin
|
|
801
|
+
created_at: default_value(value: "2024-01-15 10:30:00 UTC")
|
|
802
|
+
last_login: past_date(days: 30)? optional
|
|
803
|
+
DSL
|
|
804
|
+
|
|
805
|
+
schema = registry.schema("User")
|
|
806
|
+
|
|
807
|
+
# Generate multiple users - all have same created_at
|
|
808
|
+
users = schema.generate_many(10, seed: 42)
|
|
809
|
+
|
|
810
|
+
users.each do |user|
|
|
811
|
+
puts user["created_at"] # => "2024-01-15 10:30:00 UTC" (same for all)
|
|
812
|
+
puts user["first_name"] # => Different names (varies with seed)
|
|
813
|
+
end
|
|
814
|
+
```
|
|
815
|
+
|
|
816
|
+
### When to Use Each Method
|
|
817
|
+
|
|
818
|
+
**Use `default_value` when:**
|
|
819
|
+
- ✅ Value should be the same in the DSL definition
|
|
820
|
+
- ✅ Value is a constant that never changes
|
|
821
|
+
- ✅ You want it defined in the schema itself
|
|
822
|
+
|
|
823
|
+
**Use `overrides` when:**
|
|
824
|
+
- ✅ Value needs to be set at generation time
|
|
825
|
+
- ✅ Value might vary per batch but same within batch
|
|
826
|
+
- ✅ You want flexibility to change it programmatically
|
|
827
|
+
|
|
828
|
+
### Tips
|
|
829
|
+
|
|
830
|
+
1. **Timestamp Format**: Use ISO 8601 format for timestamps:
|
|
831
|
+
```ruby
|
|
832
|
+
created_at: default_value(value: "2024-01-15T10:30:00Z")
|
|
833
|
+
```
|
|
834
|
+
|
|
835
|
+
2. **Time Objects**: You can also use Time objects in overrides:
|
|
836
|
+
```ruby
|
|
837
|
+
overrides: { "created_at" => Time.parse("2024-01-15 10:30:00 UTC") }
|
|
838
|
+
```
|
|
839
|
+
|
|
840
|
+
3. **Multiple Fixed Fields**: Set multiple fixed values:
|
|
841
|
+
```ruby
|
|
842
|
+
overrides: {
|
|
843
|
+
"created_at" => fixed_time,
|
|
844
|
+
"status" => "active",
|
|
845
|
+
"version" => 1
|
|
846
|
+
}
|
|
847
|
+
```
|
|
848
|
+
|
|
849
|
+
---
|
|
850
|
+
|
|
851
|
+
## Complete Examples
|
|
852
|
+
|
|
853
|
+
### E-commerce Order
|
|
854
|
+
|
|
855
|
+
```ruby
|
|
856
|
+
Order:
|
|
857
|
+
id: uuid unique
|
|
858
|
+
order_number: sequence(start: 1000) unique
|
|
859
|
+
customer_id: uuid
|
|
860
|
+
items: array(OrderItem, 1..5)
|
|
861
|
+
subtotal: formula(items.map(&:total).sum)
|
|
862
|
+
tax_rate: number(0.05..0.15)
|
|
863
|
+
tax: formula(subtotal * tax_rate)
|
|
864
|
+
shipping: number(5..25)
|
|
865
|
+
total: formula(subtotal + tax + shipping)
|
|
866
|
+
status: enum(pending: 20%, processing: 30%, shipped: 40%, delivered: 10%)
|
|
867
|
+
created_at: timestamp
|
|
868
|
+
shipped_at: timestamp? optional
|
|
869
|
+
|
|
870
|
+
OrderItem:
|
|
871
|
+
product_id: uuid
|
|
872
|
+
product_name: product_name
|
|
873
|
+
quantity: number(1..10)
|
|
874
|
+
unit_price: number(10..100)
|
|
875
|
+
total: formula(quantity * unit_price)
|
|
876
|
+
```
|
|
877
|
+
|
|
878
|
+
### User Profile
|
|
879
|
+
|
|
880
|
+
```ruby
|
|
881
|
+
User:
|
|
882
|
+
id: uuid unique
|
|
883
|
+
username: username unique
|
|
884
|
+
email: email unique
|
|
885
|
+
first_name: first_name
|
|
886
|
+
last_name: last_name
|
|
887
|
+
full_name: template("{{first_name}} {{last_name}}")
|
|
888
|
+
age: number(18..100)
|
|
889
|
+
gender: gender
|
|
890
|
+
phone: phone? optional
|
|
891
|
+
address: address? optional
|
|
892
|
+
is_admin: boolean(true: 10%)
|
|
893
|
+
admin_level: enum(none: 90%, moderator: 8%, admin: 2%) if is_admin
|
|
894
|
+
created_at: timestamp
|
|
895
|
+
last_login: past_date(days: 30)? optional
|
|
896
|
+
```
|
|
897
|
+
|
|
898
|
+
### Financial Transaction
|
|
899
|
+
|
|
900
|
+
```ruby
|
|
901
|
+
Transaction:
|
|
902
|
+
id: uuid unique
|
|
903
|
+
transaction_id: sequence(start: "TXN-0001", format: "TXN-%04d") unique
|
|
904
|
+
from_account: account_number
|
|
905
|
+
to_account: account_number
|
|
906
|
+
amount: transaction_amount(min: 1.0, max: 10000.0)
|
|
907
|
+
currency: currency
|
|
908
|
+
fee: formula(amount * 0.02)
|
|
909
|
+
total: formula(amount + fee)
|
|
910
|
+
status: enum(pending: 10%, processing: 20%, completed: 65%, failed: 5%)
|
|
911
|
+
created_at: timestamp
|
|
912
|
+
completed_at: timestamp? if status == "completed"
|
|
913
|
+
```
|
|
914
|
+
|
|
915
|
+
### Product Catalog
|
|
916
|
+
|
|
917
|
+
```ruby
|
|
918
|
+
Product:
|
|
919
|
+
id: uuid unique
|
|
920
|
+
sku: sku unique
|
|
921
|
+
name: product_name
|
|
922
|
+
category: product_category
|
|
923
|
+
description: text(50..500)
|
|
924
|
+
price: number(10.0..999.99, precision: 2)
|
|
925
|
+
discount: discount(min: 0, max: 50)
|
|
926
|
+
final_price: formula(price * (1 - discount / 100))
|
|
927
|
+
in_stock: boolean(true: 80%)
|
|
928
|
+
stock_quantity: number(0..1000) if in_stock
|
|
929
|
+
tags: array(text, 3..10)
|
|
930
|
+
created_at: timestamp
|
|
931
|
+
```
|
|
932
|
+
|
|
933
|
+
---
|
|
934
|
+
|
|
935
|
+
## Quick Reference
|
|
936
|
+
|
|
937
|
+
### Field Syntax
|
|
938
|
+
|
|
939
|
+
```
|
|
940
|
+
field_name: type(arguments) modifiers
|
|
941
|
+
```
|
|
942
|
+
|
|
943
|
+
### Common Modifiers
|
|
944
|
+
|
|
945
|
+
- `unique` - Ensure unique values
|
|
946
|
+
- `optional` - May be omitted
|
|
947
|
+
- `?` - Nullable (can be nil)
|
|
948
|
+
- `if condition` - Conditional field
|
|
949
|
+
|
|
950
|
+
### Common Types
|
|
951
|
+
|
|
952
|
+
- `uuid` - UUID v4
|
|
953
|
+
- `name` - Full name
|
|
954
|
+
- `email` - Email address
|
|
955
|
+
- `number(range)` - Number in range
|
|
956
|
+
- `text(length)` - Text with length
|
|
957
|
+
- `boolean` - True/false
|
|
958
|
+
- `enum(values)` - Enum with values
|
|
959
|
+
- `array(type, size)` - Array of type
|
|
960
|
+
- `formula(expression)` - Computed value
|
|
961
|
+
- `sequence(start, step)` - Sequential values
|
|
962
|
+
|
|
963
|
+
### Type Arguments
|
|
964
|
+
|
|
965
|
+
- `number(18..65)` - Range
|
|
966
|
+
- `number(min: 10, max: 100)` - Named arguments
|
|
967
|
+
- `text(10..50)` - Length range
|
|
968
|
+
- `enum(low: 50%, high: 50%)` - With weights
|
|
969
|
+
- `array(text, 3..10)` - Element type and size
|
|
970
|
+
|
|
971
|
+
---
|
|
972
|
+
|
|
973
|
+
## Next Steps
|
|
974
|
+
|
|
975
|
+
- [Getting Started](getting_started.md) - Quick start guide
|
|
976
|
+
- [DSL Reference](dsl_reference.md) - Complete syntax reference
|
|
977
|
+
- [Type Reference](type_reference.md) - All available types
|
|
978
|
+
- [Examples](examples.md) - Real-world examples
|
|
979
|
+
- [Advanced Features](advanced_features.md) - References, custom functions, streaming
|
|
980
|
+
|