Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 3 additions & 16 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -68,18 +68,13 @@ jobs:
fail-fast: false
matrix:
rails-version:
- 7.1.6
- 7.2.3
- 8.0.4
- 8.1.1
- 8.1.2
ruby-version:
- 3.4
postgres-version:
- 18
exclude:
- rails-version: '7.1.6'
ruby-version: '3.4'
postgres-version: '18'

steps:
- name: Install Postgresql
Expand Down Expand Up @@ -145,15 +140,11 @@ jobs:
fail-fast: false
matrix:
rails-version:
- 7.1.6
- 7.2.3
- 8.0.4
- 8.1.1
- 8.1.2
ruby-version:
- 3.4
exclude:
- rails-version: '7.1.6'
ruby-version: '3.4'

steps:
- uses: actions/checkout@v4
Expand Down Expand Up @@ -210,15 +201,11 @@ jobs:
fail-fast: false
matrix:
rails-version:
- 7.1.6
- 7.2.3
- 8.0.4
- 8.1.1
- 8.1.2
ruby-version:
- 3.4
exclude:
- rails-version: '7.1.6'
ruby-version: '3.4'

steps:
- name: Install MySQL
Expand Down
9 changes: 9 additions & 0 deletions lib/active_record/filter/predicate_builder_extension.rb
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,15 @@ def build_filter_joins(klass, filters, relations=[], custom=[])
relations << j
end
end
elsif js.is_a?(Proc)
resolved = js.call(value)
Array(resolved).compact.each do |j|
if j.is_a?(String)
custom << j
else
relations << j
end
end
elsif js
if js.is_a?(String)
custom << js
Expand Down
6 changes: 1 addition & 5 deletions lib/active_record/filter/query_methods_extension.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,7 @@ def build_join_buckets
left_joins = select_named_joins(left_outer_joins_values, stashed_left_joins) do |left_join|
if left_join.is_a?(ActiveRecord::QueryMethods::CTEJoin)
buckets[:join_node] << build_with_join_node(left_join.name, Arel::Nodes::OuterJoin)
# Add this elsif becasuse PR https://github.com/rails/rails/pull/46843
# Changed a line https://github.com/rails/rails/blob/ae2983a75ca658d84afa414dea8eaf1cca87aa23/activerecord/lib/active_record/relation/query_methods.rb#L1769
# that was probably a bug beforehand but allowed nodes to be joined
# which I think was and still is supported?
elsif left_join.is_a?(Arel::Nodes::OuterJoin)
elsif left_join.is_a?(Arel::Nodes::Join)
buckets[:join_node] << left_join
else
raise ArgumentError, "only Hash, Symbol and Array are allowed"
Expand Down
60 changes: 60 additions & 0 deletions test/filter_relationship_test/has_many_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,66 @@ class Property < ActiveRecord::Base
SQL
end

test "::filter filter_on with proc joins" do
photos_table = Photo.arel_table
accounts_table = Account.arel_table

Account.filter_on :by_format, -> (value) {
if value.is_a?(Array) && value.size > 1
accounts_table.join(photos_table).on(
photos_table[:account_id].eq(accounts_table[:id])
).join_sources
else
:photos
end
} do |klass, table, key, value, relation_trail, alias_tracker|
if value.is_a?(Array)
children = value.map { |v| photos_table[:format].eq(v) }
Arel::Nodes::And.new(children)
else
photos_table[:format].eq(value)
end
end

query = Account.filter(by_format: 'jpg')
assert_sql(<<-SQL, query)
SELECT accounts.* FROM accounts
LEFT OUTER JOIN photos ON photos.account_id = accounts.id
WHERE photos.format = 'jpg'
SQL

query = Account.filter(by_format: ['jpg', 'png'])
assert_sql(<<-SQL, query)
SELECT accounts.* FROM accounts
INNER JOIN photos ON photos.account_id = accounts.id
WHERE photos.format = 'jpg' AND photos.format = 'png'
SQL
ensure
Account.filters.delete('by_format')
end

test "::filter filter_on with arel join node" do
photos_table = Photo.arel_table
accounts_table = Account.arel_table

join_node = accounts_table.join(photos_table).on(
photos_table[:account_id].eq(accounts_table[:id])
).join_sources.first

Account.filter_on :with_photo_format, join_node do |klass, table, key, value, relation_trail, alias_tracker|
photos_table[:format].eq(value)
end

query = Account.filter(with_photo_format: 'jpg')
assert_sql(<<-SQL, query)
SELECT accounts.* FROM accounts
INNER JOIN photos ON photos.account_id = accounts.id
WHERE photos.format = 'jpg'
SQL
ensure
Account.filters.delete('with_photo_format')
end

test "::filter filter_on" do
query = Photo.filter(no_properties_where_state_is_null: true)

Expand Down
Loading