sqa 0.0.24 → 0.0.31

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 (180) hide show
  1. checksums.yaml +4 -4
  2. data/.goose/memory/development.txt +3 -0
  3. data/.semver +6 -0
  4. data/ARCHITECTURE.md +648 -0
  5. data/CHANGELOG.md +82 -0
  6. data/CLAUDE.md +653 -0
  7. data/COMMITS.md +196 -0
  8. data/DATAFRAME_ARCHITECTURE_REVIEW.md +421 -0
  9. data/NEXT-STEPS.md +154 -0
  10. data/README.md +812 -262
  11. data/TASKS.md +358 -0
  12. data/TEST_RESULTS.md +140 -0
  13. data/TODO.md +42 -0
  14. data/_notes.txt +25 -0
  15. data/bin/sqa-console +11 -0
  16. data/data/talk_talk.json +103284 -0
  17. data/develop_summary.md +313 -0
  18. data/docs/advanced/backtesting.md +206 -0
  19. data/docs/advanced/ensemble.md +68 -0
  20. data/docs/advanced/fpop.md +153 -0
  21. data/docs/advanced/index.md +112 -0
  22. data/docs/advanced/multi-timeframe.md +67 -0
  23. data/docs/advanced/pattern-matcher.md +75 -0
  24. data/docs/advanced/portfolio-optimizer.md +79 -0
  25. data/docs/advanced/portfolio.md +166 -0
  26. data/docs/advanced/risk-management.md +210 -0
  27. data/docs/advanced/strategy-generator.md +158 -0
  28. data/docs/advanced/streaming.md +209 -0
  29. data/docs/ai_and_ml.md +80 -0
  30. data/docs/api/dataframe.md +1115 -0
  31. data/docs/api/index.md +126 -0
  32. data/docs/assets/css/custom.css +88 -0
  33. data/docs/assets/js/mathjax.js +18 -0
  34. data/docs/concepts/index.md +68 -0
  35. data/docs/contributing/index.md +60 -0
  36. data/docs/data-sources/index.md +66 -0
  37. data/docs/data_frame.md +317 -97
  38. data/docs/factors_that_impact_price.md +26 -0
  39. data/docs/finviz.md +11 -0
  40. data/docs/fx_pro_bit.md +25 -0
  41. data/docs/genetic_programming.md +104 -0
  42. data/docs/getting-started/index.md +123 -0
  43. data/docs/getting-started/installation.md +229 -0
  44. data/docs/getting-started/quick-start.md +244 -0
  45. data/docs/i_gotta_an_idea.md +22 -0
  46. data/docs/index.md +163 -0
  47. data/docs/indicators/index.md +97 -0
  48. data/docs/indicators.md +110 -24
  49. data/docs/options.md +8 -0
  50. data/docs/strategies/bollinger-bands.md +146 -0
  51. data/docs/strategies/consensus.md +64 -0
  52. data/docs/strategies/custom.md +310 -0
  53. data/docs/strategies/ema.md +53 -0
  54. data/docs/strategies/index.md +92 -0
  55. data/docs/strategies/kbs.md +164 -0
  56. data/docs/strategies/macd.md +96 -0
  57. data/docs/strategies/market-profile.md +54 -0
  58. data/docs/strategies/mean-reversion.md +58 -0
  59. data/docs/strategies/rsi.md +95 -0
  60. data/docs/strategies/sma.md +55 -0
  61. data/docs/strategies/stochastic.md +63 -0
  62. data/docs/strategies/volume-breakout.md +54 -0
  63. data/docs/tags.md +7 -0
  64. data/docs/true_strength_index.md +46 -0
  65. data/docs/weighted_moving_average.md +48 -0
  66. data/examples/README.md +354 -0
  67. data/examples/advanced_features_example.rb +350 -0
  68. data/examples/fpop_analysis_example.rb +191 -0
  69. data/examples/genetic_programming_example.rb +148 -0
  70. data/examples/kbs_strategy_example.rb +208 -0
  71. data/examples/pattern_context_example.rb +300 -0
  72. data/examples/rails_app/Gemfile +34 -0
  73. data/examples/rails_app/README.md +416 -0
  74. data/examples/rails_app/app/assets/javascripts/application.js +107 -0
  75. data/examples/rails_app/app/assets/stylesheets/application.css +659 -0
  76. data/examples/rails_app/app/controllers/analysis_controller.rb +11 -0
  77. data/examples/rails_app/app/controllers/api/v1/stocks_controller.rb +227 -0
  78. data/examples/rails_app/app/controllers/application_controller.rb +22 -0
  79. data/examples/rails_app/app/controllers/backtest_controller.rb +11 -0
  80. data/examples/rails_app/app/controllers/dashboard_controller.rb +21 -0
  81. data/examples/rails_app/app/controllers/portfolio_controller.rb +7 -0
  82. data/examples/rails_app/app/views/analysis/show.html.erb +209 -0
  83. data/examples/rails_app/app/views/backtest/show.html.erb +171 -0
  84. data/examples/rails_app/app/views/dashboard/index.html.erb +118 -0
  85. data/examples/rails_app/app/views/dashboard/show.html.erb +408 -0
  86. data/examples/rails_app/app/views/errors/show.html.erb +17 -0
  87. data/examples/rails_app/app/views/layouts/application.html.erb +60 -0
  88. data/examples/rails_app/app/views/portfolio/index.html.erb +33 -0
  89. data/examples/rails_app/bin/rails +6 -0
  90. data/examples/rails_app/config/application.rb +45 -0
  91. data/examples/rails_app/config/boot.rb +5 -0
  92. data/examples/rails_app/config/database.yml +18 -0
  93. data/examples/rails_app/config/environment.rb +11 -0
  94. data/examples/rails_app/config/routes.rb +26 -0
  95. data/examples/rails_app/config.ru +8 -0
  96. data/examples/realtime_stream_example.rb +274 -0
  97. data/examples/sinatra_app/Gemfile +22 -0
  98. data/examples/sinatra_app/QUICKSTART.md +159 -0
  99. data/examples/sinatra_app/README.md +461 -0
  100. data/examples/sinatra_app/app.rb +344 -0
  101. data/examples/sinatra_app/config.ru +5 -0
  102. data/examples/sinatra_app/public/css/style.css +659 -0
  103. data/examples/sinatra_app/public/js/app.js +107 -0
  104. data/examples/sinatra_app/views/analyze.erb +306 -0
  105. data/examples/sinatra_app/views/backtest.erb +325 -0
  106. data/examples/sinatra_app/views/dashboard.erb +419 -0
  107. data/examples/sinatra_app/views/error.erb +58 -0
  108. data/examples/sinatra_app/views/index.erb +118 -0
  109. data/examples/sinatra_app/views/layout.erb +61 -0
  110. data/examples/sinatra_app/views/portfolio.erb +43 -0
  111. data/examples/strategy_generator_example.rb +346 -0
  112. data/hsa_portfolio.csv +11 -0
  113. data/justfile +0 -0
  114. data/lib/api/alpha_vantage_api.rb +462 -0
  115. data/lib/sqa/backtest.rb +329 -0
  116. data/lib/sqa/data_frame/alpha_vantage.rb +43 -65
  117. data/lib/sqa/data_frame/data.rb +92 -0
  118. data/lib/sqa/data_frame/yahoo_finance.rb +35 -43
  119. data/lib/sqa/data_frame.rb +148 -243
  120. data/lib/sqa/ensemble.rb +359 -0
  121. data/lib/sqa/fpop.rb +199 -0
  122. data/lib/sqa/gp.rb +259 -0
  123. data/lib/sqa/indicator.rb +5 -8
  124. data/lib/sqa/init.rb +15 -8
  125. data/lib/sqa/market_regime.rb +240 -0
  126. data/lib/sqa/multi_timeframe.rb +379 -0
  127. data/lib/sqa/pattern_matcher.rb +497 -0
  128. data/lib/sqa/portfolio.rb +260 -6
  129. data/lib/sqa/portfolio_optimizer.rb +377 -0
  130. data/lib/sqa/risk_manager.rb +442 -0
  131. data/lib/sqa/seasonal_analyzer.rb +209 -0
  132. data/lib/sqa/sector_analyzer.rb +300 -0
  133. data/lib/sqa/stock.rb +67 -125
  134. data/lib/sqa/strategy/bollinger_bands.rb +42 -0
  135. data/lib/sqa/strategy/consensus.rb +5 -2
  136. data/lib/sqa/strategy/kbs_strategy.rb +470 -0
  137. data/lib/sqa/strategy/macd.rb +46 -0
  138. data/lib/sqa/strategy/mp.rb +1 -1
  139. data/lib/sqa/strategy/stochastic.rb +60 -0
  140. data/lib/sqa/strategy/volume_breakout.rb +57 -0
  141. data/lib/sqa/strategy.rb +5 -0
  142. data/lib/sqa/strategy_generator.rb +947 -0
  143. data/lib/sqa/stream.rb +361 -0
  144. data/lib/sqa/version.rb +1 -7
  145. data/lib/sqa.rb +23 -16
  146. data/main.just +81 -0
  147. data/mkdocs.yml +288 -0
  148. data/trace.log +0 -0
  149. metadata +261 -51
  150. data/bin/sqa +0 -6
  151. data/lib/patches/dry-cli.rb +0 -228
  152. data/lib/sqa/activity.rb +0 -10
  153. data/lib/sqa/cli.rb +0 -62
  154. data/lib/sqa/commands/analysis.rb +0 -309
  155. data/lib/sqa/commands/base.rb +0 -139
  156. data/lib/sqa/commands/web.rb +0 -199
  157. data/lib/sqa/commands.rb +0 -22
  158. data/lib/sqa/constants.rb +0 -23
  159. data/lib/sqa/indicator/average_true_range.rb +0 -33
  160. data/lib/sqa/indicator/bollinger_bands.rb +0 -28
  161. data/lib/sqa/indicator/candlestick_pattern_recognizer.rb +0 -60
  162. data/lib/sqa/indicator/donchian_channel.rb +0 -29
  163. data/lib/sqa/indicator/double_top_bottom_pattern.rb +0 -34
  164. data/lib/sqa/indicator/elliott_wave_theory.rb +0 -57
  165. data/lib/sqa/indicator/exponential_moving_average.rb +0 -25
  166. data/lib/sqa/indicator/exponential_moving_average_trend.rb +0 -36
  167. data/lib/sqa/indicator/fibonacci_retracement.rb +0 -23
  168. data/lib/sqa/indicator/head_and_shoulders_pattern.rb +0 -26
  169. data/lib/sqa/indicator/market_profile.rb +0 -32
  170. data/lib/sqa/indicator/mean_reversion.rb +0 -37
  171. data/lib/sqa/indicator/momentum.rb +0 -28
  172. data/lib/sqa/indicator/moving_average_convergence_divergence.rb +0 -29
  173. data/lib/sqa/indicator/peaks_and_valleys.rb +0 -29
  174. data/lib/sqa/indicator/predict_next_value.rb +0 -202
  175. data/lib/sqa/indicator/relative_strength_index.rb +0 -47
  176. data/lib/sqa/indicator/simple_moving_average.rb +0 -24
  177. data/lib/sqa/indicator/simple_moving_average_trend.rb +0 -32
  178. data/lib/sqa/indicator/stochastic_oscillator.rb +0 -68
  179. data/lib/sqa/indicator/true_range.rb +0 -39
  180. data/lib/sqa/trade.rb +0 -26
