Question

I am trying to refactor a search method into a new class because I find myself using it in nearly every model in my app:

  def self.text_search(query)
    if query.present?
      where("username @@ :q or email @@ :q", q: query).order(
        updated_at: :desc)
    else
      order(updated_at: :desc)
    end
  end

The params username and email change depending on the model, but I have 3 models where I use the same functionality with two different params. My idea is to use a decorator, like this:

class SearchModel < SimpleDelegator

    def text_search(param1, param2, options={})
        query = options[:query]
        if query.present?
            __getobj__.where("#{param1} @@ :q or #{param2} @@ :q", q:query).order(updated_at: :desc)
        else
            order(updated_at: :desc)
        end
    end
end

I am calling SearchModel like this:

SearchModel.new(User.all).text_search('username', 'email', query: "admin")

SearchModel.new(User.all).text_search('username', 'email')

and it gives me what I want, but I am not sure if I am using a decorator in the correct way. Would this type of refactor be more appropriate in some other part of Ruby's standard library, or is this what decorators are for?

Était-ce utile?

La solution

Using a decorator is a more elaborate approach than most people would use, and leads to a complicated calling syntax. I would do it like this:

module TextSearchSupport
  def text_search(*args)
    search_term = if args.last.responds_to(:[]) then args.pop[:query] end
    query =
      if search_term
        where(args.map { |arg| "#{arg} @@ :q" }.join(' or '), q: query)
      else
        scoped
      end
    query.order(updated_at: :desc)
  end
end

and extend TextSearchSupport in each model that needs it. Then you can just call text_search('foo', 'bar', query: 'query') on the model.

A couple of asides:

  • it is nicer to handle any number of fields rather than hardcoding, so I did that
  • I think you want scoped rather than all to avoid running the query right away, so I used that, but you say your version was working so not sure what was going on there.
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top