sage-rails 0.1.8 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 4d1bccb977fae3786227b2511a90fdb9163e497ba560a82d457fe6fc25b53153
4
- data.tar.gz: 4f56781eaa2545ffd8970491d68c897897b38fdd518f05cc2ef9fe235d411db8
3
+ metadata.gz: '085941f32e26330f8fccca54aa1908dddd9c295e89157539a20a851bc020e98c'
4
+ data.tar.gz: 01f0c003a16fcac98c33cbfb0d53007947f99fb65972ab15db14dd8c9a0ce890
5
5
  SHA512:
6
- metadata.gz: eac2215b633b6eeb16b707cbb74e79c081496f03748c4e98e9d9305c34896c6b2397a413c679b5eac97b68c7d436035794539fc048d6a8fc68d323ec74dcd5ca
7
- data.tar.gz: 52dec8f05a2c39e85977b803dfce1d119e160ee534bf76fb4d6c528a1757c358c94a62ae4ad85d235677d22e397011b6e3588c300cb5638c7249f353ed39c881
6
+ metadata.gz: ea7714fffba89cc55b14102e97203b8792918b91627b0ad59610279d71ed4b6edb08b8470ab87513a5e625ab30a2a7defbf63dbffec75557d756486b7bfa573c
7
+ data.tar.gz: 783b058656f060a52cc55177656bf06c46cb66390beaf214f8a7b8831018d941d514804d54046020603e220b2fbb0e735b5fed5d3ad95a61816aba8eeee7f634
@@ -1,6 +1,13 @@
1
1
  module Sage
2
2
  class BaseController < Blazer::BaseController
3
- include Pagy::Backend
3
+ # Support both Pagy 9.x (Backend) and Pagy 43.x (Method)
4
+ if defined?(Pagy::Backend)
5
+ include Pagy::Backend # Pagy 9.x
6
+ elsif defined?(Pagy::Method)
7
+ include Pagy::Method # Pagy 43.x
8
+ else
9
+ raise "Pagy must be loaded before Sage::BaseController"
10
+ end
4
11
 
5
12
  layout "sage/application"
6
13
  helper Importmap::ImportmapTagsHelper
@@ -16,8 +16,12 @@ module Sage
16
16
  @dashboards = @dashboards.where(creator_id: blazer_user.id).reorder(updated_at: :desc)
17
17
  end
18
18
 
19
- # Apply pagination with Pagy
20
- @pagy, @dashboards = pagy(@dashboards)
19
+ # Apply pagination with Pagy (support both 9.x and 43.x)
20
+ @pagy, @dashboards = if defined?(Pagy::Backend)
21
+ pagy(@dashboards) # Pagy 9.x
22
+ else
23
+ pagy(:offset, @dashboards) # Pagy 43.x
24
+ end
21
25
  end
22
26
 
23
27
  def new
@@ -23,8 +23,12 @@ module Sage
23
23
  # Filter out private queries (starting with #) unless they belong to the current user
24
24
  @queries = @queries.where("name NOT LIKE ? OR creator_id = ?", "#%", blazer_user.try(:id))
25
25
 
26
- # Apply pagination with Pagy
27
- @pagy, @queries = pagy(@queries)
26
+ # Apply pagination with Pagy (support both 9.x and 43.x)
27
+ @pagy, @queries = if defined?(Pagy::Backend)
28
+ pagy(@queries) # Pagy 9.x
29
+ else
30
+ pagy(:offset, @queries) # Pagy 43.x
31
+ end
28
32
  end
29
33
 
30
34
  def new
@@ -1,7 +1,5 @@
1
1
  module Sage
2
2
  module ApplicationHelper
3
- include Pagy::Frontend
4
-
5
3
  # Provide access to Sage engine routes
6
4
  def sage
7
5
  Sage::Engine.routes.url_helpers
@@ -26,5 +24,16 @@ module Sage
26
24
  end
27
25
  end
28
26
  end
27
+
28
+ # Support both Pagy 9.x and 43.x
29
+ def pagy_navigation(pagy_object)
30
+ if defined?(Pagy::Backend)
31
+ # Pagy 9.x uses pagy_nav helper
32
+ pagy_nav(pagy_object)
33
+ else
34
+ # Pagy 43.x uses instance method
35
+ pagy_object.series_nav
36
+ end
37
+ end
29
38
  end
30
39
  end
@@ -48,7 +48,7 @@
48
48
  </table>
49
49
 
50
50
  <div style="display: flex; justify-content: center; padding: 1rem;">
51
- <%== pagy_nav(@pagy) if @pagy.pages > 1 %>
51
+ <%== pagy_navigation(@pagy) if @pagy.pages > 1 %>
52
52
  </div>
