randx
randx

Reputation: 129

Help me refactor ruby next code

@people = People.scoped
@people = @people.where(...) if ...
@people = @people.where(...) if ...
@people = @people.where(...) if ...
@people = @people.where(...) if ...

Is any ruby existing solutions to make something like

@people = People.scoped

@people.???? do 
  where(...) if ...
  where(...) if ...
  where(...) if ...
end

PS: Thanks for answers. But solutions you provide looks like

def self.conditional_scope
  where(...) if ...
  where(...) if ...
  where(...) if ...
end

I think i'll get only last where even if all "if" is true.

Am i right?

Upvotes: 1

Views: 135

Answers (5)

anon
anon

Reputation:

Maybe you're looking for a way to avoid explicitly assigning the new scope after every where clause? You might be interested in this railscast: http://railscasts.com/episodes/212-refactoring-dynamic-delegator. Ryan Bates uses a delegator to achieve code like this:

def self.search(params)
  products = scope_builder
  products.where("name like ?", "%" + params[:name] + "%") if params[:name]
  products.where("price >= ?", params[:price_gt]) if params[:price_gt]
  products.where("price <= ?", params[:price_lt]) if params[:price_lt]
  products
end

Upvotes: 0

DGM
DGM

Reputation: 26979

If I understand what you are asking, you only want to apply each scope if a condition exists... you could use a named scope with a lambda, and then chain them:

scope :one, lambda {|condition| condition ? where(...) : {}}
scope :two, lambda {|condition| condition ? where(...) : {}}
...


@people = Person.one(true).two(false)

Upvotes: 1

fl00r
fl00r

Reputation: 83680

Yes. You just need to move it to model:

# Controller
@people = People.find_my_guy

# Model
def self.find_my_guy
  where(...) if ...
  where(...) if ...
  where(...) if ...
end

Obviously, you'll need to pass some environment variable to your model if they are used in your statements:

# Controller
@people = People.find_my_guy(params)

# Model
def self.find_my_guy(params)
  where(:id => params[:id]) if params[:id]
  where('title LIKE (?)', "%#{params[:search]}%") if parmas[:search]
  where(...) if ...
end

As far as you're right about last where I can suggest only method chaining here (simmilar as @socjopata did(:

# Model
def self.with_name(name)
  where(:name => name) if name.present?
end

def self.with_id_gt(id)
  where('id >= ?', id) if id.to_i > 3
end

# Controller
Post.with_name(parms[:name]).with_id_gt(params[:id])

Upvotes: 1

apneadiving
apneadiving

Reputation: 115521

def self.conditional_scope
  where(...) if ...
  where(...) if ...
  where(...) if ...
end

Then:

Model.conditional_scope

Upvotes: 1

socjopata
socjopata

Reputation: 5095

I think you should get yourself familiar with named_scopes: http://api.rubyonrails.org/classes/ActiveRecord/NamedScope/ClassMethods.html

They are composable, so you can write something like:

People.tall.having_children.older_than(30)

where "tall", "having_children" and "older_than" are named scopes.

Upvotes: 2

Related Questions