ActiveRecord query with alias'd table names
Well, well, well. After quite a big time looking through the sources of Arel
, ActiveRecord
and Rails
issues (it seems this is not new), I was able to find the way to access the current arel_table
object, with its table_aliases
if they are being used, inside the current
scope at the moment of its execution.
That made possible to know if the scope is going to be used within a JOIN
that has the table name aliased, or if on the other hand the scope can be used on the real table name.
I just added this method to your Expirable
concern:
def self.current_table_name
current_table = current_scope.arel.source.left
case current_table
when Arel::Table
current_table.name
when Arel::Nodes::TableAlias
current_table.right
else
fail
end
end
As you can see, I'm using current_scope
as the base object to look for the arel table, instead of the prior attempts of using self.class.arel_table
or even relation.arel_table
, which as you said remained the same regardless of where the scope was used. I'm just calling source
on that object to obtain an Arel::SelectManager
that in turn will give you the current table on the #left
. At this moment there are two options: that you have there an Arel::Table
(no alias, table name is on #name
) or that you have an Arel::Nodes::TableAlias
with the alias on its #right
.
With that table_name you can revert to your first attempt of #{current_table_name}.#{lower_bound_field}
and #{current_table_name}.#{upper_bound_field}
in your scopes:
def self.lower_bound_column
"#{current_table_name}.#{lower_bound_field}"
end
def self.upper_bound_column
"#{current_table_name}.#{upper_bound_field}"
end
scope :current_and_future, ->(as_at = Time.now) { where("#{upper_bound_column} IS NULL OR #{upper_bound_column} >= ?", as_at) }
scope :current_and_expired, ->(as_at = Time.now) { where("#{lower_bound_column} IS NULL OR #{lower_bound_column} <= ?", as_at) }
This current_table_name
method seems to me to be something that would be useful to have on the AR / Arel public API, so it can be maintained across version upgrades. What do you think?
If you are interested, here are some references I used down the road:
- A similar question on SO, answered with a ton of code, that you could use instead of your beautiful and concise Ability.
- This Rails issue and this other one.
- And the commit on your test app on github that made tests green!