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,524 @@
|
|
|
1
|
+
# ActiveRecord Schema Inference
|
|
2
|
+
|
|
3
|
+
FakeDataDSL can **automatically infer DSL schemas from your ActiveRecord models**, eliminating the need to manually define schemas for existing database tables.
|
|
4
|
+
|
|
5
|
+
## Quick Start
|
|
6
|
+
|
|
7
|
+
```ruby
|
|
8
|
+
# Infer schema from a single model
|
|
9
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User)
|
|
10
|
+
|
|
11
|
+
# Infer schemas from all models
|
|
12
|
+
FakeDataDSL::ActiveRecordInference.discover_all!
|
|
13
|
+
|
|
14
|
+
# Generate fake data directly from model
|
|
15
|
+
user_data = FakeDataDSL.from_model(User).generate
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## Basic Usage
|
|
19
|
+
|
|
20
|
+
### Single Model Inference
|
|
21
|
+
|
|
22
|
+
```ruby
|
|
23
|
+
# Given an ActiveRecord model:
|
|
24
|
+
class User < ApplicationRecord
|
|
25
|
+
# Table: users
|
|
26
|
+
# Columns: id (uuid), name (string), email (string),
|
|
27
|
+
# age (integer), active (boolean), created_at (datetime)
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
# Infer the schema
|
|
31
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User)
|
|
32
|
+
|
|
33
|
+
# The inferred schema is equivalent to:
|
|
34
|
+
# User:
|
|
35
|
+
# id: uuid
|
|
36
|
+
# name: name
|
|
37
|
+
# email: email
|
|
38
|
+
# age: number
|
|
39
|
+
# active: boolean
|
|
40
|
+
# created_at: timestamp
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Generate from Model
|
|
44
|
+
|
|
45
|
+
```ruby
|
|
46
|
+
# Direct generation (no schema file needed)
|
|
47
|
+
user = FakeDataDSL.from_model(User).generate
|
|
48
|
+
# => { id: "550e8400-...", name: "John Doe", email: "john@example.com", ... }
|
|
49
|
+
|
|
50
|
+
# Generate multiple
|
|
51
|
+
users = FakeDataDSL.from_model(User).generate_many(10)
|
|
52
|
+
|
|
53
|
+
# With overrides
|
|
54
|
+
admin = FakeDataDSL.from_model(User).generate(role: "admin")
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Smart Type Inference
|
|
58
|
+
|
|
59
|
+
### Column Name Patterns
|
|
60
|
+
|
|
61
|
+
The inference engine uses column names to determine appropriate types:
|
|
62
|
+
|
|
63
|
+
| Column Pattern | Inferred Type |
|
|
64
|
+
|----------------|---------------|
|
|
65
|
+
| `*email*` | `email` |
|
|
66
|
+
| `*phone*`, `*mobile*` | `phone` |
|
|
67
|
+
| `*url*`, `*website*`, `*link*` | `url` |
|
|
68
|
+
| `*password*` | `password` |
|
|
69
|
+
| `*name*` | `name` |
|
|
70
|
+
| `*first_name*` | `first_name` |
|
|
71
|
+
| `*last_name*` | `last_name` |
|
|
72
|
+
| `*address*` | `address` |
|
|
73
|
+
| `*city*` | `city` |
|
|
74
|
+
| `*country*` | `country` |
|
|
75
|
+
| `*zip*`, `*postal*` | `zip_code` |
|
|
76
|
+
| `*latitude*`, `*lat*` | `latitude` |
|
|
77
|
+
| `*longitude*`, `*lng*` | `longitude` |
|
|
78
|
+
| `*avatar*`, `*image*`, `*photo*` | `image_url` |
|
|
79
|
+
| `*description*`, `*bio*`, `*about*` | `paragraph` |
|
|
80
|
+
| `*title*`, `*subject*` | `sentence` |
|
|
81
|
+
| `*company*`, `*organization*` | `company` |
|
|
82
|
+
| `*ip*` | `ip_address` |
|
|
83
|
+
| `*token*`, `*api_key*` | `secure_token` |
|
|
84
|
+
| `*uuid*` | `uuid` |
|
|
85
|
+
| `*slug*` | `slug` |
|
|
86
|
+
| `*color*` | `hex_color` |
|
|
87
|
+
|
|
88
|
+
### Column Type Mapping
|
|
89
|
+
|
|
90
|
+
| ActiveRecord Type | DSL Type |
|
|
91
|
+
|-------------------|----------|
|
|
92
|
+
| `string` | `text` |
|
|
93
|
+
| `text` | `paragraph` |
|
|
94
|
+
| `integer` | `number` |
|
|
95
|
+
| `bigint` | `number` |
|
|
96
|
+
| `float` | `float` |
|
|
97
|
+
| `decimal` | `money` |
|
|
98
|
+
| `boolean` | `boolean` |
|
|
99
|
+
| `date` | `date` |
|
|
100
|
+
| `datetime` | `timestamp` |
|
|
101
|
+
| `time` | `time` |
|
|
102
|
+
| `uuid` | `uuid` |
|
|
103
|
+
| `json`, `jsonb` | `json` |
|
|
104
|
+
| `binary` | `binary` |
|
|
105
|
+
| `inet` | `ip_address` |
|
|
106
|
+
|
|
107
|
+
## Association Inference
|
|
108
|
+
|
|
109
|
+
### Belongs To
|
|
110
|
+
|
|
111
|
+
```ruby
|
|
112
|
+
class Order < ApplicationRecord
|
|
113
|
+
belongs_to :user
|
|
114
|
+
belongs_to :product
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(Order)
|
|
118
|
+
# Generates:
|
|
119
|
+
# Order:
|
|
120
|
+
# user_id: Ref(User.id)
|
|
121
|
+
# product_id: Ref(Product.id)
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Has Many / Has One
|
|
125
|
+
|
|
126
|
+
```ruby
|
|
127
|
+
class User < ApplicationRecord
|
|
128
|
+
has_many :orders
|
|
129
|
+
has_one :profile
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
# By default, has_many/has_one are not included in the schema
|
|
133
|
+
# Use options to include them:
|
|
134
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User, include_associations: true)
|
|
135
|
+
# Generates:
|
|
136
|
+
# User:
|
|
137
|
+
# orders: array(Order, 0..5)
|
|
138
|
+
# profile: Profile?
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Polymorphic Associations
|
|
142
|
+
|
|
143
|
+
```ruby
|
|
144
|
+
class Comment < ApplicationRecord
|
|
145
|
+
belongs_to :commentable, polymorphic: true
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(Comment)
|
|
149
|
+
# Generates:
|
|
150
|
+
# Comment:
|
|
151
|
+
# commentable_type: enum(Post, Article, Video)
|
|
152
|
+
# commentable_id: uuid
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
## Validation Inference
|
|
156
|
+
|
|
157
|
+
### Presence Validations
|
|
158
|
+
|
|
159
|
+
```ruby
|
|
160
|
+
class User < ApplicationRecord
|
|
161
|
+
validates :name, presence: true
|
|
162
|
+
validates :bio, presence: false # Optional
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User)
|
|
166
|
+
# Generates:
|
|
167
|
+
# User:
|
|
168
|
+
# name: name # Required (no ?)
|
|
169
|
+
# bio: paragraph? # Optional (has ?)
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Numericality Validations
|
|
173
|
+
|
|
174
|
+
```ruby
|
|
175
|
+
class Product < ApplicationRecord
|
|
176
|
+
validates :price, numericality: { greater_than: 0, less_than: 10000 }
|
|
177
|
+
validates :quantity, numericality: { only_integer: true, in: 0..1000 }
|
|
178
|
+
end
|
|
179
|
+
|
|
180
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(Product)
|
|
181
|
+
# Generates:
|
|
182
|
+
# Product:
|
|
183
|
+
# price: money(range: 0.01..9999.99)
|
|
184
|
+
# quantity: number(0..1000)
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
### Inclusion Validations
|
|
188
|
+
|
|
189
|
+
```ruby
|
|
190
|
+
class User < ApplicationRecord
|
|
191
|
+
validates :role, inclusion: { in: %w[user admin moderator] }
|
|
192
|
+
validates :status, inclusion: { in: %w[active inactive pending] }
|
|
193
|
+
end
|
|
194
|
+
|
|
195
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User)
|
|
196
|
+
# Generates:
|
|
197
|
+
# User:
|
|
198
|
+
# role: enum(user, admin, moderator)
|
|
199
|
+
# status: enum(active, inactive, pending)
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### Length Validations
|
|
203
|
+
|
|
204
|
+
```ruby
|
|
205
|
+
class Post < ApplicationRecord
|
|
206
|
+
validates :title, length: { minimum: 5, maximum: 100 }
|
|
207
|
+
validates :body, length: { minimum: 50 }
|
|
208
|
+
end
|
|
209
|
+
|
|
210
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(Post)
|
|
211
|
+
# Generates:
|
|
212
|
+
# Post:
|
|
213
|
+
# title: text(length: 5..100)
|
|
214
|
+
# body: paragraph(min_length: 50)
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
### Format Validations
|
|
218
|
+
|
|
219
|
+
```ruby
|
|
220
|
+
class User < ApplicationRecord
|
|
221
|
+
validates :username, format: { with: /\A[a-z0-9_]+\z/ }
|
|
222
|
+
end
|
|
223
|
+
|
|
224
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User)
|
|
225
|
+
# Generates:
|
|
226
|
+
# User:
|
|
227
|
+
# username: regex("[a-z0-9_]+")
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Enum Inference
|
|
231
|
+
|
|
232
|
+
```ruby
|
|
233
|
+
class Order < ApplicationRecord
|
|
234
|
+
enum status: { pending: 0, processing: 1, shipped: 2, delivered: 3 }
|
|
235
|
+
enum priority: [:low, :medium, :high]
|
|
236
|
+
end
|
|
237
|
+
|
|
238
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(Order)
|
|
239
|
+
# Generates:
|
|
240
|
+
# Order:
|
|
241
|
+
# status: enum(pending, processing, shipped, delivered)
|
|
242
|
+
# priority: enum(low, medium, high)
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Discovery Options
|
|
246
|
+
|
|
247
|
+
### Full Discovery
|
|
248
|
+
|
|
249
|
+
```ruby
|
|
250
|
+
# Discover all models in app/models
|
|
251
|
+
FakeDataDSL::ActiveRecordInference.discover_all!
|
|
252
|
+
|
|
253
|
+
# With options
|
|
254
|
+
FakeDataDSL::ActiveRecordInference.discover_all!(
|
|
255
|
+
include_sti: true, # Include STI subclasses
|
|
256
|
+
include_concerns: false, # Skip models with only concerns
|
|
257
|
+
exclude: [AdminUser, Audit], # Skip specific models
|
|
258
|
+
namespace: "App::Models" # Only discover in namespace
|
|
259
|
+
)
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
### Single Model Options
|
|
263
|
+
|
|
264
|
+
```ruby
|
|
265
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User,
|
|
266
|
+
include_associations: true, # Include has_many, has_one
|
|
267
|
+
include_validations: true, # Infer from validations
|
|
268
|
+
include_timestamps: true, # Include created_at, updated_at
|
|
269
|
+
skip_columns: [:password_digest, :encrypted_password],
|
|
270
|
+
custom_mappings: {
|
|
271
|
+
"legacy_id" => "number",
|
|
272
|
+
"metadata" => "json"
|
|
273
|
+
}
|
|
274
|
+
)
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
## Custom Type Mappings
|
|
278
|
+
|
|
279
|
+
### Global Mappings
|
|
280
|
+
|
|
281
|
+
```ruby
|
|
282
|
+
# config/initializers/fake_data_dsl.rb
|
|
283
|
+
FakeDataDSL::ActiveRecordInference.configure do |config|
|
|
284
|
+
# Add custom column name patterns
|
|
285
|
+
config.column_patterns = {
|
|
286
|
+
/ssn|social_security/i => "ssn",
|
|
287
|
+
/iban/i => "iban",
|
|
288
|
+
/credit_card/i => "credit_card"
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
# Add custom type mappings
|
|
292
|
+
config.type_mappings = {
|
|
293
|
+
"citext" => "text",
|
|
294
|
+
"hstore" => "json"
|
|
295
|
+
}
|
|
296
|
+
end
|
|
297
|
+
```
|
|
298
|
+
|
|
299
|
+
### Per-Model Mappings
|
|
300
|
+
|
|
301
|
+
```ruby
|
|
302
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User,
|
|
303
|
+
custom_mappings: {
|
|
304
|
+
"legacy_status" => "enum(active, inactive)",
|
|
305
|
+
"settings" => "json"
|
|
306
|
+
}
|
|
307
|
+
)
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
## Saving Inferred Schemas
|
|
311
|
+
|
|
312
|
+
### To File
|
|
313
|
+
|
|
314
|
+
```ruby
|
|
315
|
+
# Save single schema
|
|
316
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User)
|
|
317
|
+
schema.save_to("db/schemas/user.dsl")
|
|
318
|
+
|
|
319
|
+
# Save all discovered schemas
|
|
320
|
+
FakeDataDSL::ActiveRecordInference.discover_all!
|
|
321
|
+
FakeDataDSL.save_all_schemas("db/schemas/")
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Generated Schema Format
|
|
325
|
+
|
|
326
|
+
```ruby
|
|
327
|
+
# db/schemas/user.dsl (auto-generated)
|
|
328
|
+
# Generated from User model on 2026-01-24
|
|
329
|
+
# Do not edit manually - regenerate with:
|
|
330
|
+
# rails generate fake_data_dsl:schema User
|
|
331
|
+
|
|
332
|
+
User:
|
|
333
|
+
@inferred_from ActiveRecord::User
|
|
334
|
+
|
|
335
|
+
id: uuid
|
|
336
|
+
name: name
|
|
337
|
+
email: email @unique
|
|
338
|
+
role: enum(user, admin, moderator)
|
|
339
|
+
active: boolean(true:90%)
|
|
340
|
+
profile_id: Ref(Profile.id)?
|
|
341
|
+
created_at: timestamp
|
|
342
|
+
updated_at: timestamp
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## Rails Generator
|
|
346
|
+
|
|
347
|
+
### Generate Schema from Model
|
|
348
|
+
|
|
349
|
+
```bash
|
|
350
|
+
# Generate schema for User model
|
|
351
|
+
rails generate fake_data_dsl:schema User
|
|
352
|
+
|
|
353
|
+
# Generate for multiple models
|
|
354
|
+
rails generate fake_data_dsl:schema User Order Product
|
|
355
|
+
|
|
356
|
+
# Generate for all models
|
|
357
|
+
rails generate fake_data_dsl:schema --all
|
|
358
|
+
|
|
359
|
+
# With options
|
|
360
|
+
rails generate fake_data_dsl:schema User --include-associations --skip-timestamps
|
|
361
|
+
```
|
|
362
|
+
|
|
363
|
+
### Generator Options
|
|
364
|
+
|
|
365
|
+
| Option | Description |
|
|
366
|
+
|--------|-------------|
|
|
367
|
+
| `--include-associations` | Include has_many/has_one |
|
|
368
|
+
| `--skip-timestamps` | Exclude created_at/updated_at |
|
|
369
|
+
| `--skip-validations` | Don't infer from validations |
|
|
370
|
+
| `--output-dir DIR` | Output directory (default: db/schemas) |
|
|
371
|
+
| `--force` | Overwrite existing files |
|
|
372
|
+
|
|
373
|
+
## Bidirectional Sync
|
|
374
|
+
|
|
375
|
+
### Schema → Model Comparison
|
|
376
|
+
|
|
377
|
+
```ruby
|
|
378
|
+
# Compare inferred schema with existing schema file
|
|
379
|
+
diff = FakeDataDSL::ActiveRecordInference.compare(User, "db/schemas/user.dsl")
|
|
380
|
+
|
|
381
|
+
diff.added_fields # Fields in model but not schema
|
|
382
|
+
diff.removed_fields # Fields in schema but not model
|
|
383
|
+
diff.changed_fields # Fields with different types
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Auto-Update Schemas
|
|
387
|
+
|
|
388
|
+
```ruby
|
|
389
|
+
# Update schema file to match model
|
|
390
|
+
FakeDataDSL::ActiveRecordInference.sync!(User, "db/schemas/user.dsl")
|
|
391
|
+
|
|
392
|
+
# Sync all schemas
|
|
393
|
+
FakeDataDSL::ActiveRecordInference.sync_all!("db/schemas/")
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
## Rake Tasks
|
|
397
|
+
|
|
398
|
+
```bash
|
|
399
|
+
# Infer schema for a model
|
|
400
|
+
rake fake_data_dsl:infer[User]
|
|
401
|
+
|
|
402
|
+
# Infer all models
|
|
403
|
+
rake fake_data_dsl:infer:all
|
|
404
|
+
|
|
405
|
+
# Compare schemas with models
|
|
406
|
+
rake fake_data_dsl:compare
|
|
407
|
+
|
|
408
|
+
# Sync schemas with models
|
|
409
|
+
rake fake_data_dsl:sync
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
## API Reference
|
|
413
|
+
|
|
414
|
+
### ActiveRecordInference.infer
|
|
415
|
+
|
|
416
|
+
```ruby
|
|
417
|
+
FakeDataDSL::ActiveRecordInference.infer(model, options = {})
|
|
418
|
+
```
|
|
419
|
+
|
|
420
|
+
**Parameters:**
|
|
421
|
+
- `model` - ActiveRecord model class
|
|
422
|
+
- `options` - Hash of options
|
|
423
|
+
|
|
424
|
+
**Options:**
|
|
425
|
+
- `:include_associations` - Include has_many/has_one (default: false)
|
|
426
|
+
- `:include_validations` - Infer from validations (default: true)
|
|
427
|
+
- `:include_timestamps` - Include created_at/updated_at (default: true)
|
|
428
|
+
- `:skip_columns` - Array of columns to skip
|
|
429
|
+
- `:custom_mappings` - Hash of column => type mappings
|
|
430
|
+
|
|
431
|
+
**Returns:** `FakeDataDSL::Schema`
|
|
432
|
+
|
|
433
|
+
### ActiveRecordInference.discover_all!
|
|
434
|
+
|
|
435
|
+
```ruby
|
|
436
|
+
FakeDataDSL::ActiveRecordInference.discover_all!(options = {})
|
|
437
|
+
```
|
|
438
|
+
|
|
439
|
+
**Parameters:**
|
|
440
|
+
- `options` - Hash of options
|
|
441
|
+
|
|
442
|
+
**Options:**
|
|
443
|
+
- `:include_sti` - Include STI subclasses (default: false)
|
|
444
|
+
- `:exclude` - Array of models to skip
|
|
445
|
+
- `:namespace` - Only discover in namespace
|
|
446
|
+
|
|
447
|
+
**Returns:** Array of registered schema names
|
|
448
|
+
|
|
449
|
+
## Best Practices
|
|
450
|
+
|
|
451
|
+
### 1. Version Control Inferred Schemas
|
|
452
|
+
|
|
453
|
+
```ruby
|
|
454
|
+
# Keep generated schemas in version control
|
|
455
|
+
# db/schemas/user.dsl
|
|
456
|
+
```
|
|
457
|
+
|
|
458
|
+
### 2. Override Smart Inference
|
|
459
|
+
|
|
460
|
+
```ruby
|
|
461
|
+
# When inference isn't perfect, add manual overrides
|
|
462
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User,
|
|
463
|
+
custom_mappings: {
|
|
464
|
+
"legacy_code" => "regex('[A-Z]{3}[0-9]{4}')"
|
|
465
|
+
}
|
|
466
|
+
)
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
### 3. Use Comments for Clarity
|
|
470
|
+
|
|
471
|
+
Generated schemas include comments:
|
|
472
|
+
|
|
473
|
+
```ruby
|
|
474
|
+
# db/schemas/user.dsl
|
|
475
|
+
# Generated from User model
|
|
476
|
+
# Last updated: 2026-01-24
|
|
477
|
+
|
|
478
|
+
User:
|
|
479
|
+
# Primary key
|
|
480
|
+
id: uuid
|
|
481
|
+
|
|
482
|
+
# User identity
|
|
483
|
+
name: name
|
|
484
|
+
email: email @unique
|
|
485
|
+
```
|
|
486
|
+
|
|
487
|
+
## Troubleshooting
|
|
488
|
+
|
|
489
|
+
### Model Not Found
|
|
490
|
+
|
|
491
|
+
```ruby
|
|
492
|
+
# Ensure model is loaded
|
|
493
|
+
Rails.application.eager_load!
|
|
494
|
+
|
|
495
|
+
# Then infer
|
|
496
|
+
FakeDataDSL::ActiveRecordInference.infer(User)
|
|
497
|
+
```
|
|
498
|
+
|
|
499
|
+
### Wrong Type Inferred
|
|
500
|
+
|
|
501
|
+
```ruby
|
|
502
|
+
# Use custom mapping
|
|
503
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User,
|
|
504
|
+
custom_mappings: {
|
|
505
|
+
"status_code" => "number(100..599)" # Not inferred as enum
|
|
506
|
+
}
|
|
507
|
+
)
|
|
508
|
+
```
|
|
509
|
+
|
|
510
|
+
### Missing Associations
|
|
511
|
+
|
|
512
|
+
```ruby
|
|
513
|
+
# Enable association inference
|
|
514
|
+
schema = FakeDataDSL::ActiveRecordInference.infer(User,
|
|
515
|
+
include_associations: true
|
|
516
|
+
)
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
## See Also
|
|
520
|
+
|
|
521
|
+
- [Rails Engine](RAILS_ENGINE.md)
|
|
522
|
+
- [DSL Reference](dsl_reference.md)
|
|
523
|
+
- [Migration Generator](MIGRATION_GENERATOR.md)
|
|
524
|
+
- [Type Reference](type_reference.md)
|