Search code examples
ruby-on-railsrubyshopping-cart

How to bind Act_as_shopping_cart with user


i am doing my first project and i need your help. I am using act_as_shopping_cart gem, and user model like in Rails Tutorial. How can i bind user and shopping cart? I tried bound shopping_cart.id and user.id, but without succes, still all users have same cart. It my code: Shopping cart controller:

class ShoppingCartsController < ApplicationController
  before_filter :extract_shopping_cart

  def create
    @product = Sketchbook.find(params[:product_id])
    @shopping_cart.add(@product, @product.price)
    redirect_to shop_path
  end

  def show

  end

  private

  def extract_shopping_cart
    shopping_cart_id = session[:shopping_cart_id]
    @shopping_cart = session[:shopping_cart_id] ? ShoppingCart.find(shopping_cart_id) : ShoppingCart.create
    session[:shopping_cart_id] = @shopping_cart.id
  end
end

User controller:

class UsersController < ApplicationController
  before_action :logged_in_user, only: [:show, :edit, :update]
  before_action :correct_user, only: [:edit, :update]


  def show
    @user = User.find(params[:id])
  end

  def new
    @user = User.new
  end

  def create
    @user = User.new(user_params)
    if @user.save
        redirect_to user_url(@user)
        flash[:notice] = "Użytkownik stworzony"
    else
        render 'new'
    end
  end

  def edit
  end

  def update
    if @user.update_attributes(user_params)
      redirect_to user_url(current_user)
      flash[:notice] = "Dane zaktualizowane"
    else
      render 'edit'
    end
  end

  def destroy
    User.find(params[:id]).destroy
    flash[:success] = "Konto usunięte"
    redirect_to root_url
  end



  private
    def user_params
        params.require(:user).permit(:username, :name, :surname, :email, :adress, :city, :zip_code, :country, :password, :password_confirmation)
    end

    #confirms a logged user
    def logged_in_user
      unless logged_in?
        store_location 
        flash[:danger] = "Zaloguj się"
        redirect_to login_url
      end
    end

    #confirms the correct user
    def correct_user
      @user = User.find(params[:id])
      redirect_to(root_url) unless current_user?(@user)
    end
end

Session helper:

module SessionsHelper

    #log in method
    def log_in(user)
        session[:user_id] = user.id
    end

    #remember a user in a presisnet session
    def remember(user)
        user.remember
        cookies.permanent.signed[:user_id] = user.id
        cookies.permanent[:remember_token] = user.remember_token
    end

    #returns true if the given user i current user
    def current_user?(user)
        user == current_user
    end

    #forgets a presistent session
    def forget(user)
        user.forget
        cookies.delete(:user_id)
        cookies.delete(:remember_token)
    end

    # returns logged user
    def current_user
        if (user_id = session[:user_id]) #open broweser
            @current_user ||= User.find_by(id: session[:user_id])
        elsif (user_id = cookies.signed[:user_id]) #cookie is present
            user= User.find_by(id: cookies.signed[:user_id])
            if user && user.authenticated?(cookies[:remember_token])
                log_in user
                @current_user = user
            end
        end
    end

    # Returns true if the user is logged in
    def logged_in?
        !current_user.nil?
    end


    # logs out current user
    def log_out
        forget(current_user)
        session.delete(:user_id)
        @current_user = nil?
    end


    #stores the url trying to be accessed
    def store_location
        session[:forwarding_url] = request.url if request.get?
    end

    #redirect back to stored location (or to the default)
    def redirect_back_or(default)
        redirect_to(session[:forwarding_url] || default)
        session.delete(:forwarding_url)
    end
end

I'd also happy if you could give me some other guidances how to improve this code.


Solution

  • Rails lets you do lots of "magic" where code in one place magically affects behavior elsewhere in the app, with no explicit connection between the two. This is really powerful but I think is also a huge problem for beginning developers; when you're learning Rails, it's super hard to keep track of how one thing is related to another so having those connections hidden up in the inheritance chains is almost cruel.

    So if you're in a place where the Rails magic is overwhelming (it certainly still is for me), my advice is to write stupid code that makes connections as local and simple as possible. Sometimes it's worth doing that even when it deviates from "the Rails way"; would you rather have "proper Rails" code, or code that's easy to understand and easy to maintain?

    Anyway, your specific problem might relate to the session. In ShoppingCartsController you're defining @shopping_cart by looking up a session[:shopping_cart_id]. If multiple users (after logging out and logging in as someone else) are ending up with the same @shopping_cart.id, that must mean that their session values are the same. In the beginning of the relevant controller action, add a puts statement to give you extra info (it spits out to the console) about what the session values are:

    puts session
    

    If multiple users have the same session values, it likely means that the session isn't getting cleared properly when you log out and log in as someone else. You could test that by setting a different session variable, and seeing if that also persists from one user to another.

    In general, adding lots of puts statements at the beginning of all relevant controller actions is a great (and super simple) way to get insight into what your application is thinking.

    Hope that helps!