Как запросить модель на основе имени контроллера

#ruby-on-rails #ruby

Вопрос:

Я провел рефакторинг своих моделей и контроллеров, пытаясь устранить дублирование кода, и до сих пор все кажется персиково-сливочным. В настоящее время у меня есть немного кода, который является общим для двух моих контроллеров, например:

 def process_filters
# Filter hash we're going to pass to the model
  filter_to_use = {}

  # To process filters, we first query the model to find out what filters
  # we should be looking for, as the model knows what we can filter.
  Iso.available_filters.each do |filter|
    # We should have our array with our filter listing.
    # Check the purchase order model for a description
    filter_name = filter[0][:filter_name]

    # Filters are stored in a session variable, this way filters survive
    # page reloads, etc.  First thing we do, is set the session if new filters
    # have been set for the filter.
    session_name = session_filter_name( filter_name )
    if params[session_name]
      if params[session_name] == 'All'
        session[session_name] = nil
      else
        session[session_name] = params[session_name]
        filter_to_use[filter_name] = params[session_name]
      end
    elsif session[session_name]
      # If params aren't read, we still need to filter based off the users
      # session
      filter_to_use[filter_name] = session[session_name]
    end
  end

  # Just using this variable for now until I can refactor the helper code
  # so that this is passed in.
  @current_filter_values = filter_to_use

  filter_to_use[:page] = @current_page

  @isos = Iso.find_filtered( filter_to_use )

  if @isos.out_of_bounds?
    filter_to_use[:page] = session[:previous_page] = @current_page = 1
    @isos = Iso.find_filtered( filter_to_use )
  end
end
 

Теперь этот код точно такой же, как код в другом контроллере, за исключением ссылки на модель (в данном случае Iso). Могу ли я каким-то образом сделать эту эталонную модель динамичной?

В принципе, я хотел бы заменить ссылки Iso (включая переменную @iso) на что-то, основанное на controller.controller_name или аналогичное.

Ответ №1:

Вы можете переместить этот код в модуль, смешать этот модуль со всеми необходимыми контроллерами и использовать self.class переменная внутри модуля для определения конкретного имени контроллера. С этим именем вы можете использовать стандартные строковые функции (например, прописные буквы) и функцию Kernel.const_get, чтобы получать классы по их именам.

Комментарии:

1. Аааа, я думаю, что Kernel.const_get-это то, о чем мне нужно было знать. Спасибо!

Ответ №2:

Ладно, вот к чему я пришел в итоге. Ни в коем случае я не гуру Ruby/Rails, но это работает для меня, так что, надеюсь, это может указать кому-то правильное направление, если они пытаются что-то подобное:

 module Filtered
  module Controller
    module InstanceMethods
      def process_filters
        # Filter hash we're going to pass to the model
        filter_to_use = {}

        # To process filters, we first query the model to find out what filters
        # we should be looking for, as the model knows what we can filter.
        Kernel.const_get( 
          self.controller_name.singularize.camelize ).available_filters.each do |filter|
          # We should have our array with our filter listing.
          # Check the purchase order model for a description
          filter_name = filter[0][:filter_name]

          # Filters are stored in a session variable, this way filters survive
          # page reloads, etc.  First thing we do, is set the session if new filters
          # have been set for the filter.
          session_name = session_filter_name( filter_name )
          if params[session_name]
            if params[session_name] == 'All'
              session[session_name] = nil
            else
              session[session_name] = params[session_name]
              filter_to_use[filter_name] = params[session_name]
            end
          elsif session[session_name]
            # If params aren't read, we still need to filter based off the users
            # session
            filter_to_use[filter_name] = session[session_name]
          end
        end

        # Just using this variable for now until I can refactor the helper code
        # so that this is passed in.
        self.instance_variable_set( '@current_filter_values', filter_to_use )

        filter_to_use[:page] = self.instance_variable_get( '@current_page' )

        self.instance_variable_set( "@#{self.controller_name}",
          Kernel.const_get( self.controller_name.singularize.camelize ).find_filtered(
            filter_to_use ) )

        if self.instance_variable_get( "@#{self.controller_name}" ).out_of_bounds?
          filter_to_use[:page] = session[:previous_page] = 1
          self.instance_variable_set( "@#{self.controller_name}",
            Kernel.const_get( self.controller_name.singularize.camelize ).find_filtered(
              filter_to_use ) )
        end
      end

      private

      # Quick helper function for generating session variable names
      def session_filter_name( name )
        "#{self.controller_name}_#{name}_filter".to_sym
      end

    end
  end
end
 

Я включаю его во все свои контроллеры через плагин, файл init.rb, вот так:

 # Include hook for controllers
ActionController::Base.class_eval do
  include Filtered::Controller::InstanceMethods
end