53
53
  <% else %>
54
54
  <div class="mt-3">
@@ -26,6 +26,12 @@
26
26
  outline: 2px solid rgba(66, 165, 245, 1);
27
27
  outline-offset: 2px;
28
28
  }
29
+
30
+ /* ACE Editor cursor styling - sharp vertical line */
31
+ .ace_cursor {
32
+ border-left: 2px solid !important;
33
+ border-radius: 0 !important;
34
+ }
29
35
  </style>
30
36
 
31
37
  <div id=<%= dom_id(query, 'statement-box') %> class='' style="position: relative; border-radius: 10px;">
@@ -49,7 +49,7 @@
49
49
  </table>
50
50
 
51
51
  <div style="display: flex; justify-content: center; padding: 1rem;">
52
- <%== pagy_nav(@pagy) if @pagy.pages > 1 %>
52
+ <%== pagy_navigation(@pagy) if @pagy.pages > 1 %>
53
53
  </div>
54
54
  <% else %>
55
55
  <div class="mt-3">
@@ -198,6 +198,12 @@
198
198
  text-overflow: ellipsis;
199
199
  margin-bottom: 10px;
200
200
  }
201
+
202
+ /* ACE Editor cursor styling - sharp vertical line */
203
+ .ace_cursor {
204
+ border-left: 2px solid !important;
205
+ border-radius: 0 !important;
206
+ }
201
207
  </style>
202
208
 
203
209
  <script>
@@ -251,12 +257,16 @@
251
257
  }
252
258
  }
253
259
 
254
- document.addEventListener('DOMContentLoaded', function() {
260
+ let tabsInitialized = false;
261
+
262
+ function setupTabSwitching() {
255
263
  const promptTab = document.getElementById('prompt-tab');
256
264
  const sqlTab = document.getElementById('sql-tab');
257
265
  const promptPage = document.getElementById('prompt-page');
258
266
  const sqlPage = document.getElementById('sql-page');
259
267
 
268
+ if (!promptTab || !sqlTab || !promptPage || !sqlPage) return;
269
+
260
270
  function switchToPromptMode() {
261
271
  promptTab.classList.add('active');
262
272
  sqlTab.classList.remove('active');
@@ -269,32 +279,29 @@
269
279
  promptTab.classList.remove('active');
270
280
  sqlPage.classList.add('active');
271
281
  promptPage.classList.remove('active');
282
+
283
+ // Initialize ACE editor when switching to SQL mode
284
+ setTimeout(() => {
285
+ initializeAceEditor();
286
+ }, 100);
272
287
  }
273
288
 
274
- promptTab.addEventListener('click', switchToPromptMode);
275
- sqlTab.addEventListener('click', switchToSqlMode);
289
+ // Only attach listeners once
290
+ if (!tabsInitialized) {
291
+ promptTab.addEventListener('click', switchToPromptMode);
292
+ sqlTab.addEventListener('click', switchToSqlMode);
293
+ tabsInitialized = true;
294
+ }
276
295
 
277
296
  // If we have fork parameters, automatically switch to SQL mode
278
297
  const urlParams = new URLSearchParams(window.location.search);
279
298
  if (urlParams.get('fork_query_id') || urlParams.get('name')) {
280
- console.log('Fork detected, switching to SQL mode');
281
299
  switchToSqlMode();
282
- // Initialize ACE editor immediately for forks
283
- setTimeout(() => {
284
- console.log('Initializing ACE editor for fork');
285
- initializeAceEditor();
286
- }, 200);
287
300
  }
288
- });
301
+ }
289
302
 
290
- // Initialize ACE editor when SQL tab is clicked
291
- document.addEventListener('click', function(e) {
292
- if (e.target.id === 'sql-tab') {
293
- setTimeout(() => {
294
- initializeAceEditor();
295
- }, 100);
296
- }
297
- });
303
+ document.addEventListener('DOMContentLoaded', setupTabSwitching);
304
+ document.addEventListener('turbo:load', setupTabSwitching);
298
305
 
299
306
  // Ensure form submission captures the editor value
300
307
  document.addEventListener('submit', function(e) {
@@ -305,28 +312,4 @@
305
312
  }
306
313
  }
307
314
  });
