I get a movie filter by assigning @movies a new value, but I would like to return a piece of code @movies = @movies.filter_ratings(params[:ratings]) to self and become beautiful: @movies.filter_ratings(params[:ratings])

Swarm google 2 day, please help!

My controller:

 class MoviesController < ApplicationController def index @movies = Movie.sort_by(params[:sort_by]) @movies = @movies.filter_ratings(params[:ratings]) unless (params[:ratings]).nil? end end 

My model:

 class Movie < ActiveRecord::Base # сортируем по требуемому полю scope :sort_by, ->(sorting) { all.order(sorting) } # выводим список рейтингов scope :all_ratings, -> { all.map{|movie| movie.rating }.uniq } # получаем фильмы согласно фильтру пользователя scope :filter_ratings, ->(filters) { where(:rating => filters.each_key.to_a) } end 

    2 answers 2

    In ActiveRecord there are no methods (public at least) that change the already created Relation .

    This is intended (see comments for this commit in Rails ). The query builder API is conceived to be immutable, so that it is more predictable, and does not cause unexpected behavior somewhere where it is not explicitly stated.

    But I don’t see a single reason why unless (params[:ratings]).nil? you cannot check at the very scoop, returning self if changes are not required:

     scope :filter_ratings, ->(filters) { if filters.nil? self else where(:rating => filters.each_key.to_a) end } 

    Then you can make the request described by using a longer chain:

     @movies = Movie.sort_by(params[:sort_by]).filter_ratings(params[:ratings]) 

    Shifts / indents to taste.

    • +1 you in karma! For a long time I would be looking for ... and +1 in karma for a hint that transfer the check to the controller. - AlkP
    • @AlkP in the sense of e ... in the model :) - D-side
    • @AlkP doesn't matter. I use the first one as more explicit. - D-side
    • brrr ... of course, the model! .... made a reservation, edited the question and as a result you answered that others did not see. :) the question arose because I read the best practices and everywhere is large and bold (sometimes with italics), that you need to write as little code as possible, but at the same time it is as bold and italic, that the code should be “easy” and readable and therefore not always clear where it ends 1 rule and starts 2 ... +1 more to you in karma, Thank you so much. - AlkP

    Select data through query-obzekt:

    app/controllers/movies_controller.rb :

     class MoviesController < ApplicationController def index @movies = MoviesQuery.new(movies_query_params, Movie.all) end private def movies_query_params params.permit(:ratings, :sort_by) end end 

    app/queries/movies_query.rb :

     class MoviesQuery < ApplicationQuery action :filter_by_ratings, :sort_by_attribute def filter_by_ratings return unless query_params[:ratings].present? where(rating: query_params[:ratings].each_key.to_a) end def sort_by_attribute return unless query_params[:sort_by].in?(%w(created_at title rating)) order(query_params[:sort_by]) end end 

    app/queries/application_query.rb - this file does not change, you can copy it from project to project, and inherit from it already project query objects:

     class ApplicationQuery < SimpleDelegator attr_accessor :query_params def initialize(query_params, relation) @query_params = query_params self.relation = relation apply_actions end class_attribute :actions class << self def action(*args, &block) if block_given? actions << block elsif args.first.is_a?(Array) actions.push(*args.first) else actions.push(*args) end end def remove_action(action) actions.delete(action) end def actions @actions ||= [] end end private attr_reader :relation def relation=(value) @relation = value __setobj__(@relation) end def apply_actions self.actions.each do |action| self.relation = evaluate_action(action) || relation end end def evaluate_action(action) if action.is_a?(Proc) instance_eval(&action) else send(action.to_sym) end end end 

    config/application.rb - for the rail to see the ruby ​​files in the queries folder - add it to the autoload_paths :

     module YourApp class Application < Rails::Application config.autoload_paths += %W( #{config.root}/app/queries ) end end 
    • @AlkP, why should the 1st line remain unchanged? All your construction in the controller will be like this: @movies = MoviesQuery.new(movies_query_params, Movie.all) - Vitaly Emelyantsev