synthra 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (302) hide show
  1. checksums.yaml +7 -0
  2. data/.rspec +4 -0
  3. data/.rubocop.yml +40 -0
  4. data/.yardopts +18 -0
  5. data/ACCESS_DOCS.md +116 -0
  6. data/ADVANCED_FEATURES_SUMMARY.md +245 -0
  7. data/CHANGELOG.md +498 -0
  8. data/CODE_OF_CONDUCT.md +122 -0
  9. data/CONTRIBUTING.md +307 -0
  10. data/DOCUMENTATION.md +204 -0
  11. data/DOCUMENTATION_GUIDE.md +194 -0
  12. data/LICENSE +22 -0
  13. data/README.md +1458 -0
  14. data/Rakefile +128 -0
  15. data/benchmark/README.md +591 -0
  16. data/benchmark/batch_generation.rb +63 -0
  17. data/benchmark/engine_comparison.rb +295 -0
  18. data/benchmark/single_record.rb +108 -0
  19. data/benchmark/streaming.rb +74 -0
  20. data/docs/ACTIVERECORD_INFERENCE.md +524 -0
  21. data/docs/API_SERVER.md +278 -0
  22. data/docs/CONFIG_FILE.md +315 -0
  23. data/docs/DATA_CONTRACTS.md +312 -0
  24. data/docs/ENHANCED_REPL.md +304 -0
  25. data/docs/FACTORY_BOT.md +271 -0
  26. data/docs/GITHUB_ACTION.md +420 -0
  27. data/docs/GRAPHQL_EXPORT.md +303 -0
  28. data/docs/GRAPHQL_FEDERATION.md +651 -0
  29. data/docs/HOW_TO_GENERATE_DOCS.md +252 -0
  30. data/docs/LSP.md +189 -0
  31. data/docs/MIGRATION_GENERATOR.md +547 -0
  32. data/docs/MOCK_SERVER.md +280 -0
  33. data/docs/NATIVE_ENGINE.md +298 -0
  34. data/docs/OPENAPI_EXPORT.md +252 -0
  35. data/docs/PERFORMANCE_MODE.md +228 -0
  36. data/docs/PERSONAS.md +547 -0
  37. data/docs/PROPERTY_TESTING.md +284 -0
  38. data/docs/PROTOBUF_EXPORT.md +276 -0
  39. data/docs/QUALITY_METRICS.md +555 -0
  40. data/docs/QUICK_REFERENCE.md +88 -0
  41. data/docs/RAILS_ENGINE.md +404 -0
  42. data/docs/RAILS_INTEGRATION.md +454 -0
  43. data/docs/README.md +175 -0
  44. data/docs/REPL_QUICK_REFERENCE.md +149 -0
  45. data/docs/RUST_INTEGRATION_GUIDE.md +1376 -0
  46. data/docs/SCENARIOS.md +576 -0
  47. data/docs/SECURITY_TESTING.md +318 -0
  48. data/docs/SNAPSHOT_TESTING.md +308 -0
  49. data/docs/STRING_TEXT_TYPES_REFERENCE.md +512 -0
  50. data/docs/TERRAFORM_EXPORT.md +447 -0
  51. data/docs/TIME_TRAVEL.md +507 -0
  52. data/docs/USAGE_GUIDE.md +980 -0
  53. data/docs/WEBHOOK_SIMULATOR.md +579 -0
  54. data/docs/advanced_features.md +183 -0
  55. data/docs/api_reference.md +153 -0
  56. data/docs/behaviors.md +138 -0
  57. data/docs/best_practices.md +191 -0
  58. data/docs/dsl_reference.md +172 -0
  59. data/docs/examples.md +162 -0
  60. data/docs/generation_modes.md +125 -0
  61. data/docs/getting_started.md +92 -0
  62. data/docs/index.html +177 -0
  63. data/docs/strategic/CLOUD_REGISTRY_ARCHITECTURE.md +268 -0
  64. data/docs/strategic/SIMD_OPTIMIZATION_STRATEGY.md +153 -0
  65. data/docs/troubleshooting.md +215 -0
  66. data/docs/type_reference.md +224 -0
  67. data/examples/hostile_mode_demo.rb +64 -0
  68. data/exe/synthra +6 -0
  69. data/ext/synthra_native/Cargo.lock +972 -0
  70. data/ext/synthra_native/Cargo.toml +46 -0
  71. data/ext/synthra_native/extconf.rb +41 -0
  72. data/ext/synthra_native/src/generator.rs +397 -0
  73. data/ext/synthra_native/src/lib.rs +229 -0
  74. data/ext/synthra_native/src/types.rs +1485 -0
  75. data/lib/generators/synthra/install_generator.rb +86 -0
  76. data/lib/generators/synthra/templates/api_response.dsl +15 -0
  77. data/lib/generators/synthra/templates/fake_data.rake +116 -0
  78. data/lib/generators/synthra/templates/synthra.yml +31 -0
  79. data/lib/generators/synthra/templates/synthra_support.rb +19 -0
  80. data/lib/generators/synthra/templates/user.dsl +11 -0
  81. data/lib/synthra/activerecord_inference.rb +394 -0
  82. data/lib/synthra/api.rb +381 -0
  83. data/lib/synthra/api_server.rb +718 -0
  84. data/lib/synthra/behaviors/applicator.rb +165 -0
  85. data/lib/synthra/behaviors/base.rb +155 -0
  86. data/lib/synthra/behaviors/close_connection.rb +60 -0
  87. data/lib/synthra/behaviors/deprecated.rb +100 -0
  88. data/lib/synthra/behaviors/failure.rb +69 -0
  89. data/lib/synthra/behaviors/latency.rb +103 -0
  90. data/lib/synthra/behaviors/partial_data.rb +83 -0
  91. data/lib/synthra/behaviors/randomize_order.rb +74 -0
  92. data/lib/synthra/behaviors/registry.rb +238 -0
  93. data/lib/synthra/behaviors/simulate_error.rb +79 -0
  94. data/lib/synthra/cli/commands/base.rb +105 -0
  95. data/lib/synthra/cli/commands/diff.rb +227 -0
  96. data/lib/synthra/cli/commands/docs.rb +67 -0
  97. data/lib/synthra/cli/commands/export.rb +150 -0
  98. data/lib/synthra/cli/commands/generate.rb +72 -0
  99. data/lib/synthra/cli/commands/import.rb +80 -0
  100. data/lib/synthra/cli/commands/lint.rb +53 -0
  101. data/lib/synthra/cli/commands/live.rb +57 -0
  102. data/lib/synthra/cli/commands/seed.rb +99 -0
  103. data/lib/synthra/cli/commands/validate.rb +28 -0
  104. data/lib/synthra/cli.rb +2471 -0
  105. data/lib/synthra/config_file.rb +129 -0
  106. data/lib/synthra/configuration.rb +281 -0
  107. data/lib/synthra/contracts_registry.rb +408 -0
  108. data/lib/synthra/database_seeder.rb +268 -0
  109. data/lib/synthra/deterministic_ids.rb +218 -0
  110. data/lib/synthra/documentation_generator.rb +414 -0
  111. data/lib/synthra/engine.rb +251 -0
  112. data/lib/synthra/errors.rb +1169 -0
  113. data/lib/synthra/export/base.rb +85 -0
  114. data/lib/synthra/export/csv.rb +101 -0
  115. data/lib/synthra/export/graphql.rb +266 -0
  116. data/lib/synthra/export/graphql_federation.rb +377 -0
  117. data/lib/synthra/export/graphviz.rb +258 -0
  118. data/lib/synthra/export/javascript.rb +327 -0
  119. data/lib/synthra/export/json_data.rb +61 -0
  120. data/lib/synthra/export/json_schema.rb +290 -0
  121. data/lib/synthra/export/openapi.rb +514 -0
  122. data/lib/synthra/export/protobuf.rb +483 -0
  123. data/lib/synthra/export/python.rb +560 -0
  124. data/lib/synthra/export/sql.rb +381 -0
  125. data/lib/synthra/export/sql_insert.rb +152 -0
  126. data/lib/synthra/export/terraform.rb +501 -0
  127. data/lib/synthra/export/type_mapping.rb +389 -0
  128. data/lib/synthra/export/typescript.rb +300 -0
  129. data/lib/synthra/export/xml_data.rb +104 -0
  130. data/lib/synthra/export/yaml_data.rb +56 -0
  131. data/lib/synthra/export.rb +404 -0
  132. data/lib/synthra/factory_bot_integration.rb +157 -0
  133. data/lib/synthra/field.rb +440 -0
  134. data/lib/synthra/functions/registry.rb +104 -0
  135. data/lib/synthra/generator/context.rb +372 -0
  136. data/lib/synthra/generator/engine.rb +336 -0
  137. data/lib/synthra/generator/faker_adapter.rb +425 -0
  138. data/lib/synthra/generator/modes.rb +444 -0
  139. data/lib/synthra/generator/resolver.rb +256 -0
  140. data/lib/synthra/generator/rng.rb +273 -0
  141. data/lib/synthra/generator/streamer.rb +63 -0
  142. data/lib/synthra/generator/uniqueness.rb +118 -0
  143. data/lib/synthra/initializer.rb +97 -0
  144. data/lib/synthra/limits.rb +279 -0
  145. data/lib/synthra/live_preview.rb +518 -0
  146. data/lib/synthra/loader_config.rb +78 -0
  147. data/lib/synthra/lsp/server.rb +689 -0
  148. data/lib/synthra/migration_generator.rb +301 -0
  149. data/lib/synthra/mixin.rb +174 -0
  150. data/lib/synthra/mock_server.rb +458 -0
  151. data/lib/synthra/native_engine.rb +304 -0
  152. data/lib/synthra/openapi_importer.rb +228 -0
  153. data/lib/synthra/output/json_formatter.rb +40 -0
  154. data/lib/synthra/output/ndjson_formatter.rb +55 -0
  155. data/lib/synthra/parser/ast.rb +1287 -0
  156. data/lib/synthra/parser/lexer.rb +1152 -0
  157. data/lib/synthra/parser/parser.rb +1664 -0
  158. data/lib/synthra/parser/tokens.rb +461 -0
  159. data/lib/synthra/performance_mode.rb +364 -0
  160. data/lib/synthra/personas.rb +397 -0
  161. data/lib/synthra/property_testing.rb +172 -0
  162. data/lib/synthra/quality_metrics.rb +405 -0
  163. data/lib/synthra/rails_test_helper.rb +248 -0
  164. data/lib/synthra/registry.rb +533 -0
  165. data/lib/synthra/relationships.rb +193 -0
  166. data/lib/synthra/repl/enhanced_repl.rb +607 -0
  167. data/lib/synthra/repl/formatter.rb +191 -0
  168. data/lib/synthra/scenarios.rb +423 -0
  169. data/lib/synthra/schema.rb +605 -0
  170. data/lib/synthra/schema_inheritance.rb +104 -0
  171. data/lib/synthra/schema_versioning.rb +212 -0
  172. data/lib/synthra/snapshot_testing.rb +199 -0
  173. data/lib/synthra/time_travel.rb +338 -0
  174. data/lib/synthra/type_definitions.rb +274 -0
  175. data/lib/synthra/types/address_location/addresses.rb +125 -0
  176. data/lib/synthra/types/address_location/airports.rb +200 -0
  177. data/lib/synthra/types/address_location/banks_hospitals.rb +207 -0
  178. data/lib/synthra/types/address_location/locations.rb +406 -0
  179. data/lib/synthra/types/base.rb +48 -0
  180. data/lib/synthra/types/commerce_products/commerce.rb +119 -0
  181. data/lib/synthra/types/commerce_products/companies.rb +71 -0
  182. data/lib/synthra/types/commerce_products/construction.rb +126 -0
  183. data/lib/synthra/types/commerce_products/products.rb +205 -0
  184. data/lib/synthra/types/core/collections.rb +513 -0
  185. data/lib/synthra/types/core/defaults.rb +76 -0
  186. data/lib/synthra/types/core/enums.rb +102 -0
  187. data/lib/synthra/types/core/identifiers.rb +445 -0
  188. data/lib/synthra/types/core/primitives.rb +586 -0
  189. data/lib/synthra/types/core/references.rb +466 -0
  190. data/lib/synthra/types/core/sequences.rb +304 -0
  191. data/lib/synthra/types/crypto/crypto.rb +162 -0
  192. data/lib/synthra/types/date_time/dates.rb +387 -0
  193. data/lib/synthra/types/finance_banking/banking.rb +424 -0
  194. data/lib/synthra/types/finance_banking/credit_cards.rb +24 -0
  195. data/lib/synthra/types/finance_banking/identifiers.rb +93 -0
  196. data/lib/synthra/types/formula.rb +184 -0
  197. data/lib/synthra/types/health_medical/medical.rb +150 -0
  198. data/lib/synthra/types/hostile_payloads.rb +122 -0
  199. data/lib/synthra/types/json_array.rb +51 -0
  200. data/lib/synthra/types/media_entertainment/media.rb +62 -0
  201. data/lib/synthra/types/naughty_string.rb +30 -0
  202. data/lib/synthra/types/personal_names/chinese.rb +41 -0
  203. data/lib/synthra/types/personal_names/identifiers.rb +187 -0
  204. data/lib/synthra/types/personal_names/names.rb +246 -0
  205. data/lib/synthra/types/personal_names/national_id.rb +89 -0
  206. data/lib/synthra/types/personal_names/titles_suffixes.rb +41 -0
  207. data/lib/synthra/types/regex.rb +248 -0
  208. data/lib/synthra/types/registry.rb +182 -0
  209. data/lib/synthra/types/repeating_element.rb +50 -0
  210. data/lib/synthra/types/scenario.rb +29 -0
  211. data/lib/synthra/types/technology_internet/apps.rb +67 -0
  212. data/lib/synthra/types/technology_internet/communication.rb +162 -0
  213. data/lib/synthra/types/technology_internet/devices.rb +80 -0
  214. data/lib/synthra/types/technology_internet/formats.rb +139 -0
  215. data/lib/synthra/types/technology_internet/networking.rb +143 -0
  216. data/lib/synthra/types/template.rb +92 -0
  217. data/lib/synthra/types/text_content/business.rb +128 -0
  218. data/lib/synthra/types/text_content/colors.rb +70 -0
  219. data/lib/synthra/types/text_content/misc.rb +237 -0
  220. data/lib/synthra/types/text_content/security.rb +130 -0
  221. data/lib/synthra/types/text_content/text_generation.rb +532 -0
  222. data/lib/synthra/types/travel/travel.rb +150 -0
  223. data/lib/synthra/utils/string_distance.rb +133 -0
  224. data/lib/synthra/validator/dsl_validator.rb +339 -0
  225. data/lib/synthra/validator/path_validator.rb +615 -0
  226. data/lib/synthra/version.rb +35 -0
  227. data/lib/synthra/webhook_simulator.rb +341 -0
  228. data/lib/synthra.rb +259 -0
  229. data/schemas/address.dsl +16 -0
  230. data/schemas/api_response.dsl +8 -0
  231. data/schemas/error_payload.dsl +9 -0
  232. data/schemas/order.dsl +10 -0
  233. data/schemas/order_item.dsl +8 -0
  234. data/schemas/payment.dsl +11 -0
  235. data/schemas/social_post.dsl +13 -0
  236. data/schemas/user.dsl +10 -0
  237. data/scripts/batch_fix_all.rb +96 -0
  238. data/scripts/delete_old_files.rb +32 -0
  239. data/scripts/fix_all_grouped_files.rb +137 -0
  240. data/scripts/fix_all_indentation.rb +48 -0
  241. data/scripts/fix_all_syntax.rb +124 -0
  242. data/scripts/fix_grouped_files.rb +184 -0
  243. data/scripts/fix_syntax_errors.rb +108 -0
  244. data/scripts/group_domain_types.rb +112 -0
  245. data/scripts/group_domain_types_fixed.rb +150 -0
  246. data/scripts/merge_domains_to_one_file.rb +68 -0
  247. data/scripts/move_existing_types.rb +130 -0
  248. data/scripts/split_grouped_types.rb +142 -0
  249. data/tech_docs/README.md +134 -0
  250. data/tech_docs/advanced/streaming.md +405 -0
  251. data/tech_docs/advanced/thread_safety.md +253 -0
  252. data/tech_docs/api/overview.md +485 -0
  253. data/tech_docs/appendices/type_chart.md +193 -0
  254. data/tech_docs/basic_concepts.md +383 -0
  255. data/tech_docs/behaviors/overview.md +346 -0
  256. data/tech_docs/dsl/complex_types.md +790 -0
  257. data/tech_docs/dsl/core_types.md +464 -0
  258. data/tech_docs/dsl/datetime_types.md +325 -0
  259. data/tech_docs/dsl/field_modifiers.md +414 -0
  260. data/tech_docs/dsl/grammar.md +431 -0
  261. data/tech_docs/dsl/schema_definition.md +399 -0
  262. data/tech_docs/export/README.md +276 -0
  263. data/tech_docs/installation.md +273 -0
  264. data/tech_docs/integration/ci_cd.md +707 -0
  265. data/tech_docs/integration/ci_cd_guide.md +579 -0
  266. data/tech_docs/integration/factory_bot.md +485 -0
  267. data/tech_docs/integration/rails.md +630 -0
  268. data/tech_docs/integration/rspec.md +449 -0
  269. data/tech_docs/modes/overview.md +350 -0
  270. data/tech_docs/performance/NATIVE_RUST_EXTENSION.md +1270 -0
  271. data/tech_docs/performance/OPTIMIZATION_GUIDE.md +901 -0
  272. data/tech_docs/quick_start.md +256 -0
  273. data/tech_docs/templates/README.md +805 -0
  274. data/tech_docs/tutorials/advanced.md +371 -0
  275. data/tech_docs/tutorials/getting_started.md +189 -0
  276. data/tech_docs/tutorials/intermediate.md +231 -0
  277. data/tech_docs/tutorials/template_gallery.md +569 -0
  278. data/tech_example/01_basic_usage.rb +238 -0
  279. data/tech_example/02_types_demo.rb +336 -0
  280. data/tech_example/04_cli_usage.md +429 -0
  281. data/tech_example/05_database_seeding.rb +283 -0
  282. data/tech_example/07_rspec_integration.rb +359 -0
  283. data/tech_example/10_custom_types.rb +387 -0
  284. data/tech_example/12_behaviors.rb +372 -0
  285. data/tech_example/13_twitter_dm_example.rb +120 -0
  286. data/tech_example/14_exact_json_structure.rb +253 -0
  287. data/tech_example/16_lsp_server.rb +56 -0
  288. data/tech_example/17_property_testing.rb +92 -0
  289. data/tech_example/18_enhanced_repl.rb +120 -0
  290. data/tech_example/19_security_fuzzing.rb +145 -0
  291. data/tech_example/NEW_FEATURES.md +169 -0
  292. data/tech_example/README.md +118 -0
  293. data/tech_example/schemas/api_response.dsl +123 -0
  294. data/tech_example/schemas/ecommerce.dsl +121 -0
  295. data/tech_example/schemas/twitter_dm.dsl +109 -0
  296. data/tech_example/schemas/user.dsl +36 -0
  297. data/vscode-extension/README.md +246 -0
  298. data/vscode-extension/language-configuration.json +31 -0
  299. data/vscode-extension/package.json +55 -0
  300. data/vscode-extension/snippets/fakedatadsl.json +198 -0
  301. data/vscode-extension/syntaxes/fakedatadsl.tmLanguage.json +128 -0
  302. metadata +478 -0
