Home > Software engineering >  Rails: Using scopes to show different subsets of a model and how to build this the less DRY-ist way?
Rails: Using scopes to show different subsets of a model and how to build this the less DRY-ist way?

Time:12-24

I have a model movies in my Ruby on Rails application and I want to have a few easy links that show different subsets of it - for example, movies not seen, movies that do not have a production year entered, movies rated as "great" etc.

I have created scopes for all of these conditions in my movie model, e. g.

scope :no_year, -> { where(release_year: [0, nil, ""]) }

But then I want to have a list of these subsets - as said, a list of links where user can click and will get the results in a default view (the movie index view actually). So I have a list of links such as the below, which required me to add routes as well as methods for all of these scopes that look pretty much the same.

<%= link_to 'Movies without a Year', noyear_movies_path %>
(<%= Movie.no_year.count %>)

routes.rb:

resources :movies do
  get :noyear, on: :collection
end

movies_controller.rb:

def noyear
  @q = Movie.no_year.ransack(params[:q]) # using Ransack for a sidebar that is displayed
  @pagy, @movies = pagy(@q.result(distinct: true)) # using pagy to split up results
  render 'index'
end

EDIT: added index and sidebar method code.

My index method looks like this:

def index
  @pagy, @movies = pagy(@q.result(distinct: true))
end

... and the variable q itself is set application wide, as it is defined in a sidebar that contains a little search field that is always displayed:

def sidebar_q_movie
  @q = Movie.ransack(params[:q])
end

I am sure this can be achieved way nicer. But since I am still pretty new to Ruby, I don't know that. Any hints appreciated!

CodePudding user response:

Rather than having separate routes for each movie filter, you could specify the filter with a query param to Movies#index

<%= link_to 'Movies without a Year', movies_path(filter: 'no_year') %>

and in the controller:

def index
  @movies = Movies.send(params[:filter])
end

Of course, you would want to validate the filter param first by checking against a list of permitted strings.

  • Related