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,104 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# :nocov:
|
|
4
|
+
module Synthra
|
|
5
|
+
module Export
|
|
6
|
+
# Exports generated data as XML
|
|
7
|
+
#
|
|
8
|
+
# @example Export schema data as XML
|
|
9
|
+
# exporter = XmlData.new(schema, registry: registry, count: 10)
|
|
10
|
+
# xml = exporter.export
|
|
11
|
+
#
|
|
12
|
+
class XmlData < Base
|
|
13
|
+
def export
|
|
14
|
+
count = options[:count] || 10
|
|
15
|
+
seed = options[:seed]
|
|
16
|
+
mode = options[:mode] || :random
|
|
17
|
+
indent = options[:indent] || 2
|
|
18
|
+
|
|
19
|
+
records = schema.generate_many(count, seed: seed, mode: mode, registry: registry)
|
|
20
|
+
|
|
21
|
+
lines = []
|
|
22
|
+
lines << '<?xml version="1.0" encoding="UTF-8"?>'
|
|
23
|
+
lines << "<!-- Generated from Synthra schema: #{schema.name} -->"
|
|
24
|
+
lines << "<!-- Records: #{count}, Generated at: #{Time.now.iso8601} -->"
|
|
25
|
+
|
|
26
|
+
root_name = options[:root] || "#{to_snake_case(schema.name)}s"
|
|
27
|
+
item_name = options[:item] || to_snake_case(schema.name)
|
|
28
|
+
|
|
29
|
+
lines << "<#{root_name}>"
|
|
30
|
+
|
|
31
|
+
records.each do |record|
|
|
32
|
+
lines << build_xml_element(item_name, record, indent)
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
lines << "</#{root_name}>"
|
|
36
|
+
lines.join("\n")
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def build_xml_element(name, value, indent, depth = 1)
|
|
42
|
+
prefix = " " * (indent * depth)
|
|
43
|
+
|
|
44
|
+
case value
|
|
45
|
+
when Hash
|
|
46
|
+
lines = ["#{prefix}<#{name}>"]
|
|
47
|
+
value.each do |k, v|
|
|
48
|
+
lines << build_xml_element(to_snake_case(k.to_s), v, indent, depth + 1)
|
|
49
|
+
end
|
|
50
|
+
lines << "#{prefix}</#{name}>"
|
|
51
|
+
lines.join("\n")
|
|
52
|
+
when Array
|
|
53
|
+
if value.empty?
|
|
54
|
+
"#{prefix}<#{name}/>"
|
|
55
|
+
else
|
|
56
|
+
lines = ["#{prefix}<#{name}>"]
|
|
57
|
+
item_name = singularize(name)
|
|
58
|
+
value.each do |item|
|
|
59
|
+
lines << build_xml_element(item_name, item, indent, depth + 1)
|
|
60
|
+
end
|
|
61
|
+
lines << "#{prefix}</#{name}>"
|
|
62
|
+
lines.join("\n")
|
|
63
|
+
end
|
|
64
|
+
when nil
|
|
65
|
+
"#{prefix}<#{name} nil=\"true\"/>"
|
|
66
|
+
when true, false
|
|
67
|
+
"#{prefix}<#{name}>#{value}</#{name}>"
|
|
68
|
+
else
|
|
69
|
+
escaped = escape_xml(value.to_s)
|
|
70
|
+
"#{prefix}<#{name}>#{escaped}</#{name}>"
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def escape_xml(str)
|
|
75
|
+
str.gsub("&", "&")
|
|
76
|
+
.gsub("<", "<")
|
|
77
|
+
.gsub(">", ">")
|
|
78
|
+
.gsub("\"", """)
|
|
79
|
+
.gsub("'", "'")
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def to_snake_case(str)
|
|
83
|
+
str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
84
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
85
|
+
.downcase
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
def singularize(str)
|
|
89
|
+
# Simple singularization
|
|
90
|
+
case str
|
|
91
|
+
when /ies$/
|
|
92
|
+
str.sub(/ies$/, "y")
|
|
93
|
+
when /es$/
|
|
94
|
+
str.sub(/es$/, "")
|
|
95
|
+
when /s$/
|
|
96
|
+
str.sub(/s$/, "")
|
|
97
|
+
else
|
|
98
|
+
str
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
# :nocov:
|
|
103
|
+
end
|
|
104
|
+
end
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "yaml"
|
|
4
|
+
|
|
5
|
+
# :nocov:
|
|
6
|
+
module Synthra
|
|
7
|
+
module Export
|
|
8
|
+
# Exports generated data as YAML
|
|
9
|
+
#
|
|
10
|
+
# @example Export schema data as YAML
|
|
11
|
+
# exporter = YamlData.new(schema, registry: registry, count: 10)
|
|
12
|
+
# yaml = exporter.export
|
|
13
|
+
#
|
|
14
|
+
class YamlData < Base
|
|
15
|
+
def export
|
|
16
|
+
count = options[:count] || 10
|
|
17
|
+
seed = options[:seed]
|
|
18
|
+
mode = options[:mode] || :random
|
|
19
|
+
|
|
20
|
+
records = if count == 1
|
|
21
|
+
schema.generate(seed: seed, mode: mode, registry: registry) # single record
|
|
22
|
+
else
|
|
23
|
+
schema.generate_many(count, seed: seed, mode: mode, registry: registry)
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
output = wrap_output(records)
|
|
27
|
+
|
|
28
|
+
# Add header comment
|
|
29
|
+
lines = []
|
|
30
|
+
lines << "# Generated from Synthra schema: #{schema.name}"
|
|
31
|
+
lines << "# Version: #{schema.version}" if schema.version
|
|
32
|
+
lines << "# Records: #{count}"
|
|
33
|
+
lines << "# Generated at: #{Time.now.iso8601}"
|
|
34
|
+
lines << ""
|
|
35
|
+
lines << YAML.dump(output)
|
|
36
|
+
|
|
37
|
+
lines.join("\n")
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def wrap_output(records)
|
|
43
|
+
# :nocov:
|
|
44
|
+
if options[:wrap] == false
|
|
45
|
+
records
|
|
46
|
+
# :nocov:
|
|
47
|
+
elsif options[:root_key]
|
|
48
|
+
{ options[:root_key] => records }
|
|
49
|
+
else
|
|
50
|
+
records
|
|
51
|
+
end
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
# :nocov:
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative "export/base"
|
|
4
|
+
require_relative "export/json_schema"
|
|
5
|
+
require_relative "export/typescript"
|
|
6
|
+
require_relative "export/javascript"
|
|
7
|
+
require_relative "export/python"
|
|
8
|
+
require_relative "export/sql"
|
|
9
|
+
require_relative "export/csv"
|
|
10
|
+
require_relative "export/graphviz"
|
|
11
|
+
require_relative "export/graphql"
|
|
12
|
+
require_relative "export/json_data"
|
|
13
|
+
require_relative "export/sql_insert"
|
|
14
|
+
require_relative "export/yaml_data"
|
|
15
|
+
require_relative "export/xml_data"
|
|
16
|
+
|
|
17
|
+
module Synthra
|
|
18
|
+
# Export module for converting schemas to various formats
|
|
19
|
+
#
|
|
20
|
+
# Supports two categories of exports:
|
|
21
|
+
#
|
|
22
|
+
# ## Schema Exports (structure/types)
|
|
23
|
+
# - JSON Schema (draft 2020-12)
|
|
24
|
+
# - TypeScript interfaces
|
|
25
|
+
# - JavaScript with JSDoc types
|
|
26
|
+
# - SQL CREATE TABLE statements
|
|
27
|
+
# - GraphViz DOT diagrams
|
|
28
|
+
#
|
|
29
|
+
# ## Data Exports (generated records)
|
|
30
|
+
# - JSON data
|
|
31
|
+
# - CSV data
|
|
32
|
+
# - SQL INSERT statements
|
|
33
|
+
# - YAML data
|
|
34
|
+
# - XML data
|
|
35
|
+
#
|
|
36
|
+
# @example Export schema as TypeScript
|
|
37
|
+
# Synthra::Export.typescript(schema, registry: registry)
|
|
38
|
+
#
|
|
39
|
+
# @example Export generated data as JSON
|
|
40
|
+
# Synthra::Export.json_data(schema, registry: registry, count: 100)
|
|
41
|
+
#
|
|
42
|
+
# @example Export generated data as SQL INSERTs
|
|
43
|
+
# Synthra::Export.sql_insert(schema, count: 50, dialect: :postgresql)
|
|
44
|
+
#
|
|
45
|
+
module Export
|
|
46
|
+
# =========================================================================
|
|
47
|
+
# Schema Export Formats (types/structure)
|
|
48
|
+
# =========================================================================
|
|
49
|
+
SCHEMA_FORMATS = {
|
|
50
|
+
"json-schema" => JsonSchema,
|
|
51
|
+
"typescript" => Typescript,
|
|
52
|
+
"ts" => Typescript,
|
|
53
|
+
"javascript" => Javascript,
|
|
54
|
+
"js" => Javascript,
|
|
55
|
+
"python" => Python,
|
|
56
|
+
"py" => Python,
|
|
57
|
+
"pydantic" => Python,
|
|
58
|
+
"dataclass" => Python,
|
|
59
|
+
"sql" => Sql,
|
|
60
|
+
"sql-ddl" => Sql,
|
|
61
|
+
"postgresql" => Sql,
|
|
62
|
+
"mysql" => Sql,
|
|
63
|
+
"sqlite" => Sql,
|
|
64
|
+
"graphviz" => Graphviz,
|
|
65
|
+
"dot" => Graphviz,
|
|
66
|
+
"graphql" => Graphql,
|
|
67
|
+
"gql" => Graphql
|
|
68
|
+
}.freeze
|
|
69
|
+
|
|
70
|
+
# File extensions for auto-generated filenames
|
|
71
|
+
FILE_EXTENSIONS = {
|
|
72
|
+
"json-schema" => ".schema.json",
|
|
73
|
+
"typescript" => ".ts",
|
|
74
|
+
"ts" => ".ts",
|
|
75
|
+
"javascript" => ".js",
|
|
76
|
+
"js" => ".js",
|
|
77
|
+
"python" => ".py",
|
|
78
|
+
"py" => ".py",
|
|
79
|
+
"pydantic" => ".py",
|
|
80
|
+
"dataclass" => ".py",
|
|
81
|
+
"sql" => ".sql",
|
|
82
|
+
"sql-ddl" => ".sql",
|
|
83
|
+
"postgresql" => ".sql",
|
|
84
|
+
"mysql" => ".sql",
|
|
85
|
+
"sqlite" => ".sql",
|
|
86
|
+
"graphviz" => ".dot",
|
|
87
|
+
"dot" => ".dot",
|
|
88
|
+
"graphql" => ".graphql",
|
|
89
|
+
"gql" => ".graphql",
|
|
90
|
+
"json" => ".json",
|
|
91
|
+
"json-data" => ".json",
|
|
92
|
+
"csv" => ".csv",
|
|
93
|
+
"sql-insert" => ".sql",
|
|
94
|
+
"insert" => ".sql",
|
|
95
|
+
"yaml" => ".yaml",
|
|
96
|
+
"yml" => ".yaml",
|
|
97
|
+
"xml" => ".xml"
|
|
98
|
+
}.freeze
|
|
99
|
+
|
|
100
|
+
# =========================================================================
|
|
101
|
+
# Data Export Formats (generated records)
|
|
102
|
+
# =========================================================================
|
|
103
|
+
DATA_FORMATS = {
|
|
104
|
+
"json" => JsonData,
|
|
105
|
+
"json-data" => JsonData,
|
|
106
|
+
"csv" => Csv,
|
|
107
|
+
"sql-insert" => SqlInsert,
|
|
108
|
+
"insert" => SqlInsert,
|
|
109
|
+
"yaml" => YamlData,
|
|
110
|
+
"yml" => YamlData,
|
|
111
|
+
"xml" => XmlData
|
|
112
|
+
}.freeze
|
|
113
|
+
|
|
114
|
+
# All available formats
|
|
115
|
+
FORMATS = SCHEMA_FORMATS.merge(DATA_FORMATS).freeze
|
|
116
|
+
|
|
117
|
+
class << self
|
|
118
|
+
# Export schema to specified format
|
|
119
|
+
#
|
|
120
|
+
# @param format [String, Symbol] the export format
|
|
121
|
+
# @param schema [Schema] the schema to export
|
|
122
|
+
# @param registry [Registry] the registry for resolving references
|
|
123
|
+
# @param options [Hash] format-specific options
|
|
124
|
+
# @return [String] the exported content
|
|
125
|
+
#
|
|
126
|
+
def export(format, schema, registry: nil, **options)
|
|
127
|
+
format_key = format.to_s.downcase
|
|
128
|
+
exporter_class = FORMATS[format_key]
|
|
129
|
+
|
|
130
|
+
unless exporter_class
|
|
131
|
+
available = available_formats.join(", ")
|
|
132
|
+
raise ArgumentError, "Unknown format: #{format}. Available: #{available}"
|
|
133
|
+
end
|
|
134
|
+
|
|
135
|
+
# Set dialect for SQL formats
|
|
136
|
+
if %w[postgresql mysql sqlite].include?(format_key)
|
|
137
|
+
options[:dialect] = format_key.to_sym
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
exporter = exporter_class.new(schema, registry: registry, **options)
|
|
141
|
+
exporter.export
|
|
142
|
+
end
|
|
143
|
+
|
|
144
|
+
# =====================================================================
|
|
145
|
+
# Schema Export Methods
|
|
146
|
+
# =====================================================================
|
|
147
|
+
|
|
148
|
+
# Export schema as JSON Schema
|
|
149
|
+
#
|
|
150
|
+
# @param schema [Schema] the schema to export
|
|
151
|
+
# @param options [Hash] export options
|
|
152
|
+
# @return [String] JSON Schema string
|
|
153
|
+
#
|
|
154
|
+
def json_schema(schema, **options)
|
|
155
|
+
JsonSchema.new(schema, **options).export
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
# Export schema as TypeScript interface
|
|
159
|
+
#
|
|
160
|
+
# @param schema [Schema] the schema to export
|
|
161
|
+
# @param options [Hash] export options
|
|
162
|
+
# @return [String] TypeScript interface string
|
|
163
|
+
#
|
|
164
|
+
def typescript(schema, **options)
|
|
165
|
+
Typescript.new(schema, **options).export
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
# Export schema as JavaScript with JSDoc
|
|
169
|
+
#
|
|
170
|
+
# @param schema [Schema] the schema to export
|
|
171
|
+
# @param options [Hash] export options
|
|
172
|
+
# @return [String] JavaScript with JSDoc types
|
|
173
|
+
#
|
|
174
|
+
def javascript(schema, **options)
|
|
175
|
+
Javascript.new(schema, **options).export
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
# Export schema as Python (Pydantic/dataclass/TypedDict)
|
|
179
|
+
#
|
|
180
|
+
# @param schema [Schema] the schema to export
|
|
181
|
+
# @param options [Hash] export options (style: :pydantic/:dataclass/:typed_dict)
|
|
182
|
+
# @return [String] Python code
|
|
183
|
+
#
|
|
184
|
+
def python(schema, **options)
|
|
185
|
+
Python.new(schema, **options).export
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
# Export schema as SQL CREATE TABLE
|
|
189
|
+
#
|
|
190
|
+
# @param schema [Schema] the schema to export
|
|
191
|
+
# @param options [Hash] export options (dialect: :postgresql/:mysql/:sqlite)
|
|
192
|
+
# @return [String] SQL statement string
|
|
193
|
+
#
|
|
194
|
+
def sql(schema, **options)
|
|
195
|
+
Sql.new(schema, **options).export
|
|
196
|
+
end
|
|
197
|
+
|
|
198
|
+
# Export schemas as GraphViz DOT
|
|
199
|
+
#
|
|
200
|
+
# @param schema [Schema, nil] single schema or nil for registry
|
|
201
|
+
# @param options [Hash] export options (registry, direction)
|
|
202
|
+
# @return [String] DOT string
|
|
203
|
+
#
|
|
204
|
+
def graphviz(schema = nil, **options)
|
|
205
|
+
Graphviz.new(schema, **options).export
|
|
206
|
+
end
|
|
207
|
+
|
|
208
|
+
# Export schema as GraphQL SDL
|
|
209
|
+
#
|
|
210
|
+
# @param schema [Schema] the schema to export
|
|
211
|
+
# @param options [Hash] export options (registry)
|
|
212
|
+
# @return [String] GraphQL SDL string
|
|
213
|
+
#
|
|
214
|
+
def graphql(schema, **options)
|
|
215
|
+
Graphql.new(schema, **options).export
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
# =====================================================================
|
|
219
|
+
# Data Export Methods
|
|
220
|
+
# =====================================================================
|
|
221
|
+
|
|
222
|
+
# Export generated data as JSON
|
|
223
|
+
#
|
|
224
|
+
# @param schema [Schema] the schema to generate from
|
|
225
|
+
# @param options [Hash] export options (count, seed, mode, pretty, envelope)
|
|
226
|
+
# @return [String] JSON string
|
|
227
|
+
#
|
|
228
|
+
def json_data(schema, **options)
|
|
229
|
+
JsonData.new(schema, **options).export
|
|
230
|
+
end
|
|
231
|
+
|
|
232
|
+
# Export generated data as CSV
|
|
233
|
+
#
|
|
234
|
+
# @param schema [Schema] the schema to generate from
|
|
235
|
+
# @param options [Hash] export options (count, seed, mode)
|
|
236
|
+
# @return [String] CSV string
|
|
237
|
+
#
|
|
238
|
+
def csv(schema, **options)
|
|
239
|
+
Csv.new(schema, **options).export
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
# Export generated data as SQL INSERT statements
|
|
243
|
+
#
|
|
244
|
+
# @param schema [Schema] the schema to generate from
|
|
245
|
+
# @param options [Hash] export options (count, seed, mode, dialect, batch_insert)
|
|
246
|
+
# @return [String] SQL INSERT statements
|
|
247
|
+
#
|
|
248
|
+
def sql_insert(schema, **options)
|
|
249
|
+
SqlInsert.new(schema, **options).export
|
|
250
|
+
end
|
|
251
|
+
|
|
252
|
+
# Export generated data as YAML
|
|
253
|
+
#
|
|
254
|
+
# @param schema [Schema] the schema to generate from
|
|
255
|
+
# @param options [Hash] export options (count, seed, mode, root_key)
|
|
256
|
+
# @return [String] YAML string
|
|
257
|
+
#
|
|
258
|
+
def yaml_data(schema, **options)
|
|
259
|
+
YamlData.new(schema, **options).export
|
|
260
|
+
end
|
|
261
|
+
|
|
262
|
+
# Export generated data as XML
|
|
263
|
+
#
|
|
264
|
+
# @param schema [Schema] the schema to generate from
|
|
265
|
+
# @param options [Hash] export options (count, seed, mode, root, item, indent)
|
|
266
|
+
# @return [String] XML string
|
|
267
|
+
#
|
|
268
|
+
def xml_data(schema, **options)
|
|
269
|
+
XmlData.new(schema, **options).export
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
# =====================================================================
|
|
273
|
+
# Bulk Export Methods
|
|
274
|
+
# =====================================================================
|
|
275
|
+
|
|
276
|
+
# Export all schemas from a registry to a format
|
|
277
|
+
#
|
|
278
|
+
# @param registry [Registry] the registry
|
|
279
|
+
# @param format [String, Symbol] the export format
|
|
280
|
+
# @param options [Hash] format-specific options
|
|
281
|
+
# @return [String] the exported content
|
|
282
|
+
#
|
|
283
|
+
def export_all(registry, format, **options)
|
|
284
|
+
format_key = format.to_s.downcase
|
|
285
|
+
|
|
286
|
+
case format_key
|
|
287
|
+
when "typescript", "ts"
|
|
288
|
+
Typescript.export_all(registry, **options)
|
|
289
|
+
when "javascript", "js"
|
|
290
|
+
Javascript.export_all(registry, **options)
|
|
291
|
+
when "python", "py", "pydantic", "dataclass"
|
|
292
|
+
options[:style] = :dataclass if format_key == "dataclass"
|
|
293
|
+
Python.export_all(registry, **options)
|
|
294
|
+
when "sql", "postgresql", "mysql", "sqlite", "sql-ddl"
|
|
295
|
+
options[:dialect] = format_key.to_sym if %w[postgresql mysql sqlite].include?(format_key)
|
|
296
|
+
Sql.export_all(registry, **options)
|
|
297
|
+
when "graphviz", "dot"
|
|
298
|
+
Graphviz.new(nil, registry: registry, **options).export
|
|
299
|
+
when "graphql", "gql"
|
|
300
|
+
Graphql.export_all(registry)
|
|
301
|
+
else
|
|
302
|
+
raise ArgumentError, "Bulk export not supported for format: #{format}"
|
|
303
|
+
end
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
# =====================================================================
|
|
307
|
+
# Utility Methods
|
|
308
|
+
# =====================================================================
|
|
309
|
+
|
|
310
|
+
# List available formats
|
|
311
|
+
#
|
|
312
|
+
# @return [Array<String>] available format names
|
|
313
|
+
#
|
|
314
|
+
def available_formats
|
|
315
|
+
FORMATS.keys.uniq.sort
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
# List schema export formats
|
|
319
|
+
#
|
|
320
|
+
# @return [Array<String>] schema format names
|
|
321
|
+
#
|
|
322
|
+
def schema_formats
|
|
323
|
+
SCHEMA_FORMATS.keys.uniq.sort
|
|
324
|
+
end
|
|
325
|
+
|
|
326
|
+
# List data export formats
|
|
327
|
+
#
|
|
328
|
+
# @return [Array<String>] data format names
|
|
329
|
+
#
|
|
330
|
+
def data_formats
|
|
331
|
+
DATA_FORMATS.keys.uniq.sort
|
|
332
|
+
end
|
|
333
|
+
|
|
334
|
+
# Check if format is for schema export
|
|
335
|
+
#
|
|
336
|
+
# @param format [String] format name
|
|
337
|
+
# @return [Boolean]
|
|
338
|
+
#
|
|
339
|
+
def schema_format?(format)
|
|
340
|
+
SCHEMA_FORMATS.key?(format.to_s.downcase)
|
|
341
|
+
end
|
|
342
|
+
|
|
343
|
+
# Check if format is for data export
|
|
344
|
+
#
|
|
345
|
+
# @param format [String] format name
|
|
346
|
+
# @return [Boolean]
|
|
347
|
+
#
|
|
348
|
+
def data_format?(format)
|
|
349
|
+
DATA_FORMATS.key?(format.to_s.downcase)
|
|
350
|
+
end
|
|
351
|
+
|
|
352
|
+
# Get file extension for a format
|
|
353
|
+
#
|
|
354
|
+
# @param format [String] format name
|
|
355
|
+
# @return [String] file extension with dot
|
|
356
|
+
#
|
|
357
|
+
def file_extension(format)
|
|
358
|
+
FILE_EXTENSIONS[format.to_s.downcase] || ".txt"
|
|
359
|
+
end
|
|
360
|
+
|
|
361
|
+
# Generate automatic filename
|
|
362
|
+
#
|
|
363
|
+
# @param schema_name [String] schema name
|
|
364
|
+
# @param format [String] format name
|
|
365
|
+
# @param options [Hash] additional options (prefix, suffix)
|
|
366
|
+
# @return [String] generated filename
|
|
367
|
+
#
|
|
368
|
+
def auto_filename(schema_name, format, **options)
|
|
369
|
+
prefix = options[:prefix] || ""
|
|
370
|
+
suffix = options[:suffix] || ""
|
|
371
|
+
ext = file_extension(format)
|
|
372
|
+
|
|
373
|
+
base = to_snake_case(schema_name)
|
|
374
|
+
"#{prefix}#{base}#{suffix}#{ext}"
|
|
375
|
+
end
|
|
376
|
+
|
|
377
|
+
# Export and save to auto-generated file
|
|
378
|
+
#
|
|
379
|
+
# @param format [String] format name
|
|
380
|
+
# @param schema [Schema] schema to export
|
|
381
|
+
# @param output_dir [String] output directory
|
|
382
|
+
# @param options [Hash] export options
|
|
383
|
+
# @return [String] path to generated file
|
|
384
|
+
#
|
|
385
|
+
def export_to_file(format, schema, output_dir: ".", **options)
|
|
386
|
+
filename = auto_filename(schema.name, format, **options)
|
|
387
|
+
path = File.join(output_dir, filename)
|
|
388
|
+
|
|
389
|
+
content = export(format, schema, **options)
|
|
390
|
+
File.write(path, content)
|
|
391
|
+
|
|
392
|
+
path
|
|
393
|
+
end
|
|
394
|
+
|
|
395
|
+
private
|
|
396
|
+
|
|
397
|
+
def to_snake_case(str)
|
|
398
|
+
str.gsub(/([A-Z]+)([A-Z][a-z])/, '\1_\2')
|
|
399
|
+
.gsub(/([a-z\d])([A-Z])/, '\1_\2')
|
|
400
|
+
.downcase
|
|
401
|
+
end
|
|
402
|
+
end
|
|
403
|
+
end
|
|
404
|
+
end
|