active_windows 0.1.4 → 0.1.6

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: 2b6484d037d47f4cfa292310a435491c37b03c15921159e34df31e6d69972f9f
4
- data.tar.gz: 20ec937b0f30935c9d2775508c4a22e75edaa791b0e4ad4ce51bced086efa512
3
+ metadata.gz: b393a3c07582141b6656bbf2f9ffe55193f6fd32c3505af7875a9e6f76fec7d0
4
+ data.tar.gz: 7571dfc93bc77a96e495b2c74799100320e7bf6666809ceb754ec5fc64efffc9
5
5
  SHA512:
6
- metadata.gz: 5abfc8d5ca03ab4bd1c8ef3f76c0919b9b4dce21b2d576ad1650d0bebc870c9f29c6944441560bed1c6a73fe3742ae7adfa25f9d6439e66723e759fa07890a1a
7
- data.tar.gz: 6b195b1e5afb8b55b641208c9ebb9540b329d9a0f2de18e33ec3dcfd17acb85bf15d461bde3cb004261744289c253a8ea033311c0d3ec1edab9b184acd55fc56
6
+ metadata.gz: 8380c1b66b1a1a3c31f9e9c5a9035c43ff7689e499254b4bd7d9b250fbfaeb36054c8a012e49f4f84756a0a6ab8c42bf3a931bad82fda768b07556f955bdac3a
7
+ data.tar.gz: 92128ba7625b79177654e2f09a76f163d5901ddf2105f84174fe591d81dd020d276fdddb428d76649c0648ac7127f2bfcbee4b539c007221c74be1c6d231ae85
data/README.md CHANGED
@@ -84,6 +84,19 @@ Available options:
84
84
  | `:frame` | `String` | Raw SQL frame clause (e.g. `"ROWS BETWEEN 1 PRECEDING AND 1 FOLLOWING"`) |
85
85
  | `:value` | `Symbol`, `String`, `Array` | Expression(s) passed as function arguments |
86
86
 
87
+ ### Association Names
88
+
89
+ You can use `belongs_to` association names instead of foreign key columns. ActiveWindows automatically resolves them:
90
+
91
+ ```ruby
92
+ # These are equivalent:
93
+ Order.row_number.partition_by(:user).window_order(:amount).as(:rn)
94
+ Order.row_number.partition_by(:user_id).window_order(:amount).as(:rn)
95
+
96
+ # Works in the hash API too:
97
+ Order.window(row_number: { partition: :user, order: :amount, as: :rn })
98
+ ```
99
+
87
100
  ### Chaining with ActiveRecord
88
101
 
89
102
  Window functions integrate naturally with standard ActiveRecord methods:
@@ -34,6 +34,8 @@ The gem provides a fluent DSL for SQL window functions in ActiveRecord. Core fun
34
34
  - ~~**PostgreSQL CI**~~ — Added. GitHub Actions workflow tests against PostgreSQL 17 with service container.
35
35
  - ~~**MySQL CI**~~ — Added. GitHub Actions workflow tests against MySQL 8.0 with service container.
36
36
  - ~~**MySQL compatibility**~~ — Fixed. Aliases now use `klass.connection.quote_column_name` to properly quote reserved words (e.g., `rank`) with backticks on MySQL and double quotes on PostgreSQL/SQLite. Test assertions use adapter-agnostic `q()` and `col()` helpers.
37
+ - ~~**WindowChain `order` naming collision**~~ — Fixed. Renamed to `window_order` to avoid conflict with ActiveRecord's `.order()`. WindowChain now delegates `.order()` to the relation for query-level ordering. Uses `method_missing` for full relation method coverage.
38
+ - ~~**Association name resolution**~~ — Added. `partition_by(:user)` automatically resolves to `user_id` via `belongs_to` reflection. Works in both fluent and hash APIs. 74 tests, 355 assertions.
37
39
 
38
40
  ---
39
41
 
@@ -72,9 +72,18 @@ module ActiveWindows
72
72
  raise ArgumentError, "wrong number of arguments (given 0, expected 1+)" if args.empty?
73
73
 
74
74
  processed = process_window_args(args)
75
- arel_nodes = processed.map { |name, options| build_window_function(name, options || {}) }
76
75
 
77
76
  result = spawn
77
+
78
+ # Auto-join has_one associations referenced in partition/order
79
+ joins_needed = processed.flat_map do |_name, options|
80
+ next [] unless options.is_a?(Hash)
81
+ association_joins_for(options[:partition]) + association_joins_for(options[:order])
82
+ end.uniq
83
+ result = result.joins(*joins_needed) if joins_needed.any?
84
+
85
+ arel_nodes = processed.map { |name, options| build_window_function(name, options || {}) }
86
+
78
87
  # Ensure we keep all columns alongside the window function columns
79
88
  result = result.select(klass.arel_table[Arel.star]) if result.select_values.empty?
80
89
  result.select(*arel_nodes)
@@ -217,7 +226,30 @@ module ActiveWindows
217
226
  if name.is_a?(Arel::Nodes::Node) || name.is_a?(Arel::Nodes::SqlLiteral)
218
227
  name
219
228
  else
220
- klass.arel_table[name.to_sym]
229
+ resolve_column(name)
230
+ end
231
+ end
232
+
233
+ def resolve_column(name)
234
+ name_sym = name.to_sym
235
+ return klass.arel_table[name_sym] if klass.column_names.include?(name.to_s)
236
+
237
+ reflection = klass.reflect_on_association(name_sym)
238
+ if reflection&.macro == :belongs_to
239
+ klass.arel_table[reflection.foreign_key.to_sym]
240
+ elsif reflection&.macro == :has_one
241
+ reflection.klass.arel_table[reflection.klass.primary_key.to_sym]
242
+ else
243
+ klass.arel_table[name_sym]
244
+ end
245
+ end
246
+
247
+ def association_joins_for(columns)
248
+ Array(columns).filter_map do |col|
249
+ next unless col.is_a?(Symbol) || col.is_a?(String)
250
+
251
+ reflection = klass.reflect_on_association(col.to_sym)
252
+ col.to_sym if reflection&.macro == :has_one
221
253
  end
222
254
  end
223
255
 
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveWindows
4
- VERSION = "0.1.4"
4
+ VERSION = "0.1.6"
5
5
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_windows
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.4
4
+ version: 0.1.6
5
5
  platform: ruby
6
6
  authors:
7
7
  - Andrei Andriichuk