Breaks subscript notation for array like behavior of ActiveRecord::Relation
Closed this issue · 4 comments
Possibly a regression in 2.1.0 (#10), or maybe a slightly different bug
I'm seeing this with fairly normal AR class
class Company < ActiveRecord::Base
include ArelHelpers::ArelTable
Company.all.class
=> Company::ActiveRecord_Relation
[149] pry(main)> Company.all[0]
=> #<struct Arel::Attributes::Attribute
relation=
#<Arel::Table:0x007faa3c4d9f20
@aliases=[],
@columns=nil,
@engine=
.....
[150] pry(main)>
Thanks for the bug report @svoynow. What version of Rails/ActiveRecord/Arel are you using? Once I know that I can try reproducing.
rails 4.2.2
active-record 4.2.2
arel 6.0.3
arel-helpers 2.1.0
Awesome, thanks. I'll take a look.
Ok, I've tracked down the cause of the problem. It seems that ActiveRecord::Relation
instances delegate certain array methods like #[]
to their #to_a
methods. This makes sense when you realize that relations behave like arrays without actually being arrays. The following code lives in activerecord's delegation.rb:
def method_missing(method, *args, &block)
if @klass.respond_to?(method)
self.class.delegate_to_scoped_klass(method)
scoping { @klass.send(method, *args, &block) }
elsif Array.method_defined?(method)
self.class.delegate method, :to => :to_a
to_a.send(method, *args, &block)
elsif arel.respond_to?(method)
self.class.delegate method, :to => :arel
arel.send(method, *args, &block)
else
super
end
end
Notice that activerecord sets up the delegation as soon as any array method is detected by method_missing
, effectively memoizing the lookup. Any subsequent calls to the same array method will be handled by the delegation and bypass method_missing
entirely.
Unfortunately for arel-helpers, this is a bit of bad news. Notice that before array methods are examined, the method_missing
above checks to see if the model class (i.e. @klass
) responds to the method. ArelHelpers::ArelTable
adds #[]
to the model class, which gets invoked instead of the correct array method.
I think the fix here is to force delegation to #[]
with a call to ActiveRecord::Delegation#delegate
(which by the way is mixed into ActiveRecord::Relation
). I've created #19 to address the issue.