Skip to content

Commit

Permalink
Add Alias Tracker and Relation
Browse files Browse the repository at this point in the history
  • Loading branch information
malomalo committed Sep 18, 2019
1 parent 876011d commit d1974a9
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 55 deletions.
6 changes: 3 additions & 3 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ PATH
specs:
activerecord-filter (6.0.0.3)
activerecord (>= 6.0.0)
arel-extensions (>= 6.0.0)
arel-extensions (>= 6.0.0.5)

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -33,8 +33,8 @@ GEM
tzinfo (~> 1.1)
zeitwerk (~> 2.1, >= 2.1.8)
ansi (1.5.0)
arel-extensions (6.0.0.4)
activerecord (>= 6.0.0.rc1)
arel-extensions (6.0.0.5)
activerecord (>= 6.0.0)
builder (3.2.3)
byebug (11.0.1)
concurrent-ruby (1.1.5)
Expand Down
2 changes: 1 addition & 1 deletion activerecord-filter.gemspec
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Gem::Specification.new do |spec|
spec.require_paths = ["lib"]

spec.add_runtime_dependency 'activerecord', '>= 6.0.0'
spec.add_runtime_dependency 'arel-extensions', '>= 6.0.0'
spec.add_runtime_dependency 'arel-extensions', '>= 6.0.0.5'

spec.add_development_dependency 'pg'
spec.add_development_dependency 'actionpack', '>= 6.0.0'
Expand Down
100 changes: 50 additions & 50 deletions lib/active_record/filter.rb
Original file line number Diff line number Diff line change
Expand Up @@ -77,13 +77,13 @@ def self.build_filter_joins(klass, filters, relations=[], custom=[])
relations
end

def build_from_filter_hash(attributes)
def build_from_filter_hash(attributes, relation_trail, alias_tracker)
if attributes.is_a?(Array)
node = build_from_filter_hash(attributes.shift)
node = build_from_filter_hash(attributes.shift, relation_trail, alias_tracker)

n = attributes.shift(2)
while !n.empty?
n[1] = build_from_filter_hash(n[1])
n[1] = build_from_filter_hash(n[1], relation_trail, alias_tracker)
if n[0] == 'AND'
if node.is_a?(Arel::Nodes::And)
node.children.push(n[1])
Expand All @@ -100,26 +100,26 @@ def build_from_filter_hash(attributes)

node
elsif attributes.is_a?(Hash)
expand_from_filter_hash(attributes)
expand_from_filter_hash(attributes, relation_trail, alias_tracker)
else
expand_from_filter_hash({id: attributes})
expand_from_filter_hash({id: attributes}, relation_trail, alias_tracker)
end
end

def expand_from_filter_hash(attributes)
def expand_from_filter_hash(attributes, relation_trail, alias_tracker)
klass = table.send(:klass)

children = attributes.flat_map do |key, value|
if custom_filter = klass.filters[key]
self.instance_exec(klass, table, key, value, &custom_filter[:block])
self.instance_exec(klass, table, key, value, relation_trail, alias_tracker, &custom_filter[:block])
elsif column = klass.columns_hash[key.to_s] || klass.columns_hash[key.to_s.split('.').first]
expand_filter_for_column(key, column, value)
expand_filter_for_column(key, column, value, relation_trail)
elsif relation = klass.reflect_on_association(key)
expand_filter_for_relationship(relation, value)
expand_filter_for_relationship(relation, value, relation_trail, alias_tracker)
elsif key.to_s.ends_with?('_ids') && relation = klass.reflect_on_association(key.to_s.gsub(/_ids$/, 's'))
expand_filter_for_relationship(relation, {id: value})
expand_filter_for_relationship(relation, {id: value}, relation_trail, alias_tracker)
elsif relation = klass.reflect_on_all_associations(:has_and_belongs_to_many).find {|r| r.join_table == key.to_s && value.keys.first.to_s == r.association_foreign_key.to_s }
expand_filter_for_join_table(relation, value)
expand_filter_for_join_table(relation, value, relation_trail, alias_tracker)
else
raise ActiveRecord::UnkownFilterError.new("Unkown filter \"#{key}\" for #{klass}.")
end
Expand All @@ -133,21 +133,12 @@ def expand_from_filter_hash(attributes)
end
end

