banister / method_source

return the sourcecode for a method
MIT License
361 stars 43 forks source link

How can I use method_source to get the source of a Rails scope? #70

Open benlieb opened 3 years ago

benlieb commented 3 years ago

I have a Rails scope:

  scope :positive_amount,                    -> { where("amount > 0") }

But when I do:

Transaction.method(:positive_amount).source.display

I get:

            singleton_class.send(:define_method, name) do |*args|
              scope = all
              scope = scope._exec_scope(*args, &body)
              scope = scope.extending(extension) if extension
              scope
            end

Not the -> { where("amount > 0") } that I'd like.

Any ideas?

ccutrer commented 2 years ago

this is because Rails generates a new method that will call your block only when the scope is called. method_source is properly returning the source of the generated method

igorfokine commented 1 year ago

I was able to get the source code of a scope by adding this:

ActiveRecord::Scoping::Named::ClassMethods.prepend(Module.new do
  def scope(name, body, &block)
    (@__named_scopes__ ||= {})[name] = body
    super
  end
end)

Note that if you run this code in the Rails console you would need to reload the model (with default config.cache_classes, use reload! for development and Object.send(:remove_const, :Transaction); load "./app/models/transaction.rb" for production).

After that you can get the source like this:

Transaction.instance_variable_get(:@__named_scopes__)[:positive_amount].source.display

which would return

  scope :positive_amount,                    -> { where("amount > 0") }