Вспомогательный метод не запускается

#ruby-on-rails #if-statement #helper

#ruby-на-рельсах #if-оператор #помощник

Вопрос:

Я работаю над приложением, в котором вы можете добавлять игры в библиотеку, а также удалять их. У меня есть функция добавления, работающая с помощью нажатия кнопки, однако мой оператор if для вызова «удалить из библиотеки» не отображается.

Вот мой библиотечный метод в моем игровом контроллере, который управляет функцией добавления / удаления:

 def library
    type = params[:type]
    game = Game.new(game_params)
    game.fetch_data

    if type == "add"
      current_user.library_additions << game
      redirect_to user_library_path(current_user), notice: "Game was added to your library"

    elsif type == "remove"
      current_user.library_additions.delete(game)
      redirect_to root_path, notice: "Game was removed from your library"
    else
      # Type missing, nothing happens
      redirect_to game_path(game), notice: "Looks like nothing happened. Try once more!"
    end
 

В представлении кнопка «Добавить в библиотеку» должна появляться для игр, которых нет в вашей библиотеке, и если она есть в вашей библиотеке, она должна переключиться на «Удалить из библиотеки».

 <% if user_added_to_library?(current_user, game) %>

            <button type="button"><%= link_to 'Remove from library', add_game_path(game.id, type: "remove", game: game), method: :put %> </button>

          <% else %>
           <button type="button"> <%= link_to 'Add to library', add_game_path(game.id, type: "add", game: game), method: :put %> </button>
          <% end %>
 

Действие, определяемое user_added_to_library? не работает, поэтому я всегда вижу кнопку Добавить в библиотеку.

Вот мой помощник для user_added_to_library?

 module GamesHelper
  def user_added_to_library? user, game
    user.libraries.where(user: user, game: @game).any?
  end
end

 

Я подумал, может быть, мне нужно сменить библиотеки на library_additions, но я получаю сообщение об ошибке StatementInvalid. То, как код написан сейчас, не приводит к появлению ошибок, но с тем же успехом его могло бы вообще не быть.

Моя пользовательская модель, если это необходимо:

 class User < ApplicationRecord

  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  has_many :games
  has_many :libraries
  has_many :library_additions, through: :libraries, source: :game
end
 

Нужно ли мне изменять мой user_added_to_library? метод или есть другая проблема?

Ответ №1:

Слон в комнате здесь на самом деле представляет собой набор всеобъемлющих проблем с дизайном этого кода. Этот дизайн далек от RESTful и нарушает семантику HTTP-глаголов (PUT не должен удалять ресурс), а наличие единого метода, который выполняет много разных заданий (создание и уничтожение ресурса), действительно вонючее. Вы также даже не проверяете, действительно ли игра сохранена.

Уничтожение ресурса должно быть выполнено с помощью запроса на УДАЛЕНИЕ. В Rails вы можете создавать и изменять ресурсы, просто правильно используя HTTP-глаголы:

 POST    /games      # create a game
PATCH   /games/:id  # update a game
DELETE  /games/:id  # destroy a game
 

Большинство случаев могут и должны обрабатываться стандартными маршрутами CRUD, генерируемыми resources макрокомандой. Если у вас есть ресурсы с взаимосвязями, вы описываете эти взаимосвязи с помощью вложенных маршрутов. В этом случае вы можете вложить маршрут в отдельный ресурс, поскольку вы добавляете / удаляете игры от текущего пользователя.

 # generates 
# POST     /user/games
# DELELE   /user/games/:id
resource :user, only: [] do
  resources :games, only: [:create, :destroy]
end 
 

Это будет обработано методами #create and #destroy в вашем GamesController .

Вторая проблема на самом деле заключается в дизайне и моделях вашей базы данных. Если вы хотите создать дизайн, в котором у пользователей есть игры, которые можно организовать в разные библиотеки, вы бы сделали это с помощью:

 class User < ApplicationRecord
  has_many :libraries
  has_many :games, through: :libraries
end

class Library < ApplicationRecord
  belongs_to :user
  has_many :library_games
  has_many :games, through: :library_games
end 

class LibraryGame < ApplicationRecord
  belongs_to :library
  belongs_to :game
  has_one :user, through: :library 
end

class Game < ApplicationRecord
  has_many :library_games
  has_many :libraries, through: :library_games
  has_many :users, through: :libraries
end
 

Настройка косвенных ассоциаций вверх по дереву позволяет проверить, есть ли у пользователя игра, с помощью:

 class User < ApplicationRecord
  has_many :libraries
  has_many :games, through: :libraries

  def has_game?(game)
    games.where(id: game.id).exist?
  end 
end
 

На самом деле нет причин, по которым это вообще должно включать вспомогательный метод. В конце концов, вы действительно просто задаете вопрос объекту user. Это не должно включать передачу двух разных объектов в отдельный метод.

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

1. В качестве небольшого совета я бы сказал, что вы действительно добавляете здесь гораздо больше сложности, чем могут выдержать ваши навыки. Я бы настроил простое решение users -> user_games -> game и разобрался с основами и железнодорожным способом создания приложений, вместо того, чтобы уходить вглубь.

2. Я думал, что, поскольку данные поступают из драгоценного камня API, а пользователи ничего не создают, не обновляют и не удаляют, мне нужно было создать конкретную функцию добавления / удаления для библиотеки. Но наличие конкретной модели library_game имеет больше смысла. Я отредактирую свои модели и постараюсь сделать решение более элегантным.

3. Тот факт, что данные поступают из API, на самом деле не меняет основных пользователей дизайна. Вы все еще обрабатываете данные, а в Rails это означает REST.