@@ -0,0 +1,83 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Partial Data Behavior
6
+ # =============================================================================
7
+ #
8
+ # Simulates incomplete data by randomly removing fields from generated records.
9
+ # Useful for testing applications that handle partial data gracefully.
10
+ #
11
+ # @example DSL usage
12
+ # User:
13
+ # @partial_data 30% # 30% chance of removing fields
14
+ # name: name
15
+ # email: email
16
+ #
17
+ # @example Field-level usage
18
+ # User:
19
+ # profile: object(Profile) @partial_data 20% # 20% chance of omitting profile
20
+ #
21
+ # =============================================================================
22
+
23
+
24
+ module Synthra
25
+ module Behaviors
26
+
27
+ # Partial data behavior - removes some fields
28
+ #
29
+ # Randomly removes fields from generated records to simulate incomplete
30
+ # data. Prefers removing non-critical fields (excludes "id" and "uuid").
31
+ #
32
+ # @example Apply partial data behavior
33
+ # behavior = PartialData.new(30, rng) # 30% probability
34
+ # result = behavior.apply({ "id" => "1", "name" => "John", "email" => "..." })
35
+ # # => { "id" => "1", "name" => "John" } # email removed 30% of the time
36
+ #
37
+
38
+ class PartialData < Base
39
+
40
+ # Apply partial data behavior to result
41
+ #
42
+ # Removes a random subset of fields (approximately 30% of fields)
43
+ # from the result hash. Preserves critical fields like "id" and "uuid".
44
+ #
45
+ # @param result [Hash] generated record to modify
46
+ # @param context [Generator::Context, nil] generation context (not used)
47
+ # @return [Hash] result with some fields removed
48
+ #
49
+ # @example
50
+ # apply({ "id" => "1", "name" => "John", "email" => "..." }, nil)
51
+ # # => { "id" => "1", "name" => "John" } # email removed
52
+ #
53
+
54
+ def apply(result, context = nil)
55
+ return result unless should_apply?
56
+ return result unless result.is_a?(Hash)
57
+
58
+
59
+ # Remove optional fields first
60
+ keys = result.keys.select do |k|
61
+
62
+ # Prefer removing fields that look optional
63
+ !%w[id uuid].include?(k.to_s)
64
+ end
65
+
66
+ return result if keys.empty?
67
+
68
+
69
+ # Remove a random subset using RNG for determinism
70
+ count_to_remove = (keys.length * 0.3).ceil
71
+ if @rng
72
+ to_remove = keys.shuffle(random: @rng.instance_variable_get(:@random)).take(count_to_remove)
73
+ else
74
+ to_remove = keys.sample(count_to_remove)
75
+ end
76
+ to_remove.each { |k| result.delete(k) }
77
+
78
+ result
79
+ end
80
+ end
81
+ end
82
+ end
83
+
@@ -0,0 +1,74 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Randomize Order Behavior
6
+ # =============================================================================
7
+ #
8
+ # Shuffles array elements in generated records to simulate non-deterministic
9
+ # ordering. Useful for testing applications that handle unordered data.
10
+ #
11
+ # @example DSL usage
12
+ # User:
13
+ # @randomize_order 50% # 50% chance of shuffling arrays
14
+ # tags: array(text, 1..5)
15
+ #
16
+ # =============================================================================
17
+
18
+
19
+ module Synthra
20
+ module Behaviors
21
+
22
+ # Randomize order behavior - shuffles arrays in result
23
+ #
24
+ # Recursively shuffles all array elements in the generated result to
25
+ # simulate non-deterministic ordering.
26
+ #
27
+ # @example Apply randomize order behavior
28
+ # behavior = RandomizeOrder.new(50, rng) # 50% probability
29
+ # result = behavior.apply({ "tags" => ["a", "b", "c"] })
30
+ # # => { "tags" => ["c", "a", "b"] } # Shuffled 50% of the time
31
+ #
32
+
33
+ class RandomizeOrder < Base
34
+
35
+ # Apply randomize order behavior to result
36
+ #
37
+ # Recursively shuffles all arrays found in the result hash.
38
+ #
39
+ # @param result [Object] generated data to modify
40
+ # @param context [Generator::Context, nil] generation context (not used)
41
+ # @return [Object] result with arrays shuffled
42
+ #
43
+
44
+ def apply(result, context = nil)
45
+ return result unless should_apply?
46
+
47
+ shuffle_arrays(result)
48
+ end
49
+
50
+ private
51
+
52
+
53
+ # Recursively shuffle arrays in value
54
+ #
55
+ # Traverses the value structure and shuffles all arrays found.
56
+ #
57
+ # @param value [Object] value to process (Hash, Array, or other)
58
+ # @return [Object] value with arrays shuffled
59
+ #
60
+
61
+ def shuffle_arrays(value)
62
+ case value
63
+ when Array
64
+ value.map { |v| shuffle_arrays(v) }.shuffle
65
+ when Hash
66
+ value.transform_values { |v| shuffle_arrays(v) }
67
+ else
68
+ value
69
+ end
70
+ end
71
+ end
72
+ end
73
+ end
74
+
@@ -0,0 +1,238 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Behaviors Registry
6
+ # =============================================================================
7
+ #
8
+ # The Behaviors::Registry manages registration and lookup of behavior classes.
9
+ # Built-in behaviors are registered automatically at gem load time.
10
+ #
11
+ # @example Register a custom behavior
12
+ # Synthra::Behaviors::Registry.register(:my_behavior, MyBehaviorClass)
13
+ #
14
+ # @example Register with a block
15
+ # Synthra::Behaviors::Registry.register(:log) do |context, result, value|
16
+ # puts "Generated: #{result.inspect}"
17
+ # result
18
+ # end
19
+ #
20
+ # =============================================================================
21
+
22
+
23
+ module Synthra
24
+ module Behaviors
25
+
26
+ # Registry for behavior classes
27
+ #
28
+ # Thread-safe registry for storing and retrieving behavior implementations.
29
+ # Supports both class-based and block-based behavior registration.
30
+ #
31
+ # @example Look up a behavior
32
+ # klass = Registry.lookup(:latency)
33
+ # behavior = klass.new(100..500, rng)
34
+ # behavior.apply(result)
35
+ #
36
+
37
+ class Registry
38
+
39
+ # Internal storage for registered behaviors
40
+ # @api private
41
+ @behaviors = {}
42
+
43
+
44
+ # Mutex for thread-safe access
45
+ # @api private
46
+ @mutex = Mutex.new
47
+
48
+ class << self
49
+
50
+ # Register a behavior
51
+ #
52
+ # @param name [Symbol, String] behavior name (used with @ prefix in DSL)
53
+ # @param klass [Class, nil] behavior class (must inherit from Base)
54
+ # @yield [context, result, value] block for applying behavior
55
+ # @yieldparam context [Generator::Context] generation context
56
+ # @yieldparam result [Object] generated data
57
+ # @yieldparam value [Object] behavior configuration value
58
+ # @yieldreturn [Object] modified result
59
+ # @return [void]
60
+ # @raise [ArgumentError] if neither class nor block provided
61
+ #
62
+ # @example Register with class
63
+ # Registry.register(:latency, Latency)
64
+ #
65
+ # @example Register with block
66
+ # Registry.register(:log) do |ctx, result, value|
67
+ # puts "Generated: #{result.inspect}"
68
+ # result
69
+ # end
70
+ #
71
+
72
+ def register(name, klass = nil, &block)
73
+ @mutex.synchronize do
74
+ if block_given?
75
+ @behaviors[name.to_sym] = BlockBehavior.new(&block)
76
+ elsif klass
77
+ @behaviors[name.to_sym] = klass
78
+ else
79
+ raise ArgumentError, "Must provide a class or block"
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ # Look up a behavior by name
86
+ #
87
+ # @param name [Symbol, String] behavior name to look up
88
+ # @return [Class] behavior class (can be instantiated with new(value, rng))
89
+ # @raise [ArgumentError] if behavior is not registered
90
+ #
91
+ # @example
92
+ # klass = Registry.lookup(:latency)
93
+ # behavior = klass.new(100..500, rng)
94
+ #
95
+
96
+ def lookup(name)
97
+ @mutex.synchronize do
98
+ @behaviors.fetch(name.to_sym) do
99
+ raise ArgumentError, "Unknown behavior: #{name}"
100
+ end
101
+ end
102
+ end
103
+
104
+
105
+ # Check if a behavior is registered
106
+ #
107
+ # @param name [Symbol, String] behavior name to check
108
+ # @return [Boolean] true if registered
109
+ #
110
+ # @example
111
+ # Registry.registered?(:latency) # => true
112
+ # Registry.registered?(:unknown) # => false
113
+ #
114
+
115
+ def registered?(name)
116
+ @mutex.synchronize do
117
+ @behaviors.key?(name.to_sym)
118
+ end
119
+ end
120
+
121
+
122
+ # List all registered behavior names
123
+ #
124
+ # @return [Array<Symbol>] list of behavior names
125
+ #
126
+ # @example
127
+ # Registry.names # => [:latency, :failure, :partial_data, ...]
128
+ #
129
+
130
+ def names
131
+ @mutex.synchronize { @behaviors.keys }
132
+ end
133
+
134
+
135
+ # Clear all registered behaviors
136
+ #
137
+ # Primarily used for testing. After calling this, you'll need
138
+ # to call register_builtins! to restore built-in behaviors.
139
+ #
140
+ # @return [void]
141
+ #
142
+
143
+ def clear
144
+ @mutex.synchronize { @behaviors.clear }
145
+ end
146
+
147
+
148
+ # Register all built-in behaviors
149
+ #
150
+ # Called automatically at gem initialization. Registers the
151
+ # standard behaviors like latency, failure, partial_data, etc.
152
+ #
153
+ # @return [void]
154
+ #
155
+
156
+ def register_builtins!
157
+ register(:latency, Latency)
158
+ register(:failure, Failure)
159
+ register(:partial_data, PartialData)
160
+ register(:close_connection, CloseConnection)
161
+ register(:simulate_error, SimulateError)
162
+ register(:randomize_order, RandomizeOrder)
163
+ register(:deprecated, Deprecated)
164
+ end
165
+ end
166
+
167
+
168
+ # Wrapper for block-based behaviors
169
+ #
170
+ # Allows blocks to be used as behaviors. The wrapper creates
171
+ # BlockBehaviorInstance objects when instantiated.
172
+ #
173
+ # @api private
174
+ #
175
+
176
+ class BlockBehavior
177
+
178
+ # Create a new block behavior wrapper
179
+ #
180
+ # @yield [context, result, value] block for applying behavior
181
+ #
182
+
183
+ def initialize(&block)
184
+ @block = block
185
+ end
186
+
187
+
188
+ # Create a new instance of this block behavior
189
+ #
190
+ # @param value [Object] behavior configuration
191
+ # @param rng [Generator::RNG, nil] random number generator
192
+ # @return [BlockBehaviorInstance] behavior instance
193
+ #
194
+
195
+ def new(value, rng = nil)
196
+ BlockBehaviorInstance.new(value, rng, @block)
197
+ end
198
+ end
199
+
200
+
201
+ # Instance of a block-based behavior
202
+ #
203
+ # Wraps a block in a Base-compatible interface.
204
+ #
205
+ # @api private
206
+ #
207
+
208
+ class BlockBehaviorInstance < Base
209
+
210
+ # Create a new block behavior instance
211
+ #
212
+ # @param value [Object] behavior configuration
213
+ # @param rng [Generator::RNG, nil] random number generator
214
+ # @param block [Proc] the block to call when applying
215
+ #
216
+
217
+ def initialize(value, rng, block)
218
+ super(value, rng)
219
+ @block = block
220
+ end
221
+
222
+
223
+ # Apply the block behavior
224
+ #
225
+ # @param result [Object] generated data
226
+ # @param context [Generator::Context, nil] generation context
227
+ # @return [Object] result from block
228
+ #
229
+
230
+ def apply(result, context = nil)
231
+ return result unless should_apply?
232
+
233
+ @block.call(context, result, value)
234
+ end
235
+ end
236
+ end
237
+ end
238
+ end
@@ -0,0 +1,79 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Simulate Error Behavior
6
+ # =============================================================================
7
+ #
8
+ # Simulates HTTP-like errors by raising SimulatedError exception with
9
+ # a specified error code. Useful for testing error handling logic.
10
+ #
11
+ # @example DSL usage
12
+ # User:
13
+ # @simulate_error 500 10% # 10% chance of 500 error
14
+ # @simulate_error 404 5% # 5% chance of 404 error
15
+ # name: name
16
+ #
17
+ # =============================================================================
18
+
19
+
20
+ module Synthra
21
+ module Behaviors
22
+
23
+ # Simulate error behavior - raises HTTP-like error
24
+ #
25
+ # Raises SimulatedError exception with a specified HTTP status code
26
+ # when activated, simulating API errors.
27
+ #
28
+ # @example Apply simulate error behavior
29
+ # behavior = SimulateError.new(500, rng) # 500 error
30
+ # begin
31
+ # behavior.apply({ "name" => "John" })
32
+ # rescue SimulatedError => e
33
+ # e.code # => 500
34
+ # end
35
+ #
36
+
37
+ class SimulateError < Base
38
+
39
+ # Apply simulate error behavior
40
+ #
41
+ # Raises SimulatedError if the behavior should be applied based
42
+ # on probability.
43
+ #
44
+ # @param result [Object] generated data (not modified)
45
+ # @param context [Generator::Context, nil] generation context (not used)
46
+ # @return [Object] result (if behavior not applied)
47
+ # @raise [SimulatedError] if behavior activates
48
+ #
49
+
50
+ def apply(result, context = nil)
51
+ if should_apply?
52
+ code = extract_code
53
+ probability = extract_probability
54
+ raise SimulatedError.new(code: code, probability: probability)
55
+ end
56
+ result
57
+ end
58
+
59
+ private
60
+
61
+
62
+ # Extract error code from value
63
+ #
64
+ # Extracts the HTTP status code from the behavior value configuration.
65
+ #
66
+ # @return [Integer] HTTP status code (default: 500)
67
+ #
68
+
69
+ def extract_code
70
+ case value
71
+ when Integer then value
72
+ when Hash then value[:code] || 500
73
+ else 500
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
79
+
@@ -0,0 +1,105 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Synthra
4
+ class CLI
5
+ module Commands
6
+ # Base class for all CLI commands
7
+ #
8
+ # Provides common functionality for command execution including
9
+ # option parsing, error handling, and registry management.
10
+ #
11
+ class Base
12
+ # Exit codes
13
+ EXIT_SUCCESS = 0
14
+ EXIT_PARSE_ERROR = 1
15
+ EXIT_RUNTIME_ERROR = 2
16
+
17
+ attr_reader :args, :options
18
+
19
+ # Create a new command instance
20
+ #
21
+ # @param args [Array<String>] command arguments
22
+ # @param options [Hash] parsed options
23
+ #
24
+ def initialize(args, options)
25
+ @args = args
26
+ @options = options
27
+ end
28
+
29
+ # Execute the command
30
+ #
31
+ # @return [Integer] exit code
32
+ #
33
+ def call
34
+ raise NotImplementedError, "#{self.class} must implement #call"
35
+ end
36
+
37
+ protected
38
+
39
+ # Load schemas from a directory
40
+ #
41
+ # @param registry [Registry] registry to load schemas into
42
+ # @param dir [String] directory path
43
+ # @param validate [Boolean] whether to validate on load
44
+ # @return [void]
45
+ #
46
+ def load_schemas_from_dir(registry, dir, validate: true)
47
+ dir = File.expand_path(dir)
48
+ raise Errno::ENOENT, "Directory not found: #{dir}" unless Dir.exist?(dir)
49
+
50
+ pattern = File.join(dir, "*.dsl")
51
+ files = Dir.glob(pattern)
52
+
53
+ if files.empty?
54
+ $stderr.puts "No .dsl files found in #{dir}"
55
+ return
56
+ end
57
+
58
+ files.each do |f|
59
+ file_path = File.expand_path(f)
60
+ raise ArgumentError, "Invalid file path: #{f}" unless file_path.start_with?(dir)
61
+ registry.load_file(f, validate: validate)
62
+ end
63
+ end
64
+
65
+ # Load from path (file or directory)
66
+ #
67
+ # @param registry [Registry] registry to load into
68
+ # @param path [String] file or directory path
69
+ # @param validate [Boolean] whether to validate
70
+ #
71
+ def load_path(registry, path, validate: false)
72
+ if File.directory?(path)
73
+ load_schemas_from_dir(registry, path, validate: validate)
74
+ else
75
+ registry.load_file(path, validate: validate)
76
+ end
77
+ end
78
+
79
+ # Print success message
80
+ #
81
+ # @param message [String] message to print
82
+ #
83
+ def success(message)
84
+ puts "✓ #{message}"
85
+ end
86
+
87
+ # Print error message
88
+ #
89
+ # @param message [String] message to print
90
+ #
91
+ def error(message)
92
+ $stderr.puts "✗ #{message}"
93
+ end
94
+
95
+ # Print info message
96
+ #
97
+ # @param message [String] message to print
98
+ #
99
+ def info(message)
100
+ puts message
101
+ end
102
+ end
103
+ end
104
+ end
105
+ end