1
@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?

5 Answers 5

2

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.

Sign up to request clarification or add additional context in comments.

Comments

1

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)

Comments

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

Then:

Model.conditional_scope

Comments

1

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])

1 Comment

this solution return only last where. I need chaining. Am i right?
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.