I have a method that looks something like
class Student < ActiveRecord::Base
def self.search(options = {})
all.tap do |s|
s.where(first_name: options[:query]) if options[:query]
s.where(graduated: options[:graduated]) if options[:graduated]
# etc there are many more things that can be filtered on...
end
end
end
When calling this method though, I am getting back all of the results and not a filtered set as I would expect. It seems like my tap
functionality is not working as I expect. What is the correct way to do this (without assigning all
to a variable. I would like to use blocks here if possible).
tap
will not work for this.
all
is an ActiveRecord::Relation
, a query waiting to happen. all.where(...)
returns a new ActiveRecord::Relation
the new query. However checking the documentation for tap, you see that it returns the object that it was called on (in this case all
) as opposed to the return value of the block.
i.e. it is defined like this:
def tap
yield self # return from block **discarded**
self
end
When what you wanted was just:
def apply
yield self # return from block **returned**
end
Or something similar to that.
This is why you keep getting all the objects returned, as opposed to the objects resulting from the query. My suggestion is that you build up the hash you send to where
as opposed to chaining where
calls. Like so:
query = {}
query[:first_name] = options[:query] if options[:query]
query[:graduated] = options[:graduated] if options[:graduated]
# ... etc.
all.where(query)
Or a possibly nicer implementation:
all.where({
first_name: options[:query],
graduated: options[:graduated],
}.delete_if { |_, v| v.empty? })
(If intermediate variables are not to your taste.)
You can easily create a let
function:
class Object
def let
return yield self
end
end
And use it like this:
all.let do |s|
s=s.where(first_name: options[:query]) if options[:query]
s=s.where(graduated: options[:graduated]) if options[:graduated]
# etc there are many more things that can be filtered on...
s
end
The difference between tap
and let
is that tap
returns the object and let
returns the blocks return value.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With