308
-
309
- // Handle Turbo navigation for fork detection
310
- document.addEventListener('turbo:load', function() {
311
- const urlParams = new URLSearchParams(window.location.search);
312
- if (urlParams.get('fork_query_id') || urlParams.get('name')) {
313
- console.log('Fork detected via turbo:load, switching to SQL mode');
314
- const promptTab = document.getElementById('prompt-tab');
315
- const sqlTab = document.getElementById('sql-tab');
316
- const promptPage = document.getElementById('prompt-page');
317
- const sqlPage = document.getElementById('sql-page');
318
-
319
- if (sqlTab && promptTab && sqlPage && promptPage) {
320
- sqlTab.classList.add('active');
321
- promptTab.classList.remove('active');
322
- sqlPage.classList.add('active');
323
- promptPage.classList.remove('active');
324
-
325
- setTimeout(() => {
326
- console.log('Initializing ACE editor for fork via turbo:load');
327
- initializeAceEditor();
328
- }, 200);
329
- }
330
- }
331
- });
332
315
  </script>
@@ -1,2 +1,17 @@
1
- Pagy::DEFAULT[:items] = 10
2
- Pagy::DEFAULT[:overflow] = :last_page
1
+ # Handle both mutable and frozen Pagy::DEFAULT (frozen in Pagy 9.x and Rails 8.1.1+ / Ruby 3.4+)
2
+ # Also support both Pagy 9.x (items/overflow) and Pagy 43.x (limit/overflow)
3
+
4
+ # Pagy 43.x uses :limit instead of :items and it's already in DEFAULT
5
+ if Pagy::DEFAULT.key?(:limit)
6
+ # Pagy 43.x - DEFAULT already has :limit, just need to set overflow if not present
7
+ unless Pagy::DEFAULT.key?(:overflow)
8
+ new_defaults = Pagy::DEFAULT.merge(overflow: :last_page)
9
+ Pagy.send(:remove_const, :DEFAULT)
10
+ Pagy.const_set(:DEFAULT, new_defaults)
11
+ end
12
+ else
13
+ # Pagy 9.x - use :items and :overflow
14
+ new_defaults = Pagy::DEFAULT.merge(items: 10, overflow: :last_page)
15
+ Pagy.send(:remove_const, :DEFAULT)
16
+ Pagy.const_set(:DEFAULT, new_defaults)
17
+ end
@@ -50,8 +50,16 @@ module Sage
50
50
  def create_migrations
51
51
  say "Creating Sage database migrations...", :green
52
52
 
53
- # Generate timestamp for migration
54
- timestamp = Time.now.utc.strftime("%Y%m%d%H%M%S")
53
+ # Generate unique timestamp for migration by checking existing migrations
54
+ # and ensuring we don't create a duplicate timestamp
55
+ existing_timestamps = Dir.glob("db/migrate/*").map do |file|
56
+ File.basename(file).match(/^(\d+)_/)[1].to_i
57
+ end.compact
58
+
59
+ timestamp = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
60
+ while existing_timestamps.include?(timestamp)
61
+ timestamp += 1
62
+ end
55
63
 
56
64
  # Create the migration file
57
65
  migration_file = "db/migrate/#{timestamp}_create_sage_messages.rb"
@@ -86,14 +94,13 @@ module Sage
86
94
  say "Sage installation complete!", :green
87
95
  say "="*50, :green
88
96
  say "\nNext steps:"
89
- say "1. Run 'bundle install' to install dependencies"
90
- say "2. Run 'rails db:migrate' to create Blazer tables"
91
- say "3. Configure your AI service in config/initializers/sage.rb"
92
- say "4. Visit #{root_url}sage to start using Sage"
97
+ say "1. Run 'rails db:migrate' to create Blazer tables"
98
+ say "2. Configure your AI service in config/initializers/sage.rb"
99
+ say "3. Visit #{root_url}sage to start using Sage"
93
100
  say "\nFor AI integration, you'll need to:"
94
101
  say "- Set up an Anthropic API key (or OpenAI if preferred)"
95
102
  say "- Add the API key to Rails credentials or .env file"
96
- say "- Configure database schema context for better SQL generation"
103
+ say "- Leverage model scopes for better SQL inference"
97
104
  end
98
105
 
99
106
  private
data/lib/sage/engine.rb CHANGED
@@ -51,9 +51,14 @@ module Sage
51
51
  end
52
52
  end
53
53
 
54
+ # Support both Pagy 9.x (Frontend) and Pagy 43.x (instance methods)
54
55
  initializer "sage.pagy_helpers" do
55
56
  ActiveSupport.on_load(:action_view) do
56
- include Pagy::Frontend if defined?(Pagy::Frontend)
57
+ if defined?(Pagy::Frontend)
58
+ # Pagy 9.x uses Frontend module
59
+ include Pagy::Frontend
60
+ end
61
+ # Pagy 43.x uses instance methods on @pagy objects, no module needed
57
62
  end
58
63
  end
59
64
 
data/lib/sage/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Sage
2
- VERSION = "0.1.8"
2
+ VERSION = "0.2.0"
3
3
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: sage-rails
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.8
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nathan Jones