Search code examples
ruby-on-railsrubyscopes

Is there an elegant one liner to append scopes stored in an array using .send()?


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.