2

I'd like to use something like

scope :published, -> { where(:published => true) }

on my Article model only if the user is not signed in. If the user is signed in then I'd like to show all articles. I realize that the Article model itself does not have access to Devise's user_signed_in? method so this logic should probably live in the controller.

The problem is that in the controller it would cause a lot of redundancy, as such:

def index
  if params[:search].present?
    @search = params[:search]
    if user_signed_in?
      @articles = Article.where('title LIKE ? OR body LIKE ?', "%#{@search}%", "%#{@search}%")
    else
      @articles = Article.published.where('title LIKE ? OR body LIKE ?', "%#{@search}%", "%#{@search}%")
    end
  elsif params[:uid].present?
    @user = User.find(params[:uid])
    if user_signed_in?
      @articles = @user.articles.order :created_at
    else
      @articles = @user.articles.published.order :created_at
    end
  else
    if user_signed_in?
      @articles = Article.all.desc
    else
      @articles = Article.published.desc
    end
  end
end

Is there a better way I can avoid redundancy here but always check if the user is signed in before using the published scope?

Thanks

DaniG2k
  • 4,772
  • 36
  • 77

2 Answers2

1

You should look into using pundit, you can scope access exactly in this way for access control.

You can pass current_user to you scope.

scope :search, -> (current_user) { where(published: [(current_user.present? ? false : true), true]) }

https://stackoverflow.com/a/16588246/1162683

Community
  • 1
  • 1
penner
  • 2,707
  • 1
  • 37
  • 48
0

Firstly, extract the second where clause into a scope of its own. This will make things easier to re-use.

Secondly, ActiveRecord queries are composable and lazy. This means that you can save off an intermediate query into a variable, then attach the scope.

class Article
  scope :search, -> {|search| where('title LIKE ? OR body LIKE ?', "%#{@search}%", "%#{@search}%") }
....
end

class ArticlesController
  def index
    articles = Article.search(params[:search])
    articles.published if user_signed_in?
  end
end

Something like that

Srdjan Pejic
  • 8,152
  • 2
  • 28
  • 24
  • Sorry I wasn't very clear. My question is: how can I do something like this `scope :published, -> { user_signed_in? ? all : where(:published => true) }` This isn't possible within the model so I was wondering if there's a better way to do that in the controller – DaniG2k Sep 22 '15 at 14:46
  • it is not a best practice to call user_signed_in? method in the model, because it is a controller business so I think the refactored code suggested by @Srdjan is pretty enough – Saiqul Haq Sep 22 '15 at 14:54
  • The thing is that action is already doing too much, even without having the `user_signed_in?` problem. Why not split out into 3 separate actions, `index` which runs the code in the `else` clause, `search`, which does the search code and then whatever `uid` does. – Srdjan Pejic Sep 22 '15 at 14:55