1

In one of my Rails models I have this:

class Project < ActiveRecord::Base

  belongs_to :user

  default_scope order("number ASC")

end

Now the problem is that I want each user to be able to set his or her default_scope individually. For example, a user A might want default_scope order("date ASC"), another one might want default_scope order("number DESC").

In my User table I even have columns to store these values: order_column and order_direction.

But how can I make the default_scope in the model dynamic?

Thanks for any help.

Tintin81
  • 9,821
  • 20
  • 85
  • 178

4 Answers4

2

You won't want to use default_scope. What you do what is regular scope.

class Post < ActiveRecord::Base
  scope :created_before, ->(time) { where("created_at < ?", time) }
end

Scope | Ruby on Rails

Dan Grahn
  • 9,044
  • 4
  • 37
  • 74
2

As @screenmutt said, default scopes are not meant to be data-driven, they are meant to be model driven. Since this scope is going to change according to each user's data I'd use a regular scope for this.

@fmendez answer is pretty good but it uses default scope which I just explained why it is not recommended using this method.

This is what I'd do in your case:

class Post < ActiveRecord::Base
  scope :user_order, lambda { order("#{current_user.order_column} #{current_user.order_direction}")}
end

Also a very important thing to notice here is SQL injection: Since you are embedding current_user.order_column and current_user.order_direction inside your query, you MUST ensure that the user can only feed these columns into the database with valid data. Otherwise, users will be able to craft unwanted SQL queries.

Erez Rabih
  • 15,562
  • 3
  • 47
  • 64
  • +1 for sql injection warning - see http://stackoverflow.com/questions/7771103/rails-3-activerecord-order-what-is-the-proper-sql-injection-work-around for more details – house9 Dec 04 '13 at 16:34
1

You could do something like this:

def self.default_scope
   order("#{current_user.order_column} #{current_user.order_direction}")
end

This should dynamically pick the values stored in the current_user's order_column and order_direction columns.

fmendez
  • 7,250
  • 5
  • 36
  • 35
1

You can define a class method with whatever logic you require and set your default scope to that. A class method is identical to a named scope when it returns a relation,eg by returning the result of a method like order.

For example:

def self.user_ordering
    # user ording logic here
end

default_scope :user_ordering

You may want to add a current_user and current_user= class methods to your User model which maintains the request user in a thread local variable. You would typically set the current user on your User model from your application controller. This makes current_user available to all your models for logic such as your sorting order and does it in a thread safe manner.

Andrew Hacking
  • 6,296
  • 31
  • 37