@@ -0,0 +1,300 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ # Example: Context-Aware Pattern Discovery with KBS
5
+ #
6
+ # This example demonstrates the full Pattern Context system:
7
+ # 1. Market regime detection (bull/bear/sideways)
8
+ # 2. Seasonal pattern analysis (monthly/quarterly)
9
+ # 3. Sector analysis with KBS blackboards
10
+ # 4. Context-aware pattern discovery
11
+ # 5. Walk-forward validation to prevent overfitting
12
+ #
13
+ # Key Insight: Patterns aren't universal. They work in specific contexts:
14
+ # - Market regimes (bull vs bear)
15
+ # - Seasons (Q4 holiday shopping)
16
+ # - Sectors (tech stocks move together)
17
+ #
18
+ # This system helps identify WHEN and WHERE patterns are valid.
19
+
20
+ require 'sqa'
21
+
22
+ SQA.init
23
+
24
+ puts "=" * 80
25
+ puts "CONTEXT-AWARE PATTERN DISCOVERY SYSTEM"
26
+ puts "=" * 80
27
+ puts
28
+
29
+ # Only run integration tests if explicitly requested
30
+ if ENV['RUN_INTEGRATION_TESTS']
31
+
32
+ # Example 1: Market Regime Detection
33
+ puts "\n" + "=" * 80
34
+ puts "Example 1: Market Regime Detection"
35
+ puts "=" * 80
36
+ puts
37
+
38
+ stock = SQA::Stock.new(ticker: 'AAPL')
39
+ puts "Loaded #{stock.df.size} days of AAPL history"
40
+ puts
41
+
42
+ # Detect current regime
43
+ regime = SQA::MarketRegime.detect(stock)
44
+ puts "Current Market Regime:"
45
+ puts " Type: #{regime[:type]}"
46
+ puts " Volatility: #{regime[:volatility]}"
47
+ puts " Strength: #{regime[:strength]}"
48
+ puts
49
+
50
+ # Analyze regime history
51
+ regime_history = SQA::MarketRegime.detect_history(stock)
52
+ puts "Regime History (#{regime_history.size} distinct periods):"
53
+ regime_history.last(5).each do |r|
54
+ puts " #{r[:type].to_s.upcase.ljust(10)} - #{r[:duration]} days"
55
+ end
56
+ puts
57
+
58
+ # Example 2: Seasonal Pattern Analysis
59
+ puts "\n" + "=" * 80
60
+ puts "Example 2: Seasonal Pattern Analysis"
61
+ puts "=" * 80
62
+ puts
63
+
64
+ seasonal = SQA::SeasonalAnalyzer.analyze(stock)
65
+ puts "Seasonal Performance:"
66
+ puts " Best performing months: #{seasonal[:best_months].map { |m| Date::MONTHNAMES[m] }.join(', ')}"
67
+ puts " Worst performing months: #{seasonal[:worst_months].map { |m| Date::MONTHNAMES[m] }.join(', ')}"
68
+ puts " Best quarters: Q#{seasonal[:best_quarters].join(', Q')}"
69
+ puts " Has seasonal pattern: #{seasonal[:has_seasonal_pattern] ? 'YES' : 'NO'}"
70
+ puts
71
+
72
+ # Show monthly returns
73
+ puts "Monthly Average Returns:"
74
+ seasonal[:monthly_returns].sort_by { |m, _| m }.each do |month, stats|
75
+ month_name = Date::MONTHNAMES[month].ljust(10)
76
+ avg_return = stats[:avg_return].round(2)
77
+ sign = avg_return >= 0 ? '+' : ''
78
+ puts " #{month_name}: #{sign}#{avg_return}% (#{stats[:count]} samples)"
79
+ end
80
+ puts
81
+
82
+ # Example 3: Sector Analysis with KBS Blackboards
83
+ puts "\n" + "=" * 80
84
+ puts "Example 3: Sector Analysis with KBS Blackboards"
85
+ puts "=" * 80
86
+ puts
87
+
88
+ analyzer = SQA::SectorAnalyzer.new
89
+
90
+ # Add technology stocks
91
+ tech_stocks = ['AAPL', 'MSFT', 'GOOGL'].map { |t| SQA::Stock.new(ticker: t) }
92
+ tech_stocks.each { |s| analyzer.add_stock(s, sector: :technology) }
93
+
94
+ puts "Analyzing Technology sector (#{tech_stocks.size} stocks)"
95
+ puts
96
+
97
+ # Detect sector regime
98
+ sector_regime = analyzer.detect_sector_regime(:technology, tech_stocks)
99
+ puts "Technology Sector Regime:"
100
+ puts " Consensus: #{sector_regime[:consensus_regime]}"
101
+ puts " Sector strength: #{sector_regime[:sector_strength]}% bullish"
102
+ puts
103
+
104
+ # Discover sector-wide patterns
105
+ puts "Discovering sector-wide patterns..."
106
+ sector_patterns = analyzer.discover_sector_patterns(
107
+ :technology,
108
+ tech_stocks,
109
+ min_gain_percent: 8.0,
110
+ fpop: 10,
111
+ max_fpl_risk: 20.0
112
+ )
113
+
114
+ if sector_patterns.any?
115
+ puts "\nSector-Wide Patterns Found: #{sector_patterns.size}"
116
+ sector_patterns.first(3).each_with_index do |sp, i|
117
+ puts "\n Pattern #{i + 1}:"
118
+ puts " Stocks: #{sp[:stocks].join(', ')}"
119
+ puts " Avg Frequency: #{sp[:avg_frequency].round(1)}"
120
+ puts " Avg Gain: #{sp[:avg_gain].round(2)}%"
121
+ puts " Conditions: #{sp[:conditions]}"
122
+ end
123
+ end
124
+ puts
125
+
126
+ # Print sector summary
127
+ analyzer.print_sector_summary(:technology)
128
+
129
+ # Example 4: Context-Aware Pattern Discovery
130
+ puts "\n" + "=" * 80
131
+ puts "Example 4: Context-Aware Pattern Discovery"
132
+ puts "=" * 80
133
+ puts
134
+
135
+ generator = SQA::StrategyGenerator.new(
136
+ stock: stock,
137
+ min_gain_percent: 10.0,
138
+ fpop: 10,
139
+ max_fpl_risk: 20.0,
140
+ required_fpl_directions: [:UP]
141
+ )
142
+
143
+ # Discover patterns with full context
144
+ context_patterns = generator.discover_context_aware_patterns(
145
+ analyze_regime: true,
146
+ analyze_seasonal: true,
147
+ sector: :technology
148
+ )
149
+
150
+ puts "\nDiscovered Context-Aware Patterns:"
151
+ context_patterns.first(3).each_with_index do |pattern, i|
152
+ puts "\n Pattern #{i + 1}:"
153
+ puts " Frequency: #{pattern.frequency}"
154
+ puts " Avg Gain: #{pattern.avg_gain.round(2)}%"
155
+ puts " Context: #{pattern.context.summary}"
156
+ puts " Market Regime: #{pattern.context.market_regime}"
157
+ puts " Valid Months: #{pattern.context.valid_months.map { |m| Date::MONTHNAMES[m] }.join(', ')}" if pattern.context.valid_months.any?
158
+ puts " Sector: #{pattern.context.sector}"
159
+ puts " Conditions: #{pattern.conditions}"
160
+ end
161
+ puts
162
+
163
+ # Example 5: Walk-Forward Validation
164
+ puts "\n" + "=" * 80
165
+ puts "Example 5: Walk-Forward Validation (Prevent Overfitting)"
166
+ puts "=" * 80
167
+ puts
168
+
169
+ puts "Running time-series cross-validation..."
170
+ puts "(This validates patterns on out-of-sample data)"
171
+ puts
172
+
173
+ validation_results = generator.walk_forward_validate(
174
+ train_size: 250, # 1 year training
175
+ test_size: 60, # ~3 months testing
176
+ step_size: 30 # Step forward 1 month each iteration
177
+ )
178
+
179
+ validated_patterns = validation_results[:validated_patterns]
180
+ all_results = validation_results[:validation_results]
181
+
182
+ puts "\nValidation Summary:"
183
+ puts " Total patterns tested: #{all_results.size}"
184
+ puts " Patterns validated (positive return, Sharpe > 0.5): #{validated_patterns.size}"
185
+ puts
186
+
187
+ if all_results.any?
188
+ # Calculate statistics
189
+ returns = all_results.map { |r| r[:test_return] }
190
+ sharpes = all_results.map { |r| r[:test_sharpe] }.compact
191
+ drawdowns = all_results.map { |r| r[:test_max_drawdown] }.compact
192
+
193
+ avg_return = returns.sum / returns.size
194
+ avg_sharpe = sharpes.any? ? sharpes.sum / sharpes.size : 0
195
+ avg_drawdown = drawdowns.any? ? drawdowns.sum / drawdowns.size : 0
196
+
197
+ puts "Out-of-Sample Performance:"
198
+ puts " Avg Return: #{avg_return.round(2)}%"
199
+ puts " Avg Sharpe: #{avg_sharpe.round(2)}"
200
+ puts " Avg Max Drawdown: #{avg_drawdown.round(2)}%"
201
+ puts
202
+
203
+ # Show best validated patterns
204
+ if validated_patterns.any?
205
+ puts "Best Validated Patterns (worked out-of-sample):"
206
+ validated_patterns.uniq.first(3).each_with_index do |pattern, i|
207
+ puts "\n Pattern #{i + 1}:"
208
+ puts " Frequency: #{pattern.frequency}"
209
+ puts " Avg Gain (in-sample): #{pattern.avg_gain.round(2)}%"
210
+ puts " Conditions: #{pattern.conditions}"
211
+ end
212
+ else
213
+ puts "⚠️ No patterns passed validation!"
214
+ puts "This suggests discovered patterns may be overfit to historical data."
215
+ puts "Try:"
216
+ puts " - Adjusting thresholds"
217
+ puts " - Increasing min_pattern_frequency"
218
+ puts " - Using context filters (regime, seasonal)"
219
+ end
220
+ end
221
+ puts
222
+
223
+ # Example 6: Pattern Validity Checking
224
+ puts "\n" + "=" * 80
225
+ puts "Example 6: Runtime Pattern Validity Checking"
226
+ puts "=" * 80
227
+ puts
228
+
229
+ if context_patterns.any?
230
+ pattern = context_patterns.first
231
+
232
+ puts "Checking if pattern is valid for different scenarios:"
233
+ puts
234
+
235
+ # Scenario 1: Current date in valid month
236
+ test_date = Date.new(2024, 12, 15) # December
237
+ valid = pattern.context.valid_for?(
238
+ date: test_date,
239
+ regime: :bull,
240
+ sector: :technology
241
+ )
242
+ puts " December 15, 2024 (Bull market, Tech sector): #{valid ? '✓ VALID' : '✗ INVALID'}"
243
+
244
+ # Scenario 2: Wrong month
245
+ test_date = Date.new(2024, 6, 15) # June
246
+ valid = pattern.context.valid_for?(
247
+ date: test_date,
248
+ regime: :bull,
249
+ sector: :technology
250
+ )
251
+ puts " June 15, 2024 (Bull market, Tech sector): #{valid ? '✓ VALID' : '✗ INVALID'}"
252
+
253
+ # Scenario 3: Wrong regime
254
+ valid = pattern.context.valid_for?(
255
+ date: Date.new(2024, 12, 15),
256
+ regime: :bear,
257
+ sector: :technology
258
+ )
259
+ puts " December 15, 2024 (Bear market, Tech sector): #{valid ? '✓ VALID' : '✗ INVALID'}"
260
+
261
+ # Scenario 4: Wrong sector
262
+ valid = pattern.context.valid_for?(
263
+ date: Date.new(2024, 12, 15),
264
+ regime: :bull,
265
+ sector: :finance
266
+ )
267
+ puts " December 15, 2024 (Bull market, Finance sector): #{valid ? '✓ VALID' : '✗ INVALID'}"
268
+ end
269
+ puts
270
+
271
+ puts "=" * 80
272
+ puts "COMPLETE PATTERN CONTEXT SYSTEM DEMONSTRATION FINISHED"
273
+ puts "=" * 80
274
+ puts
275
+
276
+ puts "Key Takeaways:"
277
+ puts " ✓ Patterns have context (regime, season, sector)"
278
+ puts " ✓ Market regimes change (bull/bear/sideways)"
279
+ puts " ✓ Seasonal patterns exist (best months/quarters)"
280
+ puts " ✓ Sector analysis finds patterns across related stocks"
281
+ puts " ✓ Walk-forward validation prevents overfitting"
282
+ puts " ✓ Runtime validation checks if pattern applies NOW"
283
+ puts
284
+ puts "This context-aware approach is much more sophisticated than"
285
+ puts "naive pattern mining that assumes universal applicability."
286
+ puts
287
+
288
+ else
289
+ puts "Skipping integration tests (set RUN_INTEGRATION_TESTS=1 to enable)"
290
+ puts
291
+ puts "This example demonstrates:"
292
+ puts " 1. Market Regime Detection - Identify bull/bear/sideways markets"
293
+ puts " 2. Seasonal Analysis - Find which months/quarters patterns work"
294
+ puts " 3. Sector Analysis - Discover patterns across related stocks using KBS"
295
+ puts " 4. Context-Aware Discovery - Patterns with regime/seasonal metadata"
296
+ puts " 5. Walk-Forward Validation - Prevent overfitting with time-series CV"
297
+ puts " 6. Runtime Validity - Check if pattern applies in current conditions"
298
+ puts
299
+ puts "Run with: RUN_INTEGRATION_TESTS=1 ruby #{__FILE__}"
300
+ end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ ruby '>= 3.2.0'
6
+
7
+ # Rails framework
8
+ gem 'rails', '~> 7.1'
9
+
10
+ # Database - use SQLite for simplicity
11
+ gem 'sqlite3', '~> 1.7'
12
+
13
+ # Web server
14
+ gem 'puma', '~> 6.0'
15
+
16
+ # Asset pipeline
17
+ gem 'sprockets-rails'
18
+ gem 'importmap-rails'
19
+ gem 'turbo-rails'
20
+ gem 'stimulus-rails'
21
+
22
+ # JSON
23
+ gem 'jbuilder'
24
+
25
+ # SQA library (load from parent directory)
26
+ # In production: gem 'sqa'
27
+
28
+ group :development, :test do
29
+ gem 'debug', platforms: %i[mri windows]
30
+ end
31
+
32
+ group :development do
33
+ gem 'web-console'
34
+ end