Is there an elegant one liner to append scopes stored in an array using .send()?
-
14-06-2021 - |
Question
Here is what I have
currently, I'm doing this:
current_account.send(object).search(params[:search]).user_id_equals_any(users).visibility_is_any(visibilities)
but thats not very flexible? What if I want to conditionally not have one of those scopes?
I figure an array of scopes with conditionally added elements would be a good solution
An array of parameters that I would invoke using .send()
scopes = []
scopes = << [:user_id_equals_any, users] if filter_users
scopes = << [:visibility_is_any, visibilities] if filter_visibility
So, I could have some unknown number scopes.
To execute the scopes, I need to invoke .send(:scope_name, scope_param)
But How do I do that for any number of scopes in my scopes
array?
As a loop, I think It would be something like
result = current_account.send(object).search(params[:search])
scopes.each do |scope|
result.send(scope[0], scope[1])
end
return result
the loop can be simplified to
scopes.each {|s| result.send(s[0], s[1]) }
but is there a way to append the different send calls in one line?
Solution
You can use inject
:
scopes = []
# Note that in your example, the assignment is invalid syntax
scopes << [:user_id_equals_any, users] if filter_users
scopes << [:visibility_is_any, visibilities] if filter_visibility
# initial result
result = current_account.send(object).search(params[:search])
# applying the scopes
result = scopes.inject(result) do |result, (method, param)|
result.send(method, param)
end
return result
In each loop, the last return value of the previous block execution gets passed the first parameter of the block, in this case result
. The first loop gets the initial result (i.e. the parameter in square brackets to the inject
call. The return value of the last loop gets returned.
For more details, have a look at the documentation.
OTHER TIPS
Feel free to build your own abstractions so your code reflects how you think about a problem:
class Object
def send_if(condition, *args, &block)
condition ? send(*args, &block) : self
end
end
current_account.send(object).search(params[:search]).
send_if(filter_user, :user_id_equals_any, users).
send_if(filter_visibility, :visibility_is_any, visibilities)