Search code examples
ruby-on-railsif-statementhelper

Helper method not triggering


I'm working on an app where you can add games to a library and also remove them. I have the adding function working via clicking a button, however my if statement to bring up "remove from library" is not showing.

Here is my library method in my games controller which controls the add/remove feature:

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

In the view an "Add to Library" button is supposed to appear on games that aren't in your library, and if it is in your library it should switch to "Remove from Library"

<% 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 %>

The action determined by user_added_to_library? isn't working so I always see the Add to Library button.

Here is my helper for user_added_to_library?

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

I thought maybe I needed to change libraries to library_additions but I get StatementInvalid error. The way the code is written now is not throwing up errors but it might as well not be there at all.

My User model if that's necessary:

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

Do I need to alter my user_added_to_library? method or is there another issue?


Solution

  • The elephant in the room here is really a set of overarching design issues with this code. This design is far from RESTful and violates the semantics of the HTTP verbs (PUT should NOT delete a resource) and having a single method that does many different jobs (creating and destroying a resource) is really smelly. You're also not even checking if the game is actually saved.

    Destroying a resource should be done with a DELETE request. In Rails you can create and modify resources by simply using the HTTP verbs correctly:

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

    Most cases can and should be handled by the standard CRUD routes generated by the resources macro. If you have resources with relationships you describe those relationships with nested routes. In this case you might choose to nest the route in a singular resource since you are adding/removing games from the current user.

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

    This would be handled by the #create and #destroy methods in your GamesController.

    The second issue is really your database design and models. If you want to create a design where users have games that can be organized into different libraries you would do it by:

    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
    

    Setting up indirect associations up the tree makes it possible to check if a user has a game by:

    class User < ApplicationRecord
      has_many :libraries
      has_many :games, through: :libraries
    
      def has_game?(game)
        games.where(id: game.id).exist?
      end 
    end
    

    There really is no reason why this should involve a helper method at all. After all you're really just asking the user object a question. That should not involve passing two different objects to a separate method.