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,165 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Behaviors Applicator
6
+ # =============================================================================
7
+ #
8
+ # The Applicator is responsible for applying behaviors to generated data.
9
+ # It handles both schema-level behaviors (applied to entire records) and
10
+ # field-level behaviors (applied to individual field values).
11
+ #
12
+ # @example Usage (internal)
13
+ # applicator = Applicator.new(schema, rng)
14
+ # result = applicator.apply_schema_behaviors(generated_record)
15
+ #
16
+ # =============================================================================
17
+
18
+
19
+ module Synthra
20
+ module Behaviors
21
+
22
+ # Applies behaviors to generated data
23
+ #
24
+ # The Applicator coordinates behavior application, looking up behavior
25
+ # classes from the registry and applying them in order. It handles
26
+ # both schema-level and field-level behaviors.
27
+ #
28
+ # @example Apply schema behaviors
29
+ # applicator = Applicator.new(schema, rng)
30
+ # result = applicator.apply_schema_behaviors({ "name" => "John" })
31
+ # # May add latency, raise errors, etc.
32
+ #
33
+ # @example Apply field behaviors
34
+ # value = applicator.apply_field_behaviors(field, "original_value")
35
+ # # => modified value or :omit to remove field
36
+ #
37
+
38
+ class Applicator
39
+
40
+ # Create a new Applicator
41
+ #
42
+ # @param schema [Schema] the schema containing behavior definitions
43
+ # @param rng [Generator::RNG] random number generator for deterministic behavior
44
+ #
45
+ # @example
46
+ # applicator = Applicator.new(schema, rng)
47
+ #
48
+
49
+ def initialize(schema, rng)
50
+ @schema = schema
51
+ @rng = rng
52
+ end
53
+
54
+
55
+ # Apply schema-level behaviors to a generated record
56
+ #
57
+ # Applies all behaviors defined at the schema level (e.g., @latency,
58
+ # @failure) to the complete generated record. Behaviors are applied
59
+ # in the order they were defined.
60
+ #
61
+ # @param result [Hash] the generated record
62
+ # @return [Hash] the result after all behaviors have been applied
63
+ # @raise [SimulatedFailure] if failure behavior triggers
64
+ # @raise [SimulatedConnectionDrop] if close_connection behavior triggers
65
+ # @raise [SimulatedError] if simulate_error behavior triggers
66
+ #
67
+ # @example
68
+ # # Schema with @latency 100ms @failure 10%
69
+ # result = applicator.apply_schema_behaviors({ "id" => "123" })
70
+ # # May add 100ms delay, or raise SimulatedFailure 10% of the time
71
+ #
72
+
73
+ def apply_schema_behaviors(result)
74
+ @schema.all_behaviors.each do |behavior_config|
75
+ name = behavior_config[:name]
76
+ value = behavior_config[:value]
77
+
78
+ begin
79
+
80
+ # Look up the behavior class from registry
81
+ behavior_class = Registry.lookup(name)
82
+
83
+
84
+ # Create behavior instance
85
+ # Handle both Class and BlockBehavior patterns
86
+ behavior = behavior_class.respond_to?(:new) ?
87
+ (behavior_class.is_a?(Class) ? behavior_class.new(value, @rng) : behavior_class.new(value, @rng)) :
88
+ behavior_class
89
+
90
+
91
+ # Apply the behavior, which may modify result or raise errors
92
+ result = behavior.apply(result)
93
+ rescue ArgumentError => e
94
+
95
+ # Only skip if it's an "Unknown behavior" error
96
+ # Re-raise other ArgumentErrors (e.g., wrong number of arguments)
97
+ if e.message.include?("Unknown behavior")
98
+
99
+ # Unknown behavior - skip silently
100
+ # This allows forward compatibility with unknown behaviors
101
+ else
102
+
103
+ # Re-raise real argument errors
104
+ raise
105
+ end
106
+ end
107
+ end
108
+ result
109
+ end
110
+
111
+
112
+ # Apply field-level behaviors to a field value
113
+ #
114
+ # Applies behaviors defined on individual fields (e.g., @partial_data,
115
+ # @latency). Field behaviors can modify the value or indicate that
116
+ # the field should be omitted entirely.
117
+ #
118
+ # @param field [Field] the field definition with behaviors
119
+ # @param value [Object] the generated field value
120
+ # @return [Object, Symbol] the modified value, or :omit to remove the field
121
+ #
122
+ # @example Apply partial_data behavior
123
+ # # Field with @partial_data 20%
124
+ # result = applicator.apply_field_behaviors(field, "value")
125
+ # # => "value" (80% of the time) or :omit (20% of the time)
126
+ #
127
+
128
+ def apply_field_behaviors(field, value)
129
+ field.behaviors.each do |fb|
130
+ begin
131
+
132
+ # Look up and instantiate the behavior
133
+ behavior_class = Registry.lookup(fb.type)
134
+ behavior = behavior_class.respond_to?(:new) ?
135
+ (behavior_class.is_a?(Class) ? behavior_class.new(fb.value, @rng) : behavior_class.new(fb.value, @rng)) :
136
+ behavior_class
137
+
138
+
139
+ # Special handling for partial_data - return :omit symbol
140
+ if fb.type == :partial_data && behavior.should_apply?
141
+ return :omit
142
+ end
143
+
144
+
145
+ # Apply other behaviors normally
146
+ value = behavior.apply(value)
147
+ rescue ArgumentError => e
148
+
149
+ # Only skip if it's an "Unknown behavior" error
150
+ # Re-raise other ArgumentErrors (e.g., wrong number of arguments)
151
+ if e.message.include?("Unknown behavior")
152
+
153
+ # Unknown behavior - skip silently
154
+ else
155
+
156
+ # Re-raise real argument errors
157
+ raise
158
+ end
159
+ end
160
+ end
161
+ value
162
+ end
163
+ end
164
+ end
165
+ end
@@ -0,0 +1,155 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Behaviors Base Class
6
+ # =============================================================================
7
+ #
8
+ # The Base class is the foundation for all behavior implementations.
9
+ # Behaviors modify how data is generated or returned, enabling simulation
10
+ # of real-world conditions like network latency, failures, and partial data.
11
+ #
12
+ # Built-in behaviors:
13
+ # - latency: Adds delays to simulate slow responses
14
+ # - failure: Raises errors to simulate service failures
15
+ # - partial_data: Omits fields to simulate incomplete data
16
+ # - close_connection: Simulates connection drops
17
+ # - simulate_error: Simulates specific HTTP errors
18
+ # - randomize_order: Shuffles array elements
19
+ #
20
+ # @example Creating a custom behavior
21
+ # class MyBehavior < Synthra::Behaviors::Base
22
+ # def apply(result, context = nil)
23
+ # return result unless should_apply?
24
+ # # Modify result here
25
+ # result.merge("modified" => true)
26
+ # end
27
+ # end
28
+ #
29
+ # =============================================================================
30
+
31
+
32
+ module Synthra
33
+ module Behaviors
34
+
35
+ # Base class for all behavior implementations
36
+ #
37
+ # All behaviors inherit from Base and implement the #apply method.
38
+ # The base class provides probability-based activation through
39
+ # #should_apply? method.
40
+ #
41
+ # @abstract Subclass and implement {#apply}
42
+ #
43
+ # @example Implement a simple behavior
44
+ # class Logging < Base
45
+ # def apply(result, context = nil)
46
+ # return result unless should_apply?
47
+ # puts "Generated: #{result.inspect}"
48
+ # result
49
+ # end
50
+ # end
51
+ #
52
+
53
+ class Base
54
+
55
+ # @!attribute [r] value
56
+ # The behavior configuration value
57
+ # @return [Object] configuration (probability, range, etc.)
58
+
59
+ attr_reader :value
60
+
61
+
62
+ # @!attribute [r] rng
63
+ # Random number generator for deterministic probability checks
64
+ # @return [Generator::RNG, nil] RNG instance
65
+
66
+ attr_reader :rng
67
+
68
+
69
+ # Create a new behavior instance
70
+ #
71
+ # @param value [Object] behavior configuration (probability, range, etc.)
72
+ # @param rng [Generator::RNG, nil] random number generator for determinism
73
+ #
74
+ # @example Create with probability
75
+ # Latency.new(100..500, rng)
76
+ # Failure.new(10, rng) # 10% probability
77
+ #
78
+
79
+ def initialize(value, rng = nil)
80
+ @value = value
81
+ @rng = rng
82
+ end
83
+
84
+
85
+ # Apply the behavior to generated data
86
+ #
87
+ # This is the main method that behaviors implement to modify
88
+ # generated data or produce side effects.
89
+ #
90
+ # @abstract Must be implemented by subclasses
91
+ #
92
+ # @param result [Object] the generated data to modify
93
+ # @param context [Generator::Context, nil] generation context
94
+ # @return [Object] the modified result
95
+ # @raise [NotImplementedError] if not overridden
96
+ #
97
+ # @example Implementation
98
+ # def apply(result, context = nil)
99
+ # return result unless should_apply?
100
+ # # ... modify result ...
101
+ # result
102
+ # end
103
+ #
104
+
105
+ def apply(result, context = nil)
106
+ raise NotImplementedError, "#{self.class} must implement #apply"
107
+ end
108
+
109
+
110
+ # Check if this behavior should be applied
111
+ #
112
+ # Uses probability-based activation. If value is an Integer,
113
+ # it's treated as a percentage probability. If nil or >= 100,
114
+ # always applies.
115
+ #
116
+ # @return [Boolean] true if the behavior should be applied
117
+ #
118
+ # @example Probability check
119
+ # behavior = Failure.new(10, rng) # 10% probability
120
+ # behavior.should_apply? # => true (10% of the time)
121
+ #
122
+
123
+ def should_apply?
124
+ probability = extract_probability
125
+
126
+ # Always apply if no probability specified or 100%+
127
+ return true if probability.nil? || probability >= 100
128
+
129
+
130
+ # Use RNG for deterministic tests, or rand() otherwise
131
+ if rng
132
+ rng.rand(100) < probability
133
+ else
134
+ rand(100) < probability
135
+ end
136
+ end
137
+
138
+ private
139
+
140
+
141
+ # Extract probability from the value configuration
142
+ #
143
+ # @return [Integer, nil] probability percentage (0-100) or nil
144
+ #
145
+
146
+ def extract_probability
147
+ case value
148
+ when Integer then value
149
+ when Hash then value[:probability] || value[:percent]
150
+ else nil
151
+ end
152
+ end
153
+ end
154
+ end
155
+ end
@@ -0,0 +1,60 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Close Connection Behavior
6
+ # =============================================================================
7
+ #
8
+ # Simulates connection drops by raising SimulatedConnectionDrop exception.
9
+ # Useful for testing error handling and retry logic.
10
+ #
11
+ # @example DSL usage
12
+ # User:
13
+ # @close_connection 5% # 5% chance of connection drop
14
+ # name: name
15
+ # email: email
16
+ #
17
+ # =============================================================================
18
+
19
+
20
+ module Synthra
21
+ module Behaviors
22
+
23
+ # Close connection behavior - simulates connection drop
24
+ #
25
+ # Raises SimulatedConnectionDrop exception when activated, simulating
26
+ # a network connection failure.
27
+ #
28
+ # @example Apply close connection behavior
29
+ # behavior = CloseConnection.new(5, rng) # 5% probability
30
+ # begin
31
+ # behavior.apply({ "name" => "John" })
32
+ # rescue SimulatedConnectionDrop => e
33
+ # # Handle connection drop
34
+ # end
35
+ #
36
+
37
+ class CloseConnection < Base
38
+
39
+ # Apply close connection behavior
40
+ #
41
+ # Raises SimulatedConnectionDrop if the behavior should be applied
42
+ # based 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 [SimulatedConnectionDrop] if behavior activates
48
+ #
49
+
50
+ def apply(result, context = nil)
51
+ if should_apply?
52
+ probability = extract_probability
53
+ raise SimulatedConnectionDrop.new(probability: probability)
54
+ end
55
+ result
56
+ end
57
+ end
58
+ end
59
+ end
60
+
@@ -0,0 +1,100 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Deprecated Behavior
6
+ # =============================================================================
7
+ #
8
+ # The Deprecated behavior emits warnings when a schema or field is generated.
9
+ # This helps teams track usage of deprecated schemas and plan migrations.
10
+ #
11
+ # @example Schema-level deprecation
12
+ # LegacyUser:
13
+ # @deprecated "Use User schema instead (v2.0)"
14
+ # id: uuid
15
+ # name: text
16
+ #
17
+ # @example Field-level deprecation (in behaviors)
18
+ # User:
19
+ # @deprecated "This schema will be removed in v3.0"
20
+ # old_id: id_sequence
21
+ # id: uuid
22
+ #
23
+ # =============================================================================
24
+
25
+
26
+ # :nocov:
27
+ module Synthra
28
+ module Behaviors
29
+
30
+ # Emits deprecation warnings during data generation
31
+ #
32
+ # The Deprecated behavior is used to mark schemas as deprecated.
33
+ # When data is generated from a deprecated schema, a warning is
34
+ # logged/printed to alert developers.
35
+ #
36
+ # @example DSL usage
37
+ # OldSchema:
38
+ # @deprecated "Use NewSchema instead"
39
+ # field: text
40
+ #
41
+ # @example Programmatic check
42
+ # behavior = Deprecated.new("Use NewSchema instead")
43
+ # behavior.apply(result, context) # Prints warning
44
+ #
45
+
46
+ class Deprecated < Base
47
+
48
+ # Apply the deprecation warning
49
+ #
50
+ # Emits a warning message about the deprecated schema.
51
+ # The warning is sent to Synthra.logger if available,
52
+ # otherwise to $stderr.
53
+ #
54
+ # @param result [Object] the generated data (passed through unchanged)
55
+ # @param context [Generator::Context, nil] generation context
56
+ # @return [Object] the unchanged result
57
+ #
58
+
59
+ def apply(result, context = nil)
60
+ emit_warning(context)
61
+ result
62
+ end
63
+
64
+ private
65
+
66
+
67
+ # Emit the deprecation warning
68
+ #
69
+ # @param context [Generator::Context, nil] generation context
70
+ # @return [void]
71
+ #
72
+
73
+ def emit_warning(context)
74
+ schema_name = context&.instance_variable_get(:@schema_name) || "Unknown"
75
+ message = build_warning_message(schema_name)
76
+
77
+ if Synthra.configuration.logger
78
+ Synthra.configuration.logger.warn(message)
79
+ else
80
+ warn "[Synthra] #{message}"
81
+ end
82
+ end
83
+
84
+
85
+ # Build the warning message
86
+ #
87
+ # @param schema_name [String] name of the deprecated schema
88
+ # @return [String] formatted warning message
89
+ #
90
+
91
+ def build_warning_message(schema_name)
92
+ deprecation_note = value.is_a?(String) ? value : "This schema is deprecated"
93
+
94
+ "DEPRECATED: Schema '#{schema_name}' - #{deprecation_note}"
95
+ end
96
+ end
97
+ end
98
+ end
99
+ # :nocov:
100
+
@@ -0,0 +1,69 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Failure Behavior
6
+ # =============================================================================
7
+ #
8
+ # Simulates service failures by raising SimulatedFailure errors.
9
+ # Useful for testing error handling and retry logic in applications.
10
+ #
11
+ # @example DSL usage
12
+ # API:
13
+ # @failure 10% # 10% chance of failure
14
+ # @failure 5 # Same as 5%
15
+ # id: uuid
16
+ #
17
+ # @example Testing error handling
18
+ # begin
19
+ # api_schema.generate
20
+ # rescue Synthra::SimulatedFailure => e
21
+ # # Test that your app handles failures correctly
22
+ # end
23
+ #
24
+ # =============================================================================
25
+
26
+
27
+ module Synthra
28
+ module Behaviors
29
+
30
+ # Failure behavior - simulates service failure
31
+ #
32
+ # Raises a SimulatedFailure error based on configured probability.
33
+ # This is useful for testing how applications handle service
34
+ # unavailability, timeouts, and other failure scenarios.
35
+ #
36
+ # @example Create with probability
37
+ # failure = Failure.new(10, rng) # 10% failure rate
38
+ # failure.apply(result) # May raise SimulatedFailure
39
+ #
40
+
41
+ class Failure < Base
42
+
43
+ # Apply the failure behavior
44
+ #
45
+ # Based on probability, either raises SimulatedFailure or
46
+ # returns the result unchanged.
47
+ #
48
+ # @param result [Object] the generated data
49
+ # @param context [Generator::Context, nil] generation context (not used)
50
+ # @return [Object] the result if no failure
51
+ # @raise [SimulatedFailure] if failure is triggered
52
+ #
53
+ # @example
54
+ # failure = Failure.new(10, rng) # 10% failure rate
55
+ # failure.apply({ "id" => "123" })
56
+ # # Returns result 90% of the time
57
+ # # Raises SimulatedFailure 10% of the time
58
+ #
59
+
60
+ def apply(result, context = nil)
61
+ if should_apply?
62
+ probability = extract_probability
63
+ raise SimulatedFailure.new(probability: probability)
64
+ end
65
+ result
66
+ end
67
+ end
68
+ end
69
+ end
@@ -0,0 +1,103 @@
1
+ # frozen_string_literal: true
2
+
3
+ # =============================================================================
4
+
5
+ # Synthra Latency Behavior
6
+ # =============================================================================
7
+ #
8
+ # Simulates network latency by adding delays to data generation.
9
+ # Useful for testing how applications handle slow responses.
10
+ #
11
+ # @example DSL usage
12
+ # User:
13
+ # @latency 500 # Fixed 500ms delay
14
+ # @latency 100..500ms # Random delay between 100-500ms
15
+ # @latency min: 100, max: 500 # Same as above with named args
16
+ #
17
+ # =============================================================================
18
+
19
+
20
+ module Synthra
21
+ module Behaviors
22
+
23
+ # Latency behavior - adds configurable delay to data generation
24
+ #
25
+ # Simulates network latency by sleeping for a specified duration.
26
+ # The delay can be fixed or randomized within a range.
27
+ #
28
+ # @example Fixed delay
29
+ # latency = Latency.new(500, rng)
30
+ # latency.apply(result) # Sleeps for 500ms
31
+ #
32
+ # @example Random delay
33
+ # latency = Latency.new(100..500, rng)
34
+ # latency.apply(result) # Sleeps for random 100-500ms
35
+ #
36
+
37
+ class Latency < Base
38
+
39
+ # Apply latency to the result
40
+ #
41
+ # Calculates the delay and sleeps for that duration.
42
+ # Validates against configured maximum latency limit.
43
+ #
44
+ # @param result [Object] the generated data (passed through unchanged)
45
+ # @param context [Generator::Context, nil] generation context (not used)
46
+ # @return [Object] the result unchanged
47
+ # @raise [LatencyLimitError] if delay exceeds max_latency_ms limit
48
+ #
49
+ # @example
50
+ # latency = Latency.new(500, rng)
51
+ # result = latency.apply({ "id" => "123" })
52
+ # # Sleeps for 500ms, then returns result
53
+ #
54
+
55
+ def apply(result, context = nil)
56
+ return result unless should_apply?
57
+
58
+ delay = calculate_delay
59
+
60
+ # Validate against configured limits
61
+ Synthra.configuration.limits.validate_latency!(delay)
62
+
63
+ # Sleep for delay (convert ms to seconds)
64
+ sleep(delay / 1000.0)
65
+ result
66
+ end
67
+
68
+ private
69
+
70
+
71
+ # Calculate the delay in milliseconds
72
+ #
73
+ # Supports multiple value formats:
74
+ # - Range: random value within range
75
+ # - Hash: { min: X, max: Y } for range
76
+ # - Integer: fixed delay
77
+ #
78
+ # @return [Integer] delay in milliseconds
79
+ #
80
+
81
+ def calculate_delay
82
+ case value
83
+ when Range
84
+
85
+ # Random delay within range
86
+ rng ? rng.rand(value) : rand(value)
87
+ when Hash
88
+
89
+ # Named min/max arguments
90
+ min = value[:min] || 0
91
+ max = value[:max] || min
92
+ rng ? rng.int(min, max) : rand(min..max)
93
+ when Integer
94
+
95
+ # Fixed delay
96
+ value
97
+ else
98
+ 0
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end