0

I'm trying to monkeypatch a method onto ActiveRecord::QueryMethods which augments the select clause instead of replacing it entirely.

I tried:

# config/initalizers/select_also.rb
module SelectAlso
  def select_also(*fields)
    select(self.select_values + fields)
  end
end

ActiveRecord::QueryMethods.include(SelectAlso)

But this gives me:

/lib/ruby/gems/2.6.0/gems/bootsnap-1.4.5/lib/bootsnap/load_path_cache/core_ext/active_support.rb:79:in `block in load_missing_constant': uninitialized constant ActiveRecord::Relation::QueryMethods (NameError)

I don't get where its getting the constant ActiveRecord::Relation::QueryMethods from at all since i'm not referencing it.

The reason I'm doing it as a monkeypatch is that I want to try it out as a potential PR / feature request to rails itself without working on the rails source and dealing with setting up a sample app.

The use case is where you want to add something like aggregates or joined columns without recreating the entire select clause:

User.joins(:answers)
   .select_also('AVG(answers.score) AS average_score')

Instead of:

User.joins(:answers)
   .select('users.*','AVG(answers.score) AS average_score')

Or some hacky solution that introspects on the table.

max
  • 96,212
  • 14
  • 104
  • 165

1 Answers1

1

You might want to consider ActiveRecord::QueryMethods#extending as an alternative to monkey-patching - it's designed to extend a scope with additional methods.

module Pagination
  def page(number)
    # pagination code goes here
  end
end

scope = Model.all.extending(Pagination)
scope.page(params[:page])

Rails also supports applying extensions to associations which is pretty neat.

odlp
  • 4,984
  • 2
  • 34
  • 45
  • Thanks, the reason I'm doing it as a monkeypatch is that I want to try it out as a potential PR to rails itself. `.extending` does not let me do this without creating a scope first and a kind of want to make it interchangeable with `#select`. – max Nov 14 '19 at 14:54
  • So it should be able to be called as `Model.select_also('LOWER(foo) AS bar')` instead of `Model.select_also('LOWER(foo) AS bar')` instead of using `Model.select('models.*', 'LOWER(foo) AS bar')`. – max Nov 14 '19 at 14:56
  • Ah, I was missing that context - good luck! If it's just for testing purposes you might be able to try `except` to clear any existing `select` values? E.g. `Model.extending(SelectAlso).except(:select).select_also(x, y, z)` https://stackoverflow.com/a/4968404/703903 / https://api.rubyonrails.org/classes/ActiveRecord/SpawnMethods.html#method-i-except – odlp Nov 14 '19 at 15:00