Есть ли метод DRYer в этом контроллере?

#ruby-on-rails

#ruby-on-rails

Вопрос:

Используя ROR 2.3.8.

Вот мой код:

 class CitiesController < ApplicationController  
  def show
    ...
  end

  def western
    @city = City.find(params[:id])

    @spots = Spot.paginate(
      :conditions => ["(city=? or state=?) and country=? and shop_type=?", "#{@city.name}", "#{@city.name}", @city.country, "Places"], 
      :page => params[:page], 
      :per_page => 20, 
      :order => 'rating_average DESC'
      )
  end

  def middle-east
    @city = City.find(params[:id])

    @spots = Spot.paginate(
      :conditions => ["(city=? or state=?) and country=? and shop_type=?", "#{@city.name}", "#{@city.name}", @city.country, "Food"], 
      :page => params[:page], 
      :per_page => 20, 
      :order => 'rating_average DESC'
      )
  end

  def asian
    @city = City.find(params[:id])

    @spots = Spot.paginate(
      :conditions => ["(city=? or state=?) and country=? and shop_type=?", "#{@city.name}", "#{@city.name}", @city.country, "Accommodation"], 
      :page => params[:page], 
      :per_page => 20, 
      :order => 'rating_average DESC'
      )
  end

end
  

Я создал western.html.erb , middle-east.html.erb asian.html.erb и _shops.html.erb .

Итак, первые три в основном пустые, но выдают _shops.html.erb , чтобы мне не приходилось перекодировать макет представления.

Есть ли лучший метод при написании контроллера?

Спасибо!

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

1. codereview.stackexchange.com

Ответ №1:

Правильный способ высушить это в Rails 2.3.X — это именованная область в вашей модели. Чрезмерные / повторяющиеся запросы в вашем контроллере — это намек на запах кода. Если вы мне не верите, то Джеймис Бак прикроет мою спину! http://weblog.jamisbuck.org/2006/10/18/skinny-controller-fat-model

В вашей точечной модели:

 # app/models/spot.rb
named_scope :by_type, lambda { |city, type|
  {:conditions => ["(city=? or state=?) and country=? and shop_type=?", city.name, city.name, city.country, type] }
}
  

В вашем контроллере Cities:

 #app/contollers/cities_controller.rb
before_filter :fetch_city, :except => :show

def western
  @spots = paginate_spots("Places")
end

....

private

def fetch_city
  @city = City.find(params[:id])
end

def paginate_spots(type)
  Spot.by_type(@city,type).paginate(:page => params[:page], 
    :per_page => 20, 
    :order => 'rating_average DESC'
  )
end
  

Чего это достигает, так это удаления большей части логики запросов из контроллера. Это хорошая вещь, поскольку позволяет находить точки по городу и вводить другие контроллеры, если возникнет необходимость. Разбивка на страницы, вероятно, зависит от вашего отдельного контроллера, поэтому я склонен исключать его из областей внутри моделей. Если вы хотите создать API, вы можете ограничить его 50 вместо 20, например, и хотите выполнить сортировку другим методом.

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

1. Спасибо. Я получил эту ошибку Mysql::Error: Operand should contain 1 column(s): SELECT * FROM spots` ГДЕ (spot_type=1,’Accommodation’) ПОРЯДОК ПО rating_average ОГРАНИЧИВАЕТ ЗНАЧЕНИЕ 0, 20`, которое, я думаю, spot_type должно быть равно "Accommodation" . Как мне это исправить? Также {} необходим from :conditions ... , type] .

2. Мой плохой, у меня есть два :by_type с именем named_scope. Переименовали его во что-то другое, и это работает безупречно. Спасибо!

3. Я исправил проблему synax, на которую вы указали в своем комментарии. Мне действительно сложно отлаживать инструкцию, не имея полной информации о моделях. Единственное, что вы можете попробовать, это разделить области на более мелкие и увидеть желаемые результаты. Вы можете объединить области вместе, такие как Spot.by_state_or_city (@city.name ).by_country(@city.country).by_type(«Размещение») во время отладки.

4. @Patrick Robertson, как указано выше, это моя вина, что я не знал, что уже существует другой :by_type named_scope. Теперь это работает. Спасибо!

5. @Patrick Robertson, что, если я хочу показать все типы? Что я должен вставить для замены Places в paginate_spots("Places") , чтобы отобразить все типы?

Ответ №2:

Первый подход: используйте before_filter . upd: у вас это не сработает (я оставлю это здесь в образовательных целях)

 class CitiesController < ApplicationController  
  before_filter :spots_and_city, :only => [:asian, :western, :middle-east]
  def show
    ...
  end

  def western
  end

  def middle-east
  end

  def asian
  end

  private

  def spots_and_city(type)
    @city = City.find(params[:id])
    @spots = Spot.paginate(
      :conditions => ["(city=? or state=?) and country=? and shop_type=?", "#{@city.name}", "#{@city.name}", @city.country, "Accommodation"], 
      :page => params[:page], 
      :per_page => 20, 
      :order => 'rating_average DESC'
      )
  end
end
  

Использование второго подхода helper method : обновлено

 class CitiesController < ApplicationController  
  helper_method :spots, :city
  def show
    ...
  end

  def western
    @city = city
    @spots = spots("Places")
  end

  def middle-east
    @city = city
    @spots = spots("Food")
  end

  def asian
    @city = city
    @spots = spots("Accommodation")
  end

  private

  def city
    city ||= City.find(params[:id])
  end

  def spots(type)
    spots ||= Spot.paginate(
      :conditions => ["(city=? or state=?) and country=? and shop_type=?", "#{@city.name}", "#{@city.name}", @city.country, type], 
      :page => params[:page], 
      :per_page => 20, 
      :order => 'rating_average DESC'
      )
  end
end
  

Третье: использовать Decent Exposure

http://railscasts.com/episodes/259-decent-exposure

На мой взгляд, я предпочитаю использовать helper_method для такой работы.

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

1. Спасибо. Но в моем последнем значении для строки условия есть определенное shop_type для каждого. Вы не возражаете просмотреть свой код?

2. Кстати, что || делает в city ||= City.find(params[:id]) ?

3. Это кэширование. оформить заказ railscasts.com/episodes/1-caching-with-instance-variables

4. Спасибо. Последний вопрос: почему ты такой замечательный?

5. Пожалуйста, еще немного информации. Это не похоже на ошибку из-за helper_method