def expand_filter_for_column(key, column, value)
# Not sure why
# activerecord/lib/active_record/table_metadata.rb#arel_attribute
# doesn't work here, something's not working with a
# Arel::Nodes::TableAlias, would like to go back to it one day
attribute = if klass = table.send(:klass)
if Arel::Nodes::TableAlias === table.send(:arel_table)
klass.arel_attribute(column.name, table.send(:arel_table).left)
else
klass.arel_attribute(column.name, table.send(:arel_table))
end
else
table.arel_attribute(column.name)
def expand_filter_for_column(key, column, value, relation_trail)
attribute = table.arel_attribute(column.name)
relation_trail.each do |rt|
attribute = Arel::Attributes::Relation.new(attribute, rt)
end

if column.type == :json || column.type == :jsonb
names = key.to_s.split('.')
names.shift
Expand Down Expand Up @@ -254,7 +245,7 @@ def expand_filter_for_arel_attribute(column, attribute, key, value)
end
end

def expand_filter_for_relationship(relation, value)
def expand_filter_for_relationship(relation, value, relation_trail, alias_tracker)
case relation.macro
when :has_many
if value == true || value == 'true'
Expand All @@ -279,24 +270,37 @@ def expand_filter_for_relationship(relation, value)
return table.arel_attribute(relation.foreign_key).eq(nil)
end
end


builder = associated_predicate_builder(relation.name)
# If we can figure out comment on line 137 we might not need this.
if relation.macro == :has_and_belongs_to_many && table.send(:klass) == relation.klass
builder.send(:table).send(:arel_table).left = Arel::Nodes::TableAlias.new(
builder.send(:table).send(:arel_table).left,
"#{relation.name}_#{relation.klass.table_name}"
)
end
builder.build_from_filter_hash(value)
builder = self.class.new(TableMetadata.new(
relation.klass,
alias_tracker.aliased_table_for(
relation.table_name,
relation.alias_candidate(table.send(:arel_table).name),
relation.klass.type_caster
),
relation
))
builder.build_from_filter_hash(value, relation_trail + [relation.name], alias_tracker)
end

def expand_filter_for_join_table(relation, value)

def expand_filter_for_join_table(relation, value, relation_trail, alias_tracker)
relation = relation.active_record._reflections[relation.active_record._reflections[relation.name.to_s].send(:delegate_reflection).options[:through].to_s]

builder = associated_predicate_builder(relation.name.to_sym)
builder.build_from_filter_hash(value)
STDOUT.puts [
relation.table_name,
relation.alias_candidate(table.send(:arel_table).name)

].inspect
builder = self.class.new(TableMetadata.new(
relation.klass,
alias_tracker.aliased_table_for(
relation.table_name,
relation.alias_candidate(table.send(:arel_table).name),
relation.klass.type_caster
),
relation
))
builder.build_from_filter_hash(value, relation_trail + [relation.name], alias_tracker)
end

end
Expand All @@ -311,14 +315,9 @@ def initialize(klass, predicate_builder)
@predicate_builder = predicate_builder
end

def build(filters)
def build(filters, alias_tracker)
if filters.is_a?(Hash) || filters.is_a?(Array)
# attributes = predicate_builder.resolve_column_aliases(filters)
# attributes = klass.send(:expand_hash_conditions_for_aggregates, attributes)
# attributes.stringify_keys!
#
# attributes, binds = predicate_builder.create_binds(attributes)
parts = [predicate_builder.build_from_filter_hash(filters)]
parts = [predicate_builder.build_from_filter_hash(filters, [], alias_tracker)]
else
raise ArgumentError, "Unsupported argument type: #{filters.inspect} (#{filters.class})"
end
Expand Down Expand Up @@ -379,13 +378,14 @@ def filter_clause_factory

def build_arel(aliases)
arel = super
build_filters(arel)
my_alias_tracker = ActiveRecord::Associations::AliasTracker.create(connection, table.name, [])
build_filters(arel, my_alias_tracker)
arel
end

def build_filters(manager)
def build_filters(manager, aliases)
@filters.each do |filters|
manager.where(filter_clause_factory.build(filters).ast)
manager.where(filter_clause_factory.build(filters, alias_tracker).ast)
end
end

Expand Down
2 changes: 1 addition & 1 deletion lib/active_record/filter/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module ActiveRecord
module Filter
VERSION = '6.0.0.3'
VERSION = '6.0.0.4'
end
end

0 comments on commit d1974a9

Please sign in to comment.