Ruby 关于 Rails 中辅助方法在视图中使用的一个小问题

2604249649 · 2023年05月04日 · 最后由 2604249649 回复于 2023年05月05日 · 336 次阅读

我在《Ruby on Rails 教程(第四版)》8.2.2 节中看到书上做了如下操作,但是我模仿着做了,但是没有效果

  • 书籍首先在 ApplicationController 中引入 Sessions 辅助模块
class ApplicationController < ActionController::Base
  protect_from_forgery with: :exception
  include SessionsHelper
end
  • 然后在 SessionsHelper 中首先创建如下辅助方法
module SessionsHelper
  # 登录指定用户
  def log_in(user)
    # 利用Rails提供的session方法在浏览器中创建一个临时cookie,内容是加密后的用户ID
    # 后续请求中,可以使用session[:user_id]取回这个ID
    session[:user_id] = user.id
  end
end
  • 接着在 SessionsController 中进行用户登录处理
class SessionsController < ApplicationController
  def new
  end

  def create
    user = User.find_by(email: params[:session][:email].downcase)

    respond_to do |format|
      if user && user.authenticate(params[:session][:password])
        # 保存用户登录信息
        log_in user
        # 用户登录成功,重定向到用户资料页面
        format.html { redirect_to user }
      else
        # flash闪现消息在一个请求的生命周期内是持续存在的,但是重新渲染页面不算一次新的请求
        # 因为使用flash.now代替(flash.now专门用于在重新渲染的页面中显示闪现消息)
        flash.now[:danger] = "Log in failed, please check your email and password!"
        format.html { render :new, status: :bad_request }
      end
    end
  end

  def destroy
  end
end
  • 接着又继续创建了两个辅助方法,一个用于获取当前登录用户,另一个用于获取用户登录状态
module SessionsHelper
  # 登录指定用户
  def log_in(user)
    # 利用Rails提供的session方法在浏览器中创建一个临时cookie,内容是加密后的用户ID
    # 后续请求中,可以使用session[:user_id]取回这个ID
    session[:user_id] = user.id
  end

  # 返回当前登录的用户(如果有的话)
  def current_user
    @current_user ||= User.find_by(id: session[:user_id])
  end

  # 判断用户是否已经登录,已登录返回true,否则返回false
  def logged_in?
    !@current_user.nil?
  end
end
  • 最后就是我不太理解的地方了:它在视图进行了如下修改,希望达到已登录用户和未登录用户看到的导航栏连接不同的效果
<% if logged_in? %>
    <li class="nav-item">
      <%= link_to "Users", '#', class: "nav-link" %>
    </li>
    <li class="nav-item dropdown">
      <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
        Account
      </a>
      <ul class="dropdown-menu">
        <li>
          <%= link_to "Profile", user_path, class: "dropdown-item" %>
        </li>
        <li>
          <%= link_to "Settings", '#', class: "dropdown-item" %>
        </li>
        <li><hr class="dropdown-divider"></li>
        <li>
          <%= link_to "Log out", logout_path, method: "delete", class: "dropdown-item" %>
        </li>
      </ul>
    </li>
  <% else %>
    <li class="nav-item">
      <%= link_to "Log in", login_path, class: "nav-link" %>
    </li>
  <% end %>

我试了书上的操作,发现登录后用户看到的导航链接根本没有变(辅助方法current_userlogged_in?貌似没有起到作用),通过debugger断点发现辅助方法中创建的@current_user对象在转到用户资料页面后就是nil了,所以用户登录后看到的链接不会变。

我改成了下面这样,效果是有了,但是我想知道书上之前那样写的操作为什么达不到效果

<% if !session[:user_id].nil? %>
     <li class="nav-item">
       <%= link_to "Users", '#', class: "nav-link" %>
     </li>
     <li class="nav-item dropdown">
       <a class="nav-link dropdown-toggle" href="#" role="button" data-bs-toggle="dropdown" aria-expanded="false">
         Account
       </a>
       <ul class="dropdown-menu">
         <li>
           <%= link_to "Profile", user_path, class: "dropdown-item" %>
         </li>
         <li>
           <%= link_to "Settings", '#', class: "dropdown-item" %>
         </li>
         <li><hr class="dropdown-divider"></li>
         <li>
           <%= link_to "Log out", logout_path, method: "delete", class: "dropdown-item" %>
         </li>
       </ul>
     </li>
   <% else %>
     <li class="nav-item">
       <%= link_to "Log in", login_path, class: "nav-link" %>
     </li>
   <% end %>


UsersController 控制器的 show 动作会处理用户资料页面的显示

class UsersController < ApplicationController
   ...

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

  ...
end

def logged_in?
  !@current_user.nil?
end

这个方法访问 @current_user 的时候实例变量还没有值,改成这样:

def logged_in?
  !current_user.nil?
end

调用 current_user 方法,让实例变量设值。

楼上正解

谢谢大佬们,主要是我看这书写的有点迷惑😂

2604249649 关闭了讨论。 05月06日 17:05
需要 登录 后方可回复, 如果你还没有账号请 注册新账号